Merge branch 'master' into user
Conflicts: admin.go template.go trantor.go
This commit is contained in:
commit
8e3b929dfe
22 changed files with 1015 additions and 385 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -7,5 +7,6 @@ tools/update/update
|
|||
tools/togridfs/togridfs
|
||||
tools/getISBNnDesc/getISBNnDesc
|
||||
tools/coverNew/coverNew
|
||||
tools/addsize/addsize
|
||||
tags
|
||||
.*.swp
|
||||
|
|
122
admin.go
122
admin.go
|
@ -9,29 +9,29 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
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
|
||||
|
@ -39,33 +39,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 {
|
||||
|
@ -78,21 +78,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,
|
||||
|
@ -101,18 +101,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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,28 +130,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)
|
||||
|
@ -160,8 +160,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 {
|
||||
|
@ -170,40 +170,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)
|
||||
}
|
||||
|
|
48
cover.go
48
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")
|
||||
}
|
||||
|
|
87
database.go
87
database.go
|
@ -7,8 +7,6 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
var db *DB
|
||||
|
||||
type Book struct {
|
||||
Id string `bson:"_id"`
|
||||
Title string
|
||||
|
@ -28,6 +26,7 @@ type Book struct {
|
|||
Rights string
|
||||
Meta string
|
||||
File bson.ObjectId
|
||||
FileSize int
|
||||
Cover bson.ObjectId
|
||||
CoverSmall bson.ObjectId
|
||||
Active bool
|
||||
|
@ -41,11 +40,6 @@ type News struct {
|
|||
|
||||
type DB struct {
|
||||
session *mgo.Session
|
||||
books *mgo.Collection
|
||||
user *mgo.Collection
|
||||
news *mgo.Collection
|
||||
stats *mgo.Collection
|
||||
mr *MR
|
||||
}
|
||||
|
||||
func initDB() *DB {
|
||||
|
@ -55,13 +49,6 @@ func initDB() *DB {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
database := d.session.DB(DB_NAME)
|
||||
d.books = database.C(BOOKS_COLL)
|
||||
d.user = database.C(USERS_COLL)
|
||||
d.news = database.C(NEWS_COLL)
|
||||
d.stats = database.C(STATS_COLL)
|
||||
d.mr = NewMR(database)
|
||||
return d
|
||||
}
|
||||
|
||||
|
@ -69,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))
|
||||
|
@ -77,12 +70,14 @@ func md5Pass(pass string) []byte {
|
|||
|
||||
func (d *DB) SetPassword(user string, pass string) error {
|
||||
hash := md5Pass(pass)
|
||||
return d.user.Update(bson.M{"user": user}, bson.M{"$set": bson.M{"pass": hash}})
|
||||
userColl := d.session.DB(DB_NAME).C(USERS_COLL)
|
||||
return userColl.Update(bson.M{"user": user}, bson.M{"$set": bson.M{"pass": hash}})
|
||||
}
|
||||
|
||||
func (d *DB) UserValid(user string, pass string) bool {
|
||||
hash := md5Pass(pass)
|
||||
n, err := d.user.Find(bson.M{"user": user, "pass": hash}).Count()
|
||||
userColl := d.session.DB(DB_NAME).C(USERS_COLL)
|
||||
n, err := userColl.Find(bson.M{"user": user, "pass": hash}).Count()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
@ -91,7 +86,8 @@ func (d *DB) UserValid(user string, pass string) bool {
|
|||
|
||||
func (d *DB) AddUser(user string, pass string) error {
|
||||
hash := md5Pass(pass)
|
||||
return d.user.Insert(bson.M{"user": user, "pass": hash, "role": ""})
|
||||
userColl := d.session.DB(DB_NAME).C(USERS_COLL)
|
||||
return userColl.Insert(bson.M{"user": user, "pass": hash, "role": ""})
|
||||
}
|
||||
|
||||
func (d *DB) UserRole(user string) string {
|
||||
|
@ -99,7 +95,8 @@ func (d *DB) UserRole(user string) string {
|
|||
Role string
|
||||
}
|
||||
res := result{}
|
||||
err := d.user.Find(bson.M{"user": user}).One(&res)
|
||||
userColl := d.session.DB(DB_NAME).C(USERS_COLL)
|
||||
err := userColl.Find(bson.M{"user": user}).One(&res)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
@ -110,7 +107,8 @@ func (d *DB) AddNews(text string) error {
|
|||
var news News
|
||||
news.Text = text
|
||||
news.Date = time.Now()
|
||||
return d.news.Insert(news)
|
||||
newsColl := d.session.DB(DB_NAME).C(NEWS_COLL)
|
||||
return newsColl.Insert(news)
|
||||
}
|
||||
|
||||
func (d *DB) GetNews(num int, days int) (news []News, err error) {
|
||||
|
@ -120,25 +118,30 @@ func (d *DB) GetNews(num int, days int) (news []News, err error) {
|
|||
date := time.Now().Add(duration)
|
||||
query = bson.M{"date": bson.M{"$gt": date}}
|
||||
}
|
||||
q := d.news.Find(query).Sort("-date").Limit(num)
|
||||
newsColl := d.session.DB(DB_NAME).C(NEWS_COLL)
|
||||
q := newsColl.Find(query).Sort("-date").Limit(num)
|
||||
err = q.All(&news)
|
||||
return
|
||||
}
|
||||
|
||||
func (d *DB) InsertStats(stats interface{}) error {
|
||||
return d.stats.Insert(stats)
|
||||
statsColl := d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
return statsColl.Insert(stats)
|
||||
}
|
||||
|
||||
func (d *DB) InsertBook(book interface{}) error {
|
||||
return d.books.Insert(book)
|
||||
booksColl := d.session.DB(DB_NAME).C(BOOKS_COLL)
|
||||
return booksColl.Insert(book)
|
||||
}
|
||||
|
||||
func (d *DB) RemoveBook(id bson.ObjectId) error {
|
||||
return d.books.Remove(bson.M{"_id": id})
|
||||
booksColl := d.session.DB(DB_NAME).C(BOOKS_COLL)
|
||||
return booksColl.Remove(bson.M{"_id": id})
|
||||
}
|
||||
|
||||
func (d *DB) UpdateBook(id bson.ObjectId, data interface{}) error {
|
||||
return d.books.Update(bson.M{"_id": id}, bson.M{"$set": data})
|
||||
booksColl := d.session.DB(DB_NAME).C(BOOKS_COLL)
|
||||
return booksColl.Update(bson.M{"_id": id}, bson.M{"$set": data})
|
||||
}
|
||||
|
||||
/* optional parameters: length and start index
|
||||
|
@ -153,7 +156,8 @@ func (d *DB) GetBooks(query bson.M, r ...int) (books []Book, num int, err error)
|
|||
start = r[1]
|
||||
}
|
||||
}
|
||||
q := d.books.Find(query).Sort("-_id")
|
||||
booksColl := d.session.DB(DB_NAME).C(BOOKS_COLL)
|
||||
q := booksColl.Find(query).Sort("-_id")
|
||||
num, err = q.Count()
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -175,14 +179,17 @@ func (d *DB) GetBooks(query bson.M, r ...int) (books []Book, num int, err error)
|
|||
/* Get the most visited books
|
||||
*/
|
||||
func (d *DB) GetVisitedBooks(num int) (books []Book, err error) {
|
||||
bookId, err := d.mr.GetMostVisited(num, d.stats)
|
||||
statsColl := d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
mr := NewMR(d.session.DB(DB_NAME))
|
||||
bookId, err := mr.GetMostVisited(num, statsColl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
books = make([]Book, num)
|
||||
for i, id := range bookId {
|
||||
d.books.Find(bson.M{"_id": id}).One(&books[i])
|
||||
booksColl := d.session.DB(DB_NAME).C(BOOKS_COLL)
|
||||
booksColl.Find(bson.M{"_id": id}).One(&books[i])
|
||||
books[i].Id = bson.ObjectId(books[i].Id).Hex()
|
||||
}
|
||||
return
|
||||
|
@ -191,14 +198,17 @@ func (d *DB) GetVisitedBooks(num int) (books []Book, err error) {
|
|||
/* Get the most downloaded books
|
||||
*/
|
||||
func (d *DB) GetDownloadedBooks(num int) (books []Book, err error) {
|
||||
bookId, err := d.mr.GetMostDownloaded(num, d.stats)
|
||||
statsColl := d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
mr := NewMR(d.session.DB(DB_NAME))
|
||||
bookId, err := mr.GetMostDownloaded(num, statsColl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
books = make([]Book, num)
|
||||
for i, id := range bookId {
|
||||
d.books.Find(bson.M{"_id": id}).One(&books[i])
|
||||
booksColl := d.session.DB(DB_NAME).C(BOOKS_COLL)
|
||||
booksColl.Find(bson.M{"_id": id}).One(&books[i])
|
||||
books[i].Id = bson.ObjectId(books[i].Id).Hex()
|
||||
}
|
||||
return
|
||||
|
@ -214,7 +224,8 @@ func (d *DB) GetNewBooks(r ...int) (books []Book, num int, err error) {
|
|||
|
||||
func (d *DB) BookActive(id bson.ObjectId) bool {
|
||||
var book Book
|
||||
err := d.books.Find(bson.M{"_id": id}).One(&book)
|
||||
booksColl := d.session.DB(DB_NAME).C(BOOKS_COLL)
|
||||
err := booksColl.Find(bson.M{"_id": id}).One(&book)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
@ -226,7 +237,9 @@ func (d *DB) GetFS(prefix string) *mgo.GridFS {
|
|||
}
|
||||
|
||||
func (d *DB) GetTags(numTags int) ([]string, error) {
|
||||
return d.mr.GetTags(numTags, d.books)
|
||||
booksColl := d.session.DB(DB_NAME).C(BOOKS_COLL)
|
||||
mr := NewMR(d.session.DB(DB_NAME))
|
||||
return mr.GetTags(numTags, booksColl)
|
||||
}
|
||||
|
||||
type Visits struct {
|
||||
|
@ -235,13 +248,19 @@ type Visits struct {
|
|||
}
|
||||
|
||||
func (d *DB) GetHourVisits(start time.Time) ([]Visits, error) {
|
||||
return d.mr.GetHourVisits(start, d.stats)
|
||||
statsColl := d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
mr := NewMR(d.session.DB(DB_NAME))
|
||||
return mr.GetHourVisits(start, statsColl)
|
||||
}
|
||||
|
||||
func (d *DB) GetDayVisits(start time.Time) ([]Visits, error) {
|
||||
return d.mr.GetDayVisits(start, d.stats)
|
||||
statsColl := d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
mr := NewMR(d.session.DB(DB_NAME))
|
||||
return mr.GetDayVisits(start, statsColl)
|
||||
}
|
||||
|
||||
func (d *DB) GetMonthVisits(start time.Time) ([]Visits, error) {
|
||||
return d.mr.GetMonthVisits(start, d.stats)
|
||||
statsColl := d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
mr := NewMR(d.session.DB(DB_NAME))
|
||||
return mr.GetMonthVisits(start, statsColl)
|
||||
}
|
||||
|
|
57
mapreduce.go
57
mapreduce.go
|
@ -7,30 +7,12 @@ import (
|
|||
)
|
||||
|
||||
type MR struct {
|
||||
meta *mgo.Collection
|
||||
tags *mgo.Collection
|
||||
visited *mgo.Collection
|
||||
downloaded *mgo.Collection
|
||||
hourly_raw *mgo.Collection
|
||||
daily_raw *mgo.Collection
|
||||
monthly_raw *mgo.Collection
|
||||
hourly *mgo.Collection
|
||||
daily *mgo.Collection
|
||||
monthly *mgo.Collection
|
||||
database *mgo.Database
|
||||
}
|
||||
|
||||
func NewMR(database *mgo.Database) *MR {
|
||||
m := new(MR)
|
||||
m.meta = database.C(META_COLL)
|
||||
m.tags = database.C(TAGS_COLL)
|
||||
m.visited = database.C(VISITED_COLL)
|
||||
m.downloaded = database.C(DOWNLOADED_COLL)
|
||||
m.hourly_raw = database.C(HOURLY_VISITS_COLL + "_raw")
|
||||
m.daily_raw = database.C(DAILY_VISITS_COLL + "_raw")
|
||||
m.monthly_raw = database.C(MONTHLY_VISITS_COLL + "_raw")
|
||||
m.hourly = database.C(HOURLY_VISITS_COLL)
|
||||
m.daily = database.C(DAILY_VISITS_COLL)
|
||||
m.monthly = database.C(MONTHLY_VISITS_COLL)
|
||||
m.database = database
|
||||
return m
|
||||
}
|
||||
|
||||
|
@ -56,7 +38,8 @@ func (m *MR) GetTags(numTags int, booksColl *mgo.Collection) ([]string, error) {
|
|||
var result []struct {
|
||||
Tag string "_id"
|
||||
}
|
||||
err := m.tags.Find(nil).Sort("-value").Limit(numTags).All(&result)
|
||||
tagsColl := m.database.C(TAGS_COLL)
|
||||
err := tagsColl.Find(nil).Sort("-value").Limit(numTags).All(&result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -88,7 +71,8 @@ func (m *MR) GetMostVisited(num int, statsColl *mgo.Collection) ([]bson.ObjectId
|
|||
var result []struct {
|
||||
Book bson.ObjectId "_id"
|
||||
}
|
||||
err := m.visited.Find(nil).Sort("-value").Limit(num).All(&result)
|
||||
visitedColl := m.database.C(VISITED_COLL)
|
||||
err := visitedColl.Find(nil).Sort("-value").Limit(num).All(&result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -120,7 +104,8 @@ func (m *MR) GetMostDownloaded(num int, statsColl *mgo.Collection) ([]bson.Objec
|
|||
var result []struct {
|
||||
Book bson.ObjectId "_id"
|
||||
}
|
||||
err := m.downloaded.Find(nil).Sort("-value").Limit(num).All(&result)
|
||||
downloadedColl := m.database.C(DOWNLOADED_COLL)
|
||||
err := downloadedColl.Find(nil).Sort("-value").Limit(num).All(&result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -157,14 +142,16 @@ func (m *MR) GetHourVisits(start time.Time, statsColl *mgo.Collection) ([]Visits
|
|||
emit(this['_id']['date'], 1);
|
||||
}`
|
||||
mr2.Reduce = reduce
|
||||
err = m.update(&mr2, bson.M{}, m.hourly_raw, HOURLY_VISITS_COLL)
|
||||
hourly_raw := m.database.C(HOURLY_VISITS_COLL + "_raw")
|
||||
err = m.update(&mr2, bson.M{}, hourly_raw, HOURLY_VISITS_COLL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var result []Visits
|
||||
err := m.hourly.Find(nil).All(&result)
|
||||
hourlyColl := m.database.C(HOURLY_VISITS_COLL)
|
||||
err := hourlyColl.Find(nil).All(&result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
|
@ -192,14 +179,16 @@ func (m *MR) GetDayVisits(start time.Time, statsColl *mgo.Collection) ([]Visits,
|
|||
emit(this['_id']['date'], 1);
|
||||
}`
|
||||
mr2.Reduce = reduce
|
||||
err = m.update(&mr2, bson.M{}, m.daily_raw, DAILY_VISITS_COLL)
|
||||
daily_raw := m.database.C(DAILY_VISITS_COLL + "_raw")
|
||||
err = m.update(&mr2, bson.M{}, daily_raw, DAILY_VISITS_COLL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var result []Visits
|
||||
err := m.daily.Find(nil).All(&result)
|
||||
dailyColl := m.database.C(DAILY_VISITS_COLL)
|
||||
err := dailyColl.Find(nil).All(&result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
|
@ -226,19 +215,22 @@ func (m *MR) GetMonthVisits(start time.Time, statsColl *mgo.Collection) ([]Visit
|
|||
emit(this['_id']['date'], 1);
|
||||
}`
|
||||
mr2.Reduce = reduce
|
||||
err = m.update(&mr2, bson.M{}, m.monthly_raw, MONTHLY_VISITS_COLL)
|
||||
monthly_raw := m.database.C(MONTHLY_VISITS_COLL + "_raw")
|
||||
err = m.update(&mr2, bson.M{}, monthly_raw, MONTHLY_VISITS_COLL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var result []Visits
|
||||
err := m.monthly.Find(nil).All(&result)
|
||||
monthlyColl := m.database.C(MONTHLY_VISITS_COLL)
|
||||
err := monthlyColl.Find(nil).All(&result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (m *MR) update(mr *mgo.MapReduce, query bson.M, queryColl *mgo.Collection, storeColl string) error {
|
||||
_, err := m.meta.RemoveAll(bson.M{"type": storeColl})
|
||||
metaColl := m.database.C(META_COLL)
|
||||
_, err := metaColl.RemoveAll(bson.M{"type": storeColl})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -249,14 +241,15 @@ func (m *MR) update(mr *mgo.MapReduce, query bson.M, queryColl *mgo.Collection,
|
|||
return err
|
||||
}
|
||||
|
||||
return m.meta.Insert(bson.M{"type": storeColl})
|
||||
return metaColl.Insert(bson.M{"type": storeColl})
|
||||
}
|
||||
|
||||
func (m *MR) isOutdated(coll string, minutes float64) bool {
|
||||
var result struct {
|
||||
Id bson.ObjectId `bson:"_id"`
|
||||
}
|
||||
err := m.meta.Find(bson.M{"type": coll}).One(&result)
|
||||
metaColl := m.database.C(META_COLL)
|
||||
err := metaColl.Find(bson.M{"type": coll}).One(&result)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
|
|
40
news.go
40
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 {
|
||||
|
|
58
reader.go
58
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)
|
||||
}
|
||||
|
|
24
search.go
24
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
51
stats.go
51
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
|
||||
|
||||
|
|
22
store.go
22
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,37 +24,37 @@ func OpenBook(id bson.ObjectId) (*epubgo.Epub, error) {
|
|||
return epubgo.Load(reader, int64(len(buff)))
|
||||
}
|
||||
|
||||
func StoreNewFile(name string, file io.Reader) (bson.ObjectId, 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 {
|
||||
return "", err
|
||||
return "", 0, err
|
||||
}
|
||||
defer fw.Close()
|
||||
|
||||
_, err = io.Copy(fw, file)
|
||||
size, err := io.Copy(fw, file)
|
||||
id, _ := fw.Id().(bson.ObjectId)
|
||||
return id, err
|
||||
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 {
|
||||
|
|
15
template.go
15
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
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,23 @@
|
|||
{{template "header.html" .S}}
|
||||
|
||||
<p>The <strong>Imperial Library of Trantor</strong> (also known as <em>Galactic Library</em>) is a repository of ebooks on ePub format.</p>
|
||||
<p>The <strong>Imperial Library of Trantor</strong> (also known as <em>Galactic Library</em>) is a repository of DRM-free ebooks on ePub format.</p>
|
||||
|
||||
<p>You can <a href="/upload/">upload</a> your books. And one of our librarians will take care to store them on our vast bookshelfs and make it available for the rest of the galaxy.</p>
|
||||
|
||||
<p>We like to pay the authors, but not the corporations that make profit from them. We won't listen to any content remove request from corporations, editorials, right management organizations or any other blood-suckers.<p>
|
||||
|
||||
<h4>Status</h4>
|
||||
|
||||
<p>The Imperial Library of Trantor it's in beta-status. We are working to provide a good user experience, but it's still in early development.</p>
|
||||
|
||||
<p><b>Any help is welcome</b>. You can write us comments to our email address (zenow@riseup.net), upload your epubs, download our <a href="https://gitorious.org/trantor">source code</a> hack it and send us patches, ...</p>
|
||||
|
||||
<h4>Copyright</h4>
|
||||
|
||||
<p>Copyright laws are obsolete. With the technology to copy books without cost we can finally have universal access to the culture. We can provide the tools to allow everybody read any book without dependence on their monetary resources.</p>
|
||||
|
||||
<p>Of course we have to feed the authors, but with the capitalist way of commercialize culture now we are doing a really bad job at that. We are feeding big corporations, not the authors.</p>
|
||||
|
||||
<p>The Imperial Library of Trantor won't listen to any content remove request from corporations, editorials, right management organizations or any other blood-suckers.<p>
|
||||
|
||||
<h4>Donations</h4>
|
||||
|
||||
<p>If you feel like donate some bitcoins we'll gladly accept them. You can request one bitcoin key for you or use our public bitcoin key:</p>
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
{{if .Isbn}}
|
||||
<guid isPermaLink="false">ISBN: {{.Isbn}}</guid>
|
||||
{{end}}
|
||||
<enclosure url="{{$baseURL}}/download/{{.Id}}/{{.Title}}.epub" length="{{.FileSize}}" type="application/epub+zip" />
|
||||
{{range .Subject}}
|
||||
{{if .}}
|
||||
<category>{{.}}</category>
|
||||
|
|
|
@ -11,3 +11,5 @@ Password:
|
|||
- getISBNnDesc (31/5/2013). Import the ISBN and the description with changes of lines to the database
|
||||
|
||||
- coverNew. Reload the cover from all the new books
|
||||
|
||||
- addsize. Add the size of the books to the book metadata
|
||||
|
|
38
tools/addsize/addsize.go
Normal file
38
tools/addsize/addsize.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"labix.org/v2/mgo/bson"
|
||||
)
|
||||
|
||||
func main() {
|
||||
db = initDB()
|
||||
defer db.Close()
|
||||
books, _, _ := db.GetBooks(bson.M{})
|
||||
|
||||
for _, book := range books {
|
||||
size, err := getSize(book.File)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
continue
|
||||
}
|
||||
err = db.UpdateBook(bson.ObjectIdHex(book.Id), bson.M{"filesize": size})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type file struct {
|
||||
Length int
|
||||
}
|
||||
|
||||
func getSize(id bson.ObjectId) (int, error) {
|
||||
fs := db.GetFS(FS_BOOKS)
|
||||
var f file
|
||||
err := fs.Find(bson.M{"_id": id}).One(&f)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return f.Length, nil
|
||||
}
|
45
tools/addsize/config.go
Normal file
45
tools/addsize/config.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
package main
|
||||
|
||||
const (
|
||||
PORT = "8080"
|
||||
|
||||
DB_IP = "127.0.0.1"
|
||||
DB_NAME = "trantor"
|
||||
META_COLL = "meta"
|
||||
BOOKS_COLL = "books"
|
||||
TAGS_COLL = "tags"
|
||||
VISITED_COLL = "visited"
|
||||
DOWNLOADED_COLL = "downloaded"
|
||||
HOURLY_VISITS_COLL = "visits.hourly"
|
||||
DAILY_VISITS_COLL = "visits.daily"
|
||||
MONTHLY_VISITS_COLL = "visits.monthly"
|
||||
USERS_COLL = "users"
|
||||
NEWS_COLL = "news"
|
||||
STATS_COLL = "statistics"
|
||||
FS_BOOKS = "fs_books"
|
||||
FS_IMGS = "fs_imgs"
|
||||
|
||||
PASS_SALT = "ImperialLibSalt"
|
||||
MINUTES_UPDATE_TAGS = 11
|
||||
MINUTES_UPDATE_VISITED = 41
|
||||
MINUTES_UPDATE_DOWNLOADED = 47
|
||||
MINUTES_UPDATE_HOURLY = 31
|
||||
MINUTES_UPDATE_DAILY = 60*12 + 7
|
||||
MINUTES_UPDATE_MONTHLY = 60*24 + 11
|
||||
TAGS_DISPLAY = 50
|
||||
SEARCH_ITEMS_PAGE = 20
|
||||
NEW_ITEMS_PAGE = 50
|
||||
NUM_NEWS = 10
|
||||
DAYS_NEWS_INDEXPAGE = 15
|
||||
|
||||
TEMPLATE_PATH = "templates/"
|
||||
CSS_PATH = "css/"
|
||||
JS_PATH = "js/"
|
||||
IMG_PATH = "img/"
|
||||
|
||||
IMG_WIDTH_BIG = 300
|
||||
IMG_WIDTH_SMALL = 60
|
||||
IMG_QUALITY = 80
|
||||
|
||||
CHAN_SIZE = 100
|
||||
)
|
243
tools/addsize/database.go
Normal file
243
tools/addsize/database.go
Normal file
|
@ -0,0 +1,243 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"labix.org/v2/mgo"
|
||||
"labix.org/v2/mgo/bson"
|
||||
"time"
|
||||
)
|
||||
|
||||
var db *DB
|
||||
|
||||
type Book struct {
|
||||
Id string `bson:"_id"`
|
||||
Title string
|
||||
Author []string
|
||||
Contributor string
|
||||
Publisher string
|
||||
Description string
|
||||
Subject []string
|
||||
Date string
|
||||
Lang []string
|
||||
Isbn string
|
||||
Type string
|
||||
Format string
|
||||
Source string
|
||||
Relation string
|
||||
Coverage string
|
||||
Rights string
|
||||
Meta string
|
||||
File bson.ObjectId
|
||||
FileSize int
|
||||
Cover bson.ObjectId
|
||||
CoverSmall bson.ObjectId
|
||||
Active bool
|
||||
Keywords []string
|
||||
}
|
||||
|
||||
type News struct {
|
||||
Date time.Time
|
||||
Text string
|
||||
}
|
||||
|
||||
type DB struct {
|
||||
session *mgo.Session
|
||||
books *mgo.Collection
|
||||
user *mgo.Collection
|
||||
news *mgo.Collection
|
||||
stats *mgo.Collection
|
||||
mr *MR
|
||||
}
|
||||
|
||||
func initDB() *DB {
|
||||
var err error
|
||||
d := new(DB)
|
||||
d.session, err = mgo.Dial(DB_IP)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
database := d.session.DB(DB_NAME)
|
||||
d.books = database.C(BOOKS_COLL)
|
||||
d.user = database.C(USERS_COLL)
|
||||
d.news = database.C(NEWS_COLL)
|
||||
d.stats = database.C(STATS_COLL)
|
||||
d.mr = NewMR(database)
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *DB) Close() {
|
||||
d.session.Close()
|
||||
}
|
||||
|
||||
func md5Pass(pass string) []byte {
|
||||
h := md5.New()
|
||||
hash := h.Sum(([]byte)(PASS_SALT + pass))
|
||||
return hash
|
||||
}
|
||||
|
||||
func (d *DB) SetPassword(user string, pass string) error {
|
||||
hash := md5Pass(pass)
|
||||
return d.user.Update(bson.M{"user": user}, bson.M{"$set": bson.M{"pass": hash}})
|
||||
}
|
||||
|
||||
func (d *DB) UserValid(user string, pass string) bool {
|
||||
hash := md5Pass(pass)
|
||||
n, err := d.user.Find(bson.M{"user": user, "pass": hash}).Count()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return n != 0
|
||||
}
|
||||
|
||||
func (d *DB) UserRole(user string) string {
|
||||
type result struct {
|
||||
Role string
|
||||
}
|
||||
res := result{}
|
||||
err := d.user.Find(bson.M{"user": user}).One(&res)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return res.Role
|
||||
}
|
||||
|
||||
func (d *DB) AddNews(text string) error {
|
||||
var news News
|
||||
news.Text = text
|
||||
news.Date = time.Now()
|
||||
return d.news.Insert(news)
|
||||
}
|
||||
|
||||
func (d *DB) GetNews(num int, days int) (news []News, err error) {
|
||||
query := bson.M{}
|
||||
if days != 0 {
|
||||
duration := time.Duration(-24*days) * time.Hour
|
||||
date := time.Now().Add(duration)
|
||||
query = bson.M{"date": bson.M{"$gt": date}}
|
||||
}
|
||||
q := d.news.Find(query).Sort("-date").Limit(num)
|
||||
err = q.All(&news)
|
||||
return
|
||||
}
|
||||
|
||||
func (d *DB) InsertStats(stats interface{}) error {
|
||||
return d.stats.Insert(stats)
|
||||
}
|
||||
|
||||
func (d *DB) InsertBook(book interface{}) error {
|
||||
return d.books.Insert(book)
|
||||
}
|
||||
|
||||
func (d *DB) RemoveBook(id bson.ObjectId) error {
|
||||
return d.books.Remove(bson.M{"_id": id})
|
||||
}
|
||||
|
||||
func (d *DB) UpdateBook(id bson.ObjectId, data interface{}) error {
|
||||
return d.books.Update(bson.M{"_id": id}, bson.M{"$set": data})
|
||||
}
|
||||
|
||||
/* optional parameters: length and start index
|
||||
*
|
||||
* Returns: list of books, number found and err
|
||||
*/
|
||||
func (d *DB) GetBooks(query bson.M, r ...int) (books []Book, num int, err error) {
|
||||
var start, length int
|
||||
if len(r) > 0 {
|
||||
length = r[0]
|
||||
if len(r) > 1 {
|
||||
start = r[1]
|
||||
}
|
||||
}
|
||||
q := d.books.Find(query).Sort("-_id")
|
||||
num, err = q.Count()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if start != 0 {
|
||||
q = q.Skip(start)
|
||||
}
|
||||
if length != 0 {
|
||||
q = q.Limit(length)
|
||||
}
|
||||
|
||||
err = q.All(&books)
|
||||
for i, b := range books {
|
||||
books[i].Id = bson.ObjectId(b.Id).Hex()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/* Get the most visited books
|
||||
*/
|
||||
func (d *DB) GetVisitedBooks(num int) (books []Book, err error) {
|
||||
bookId, err := d.mr.GetMostVisited(num, d.stats)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
books = make([]Book, num)
|
||||
for i, id := range bookId {
|
||||
d.books.Find(bson.M{"_id": id}).One(&books[i])
|
||||
books[i].Id = bson.ObjectId(books[i].Id).Hex()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/* Get the most downloaded books
|
||||
*/
|
||||
func (d *DB) GetDownloadedBooks(num int) (books []Book, err error) {
|
||||
bookId, err := d.mr.GetMostDownloaded(num, d.stats)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
books = make([]Book, num)
|
||||
for i, id := range bookId {
|
||||
d.books.Find(bson.M{"_id": id}).One(&books[i])
|
||||
books[i].Id = bson.ObjectId(books[i].Id).Hex()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/* optional parameters: length and start index
|
||||
*
|
||||
* Returns: list of books, number found and err
|
||||
*/
|
||||
func (d *DB) GetNewBooks(r ...int) (books []Book, num int, err error) {
|
||||
return d.GetBooks(bson.M{"$nor": []bson.M{{"active": true}}}, r...)
|
||||
}
|
||||
|
||||
func (d *DB) BookActive(id bson.ObjectId) bool {
|
||||
var book Book
|
||||
err := d.books.Find(bson.M{"_id": id}).One(&book)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return book.Active
|
||||
}
|
||||
|
||||
func (d *DB) GetFS(prefix string) *mgo.GridFS {
|
||||
return d.session.DB(DB_NAME).GridFS(prefix)
|
||||
}
|
||||
|
||||
func (d *DB) GetTags(numTags int) ([]string, error) {
|
||||
return d.mr.GetTags(numTags, d.books)
|
||||
}
|
||||
|
||||
type Visits struct {
|
||||
Date int64 "_id"
|
||||
Count int "value"
|
||||
}
|
||||
|
||||
func (d *DB) GetHourVisits(start time.Time) ([]Visits, error) {
|
||||
return d.mr.GetHourVisits(start, d.stats)
|
||||
}
|
||||
|
||||
func (d *DB) GetDayVisits(start time.Time) ([]Visits, error) {
|
||||
return d.mr.GetDayVisits(start, d.stats)
|
||||
}
|
||||
|
||||
func (d *DB) GetMonthVisits(start time.Time) ([]Visits, error) {
|
||||
return d.mr.GetMonthVisits(start, d.stats)
|
||||
}
|
266
tools/addsize/mapreduce.go
Normal file
266
tools/addsize/mapreduce.go
Normal file
|
@ -0,0 +1,266 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"labix.org/v2/mgo"
|
||||
"labix.org/v2/mgo/bson"
|
||||
"time"
|
||||
)
|
||||
|
||||
type MR struct {
|
||||
meta *mgo.Collection
|
||||
tags *mgo.Collection
|
||||
visited *mgo.Collection
|
||||
downloaded *mgo.Collection
|
||||
hourly_raw *mgo.Collection
|
||||
daily_raw *mgo.Collection
|
||||
monthly_raw *mgo.Collection
|
||||
hourly *mgo.Collection
|
||||
daily *mgo.Collection
|
||||
monthly *mgo.Collection
|
||||
}
|
||||
|
||||
func NewMR(database *mgo.Database) *MR {
|
||||
m := new(MR)
|
||||
m.meta = database.C(META_COLL)
|
||||
m.tags = database.C(TAGS_COLL)
|
||||
m.visited = database.C(VISITED_COLL)
|
||||
m.downloaded = database.C(DOWNLOADED_COLL)
|
||||
m.hourly_raw = database.C(HOURLY_VISITS_COLL + "_raw")
|
||||
m.daily_raw = database.C(DAILY_VISITS_COLL + "_raw")
|
||||
m.monthly_raw = database.C(MONTHLY_VISITS_COLL + "_raw")
|
||||
m.hourly = database.C(HOURLY_VISITS_COLL)
|
||||
m.daily = database.C(DAILY_VISITS_COLL)
|
||||
m.monthly = database.C(MONTHLY_VISITS_COLL)
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MR) GetTags(numTags int, booksColl *mgo.Collection) ([]string, error) {
|
||||
if m.isOutdated(TAGS_COLL, MINUTES_UPDATE_TAGS) {
|
||||
var mr mgo.MapReduce
|
||||
mr.Map = `function() {
|
||||
if (this.subject) {
|
||||
this.subject.forEach(function(s) { emit(s, 1); });
|
||||
}
|
||||
}`
|
||||
mr.Reduce = `function(tag, vals) {
|
||||
var count = 0;
|
||||
vals.forEach(function() { count += 1; });
|
||||
return count;
|
||||
}`
|
||||
err := m.update(&mr, bson.M{"active": true}, booksColl, TAGS_COLL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var result []struct {
|
||||
Tag string "_id"
|
||||
}
|
||||
err := m.tags.Find(nil).Sort("-value").Limit(numTags).All(&result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tags := make([]string, len(result))
|
||||
for i, r := range result {
|
||||
tags[i] = r.Tag
|
||||
}
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
func (m *MR) GetMostVisited(num int, statsColl *mgo.Collection) ([]bson.ObjectId, error) {
|
||||
if m.isOutdated(VISITED_COLL, MINUTES_UPDATE_VISITED) {
|
||||
var mr mgo.MapReduce
|
||||
mr.Map = `function() {
|
||||
emit(this.id, 1);
|
||||
}`
|
||||
mr.Reduce = `function(tag, vals) {
|
||||
var count = 0;
|
||||
vals.forEach(function() { count += 1; });
|
||||
return count;
|
||||
}`
|
||||
err := m.update(&mr, bson.M{"section": "book"}, statsColl, VISITED_COLL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var result []struct {
|
||||
Book bson.ObjectId "_id"
|
||||
}
|
||||
err := m.visited.Find(nil).Sort("-value").Limit(num).All(&result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
books := make([]bson.ObjectId, len(result))
|
||||
for i, r := range result {
|
||||
books[i] = r.Book
|
||||
}
|
||||
return books, nil
|
||||
}
|
||||
|
||||
func (m *MR) GetMostDownloaded(num int, statsColl *mgo.Collection) ([]bson.ObjectId, error) {
|
||||
if m.isOutdated(DOWNLOADED_COLL, MINUTES_UPDATE_DOWNLOADED) {
|
||||
var mr mgo.MapReduce
|
||||
mr.Map = `function() {
|
||||
emit(this.id, 1);
|
||||
}`
|
||||
mr.Reduce = `function(tag, vals) {
|
||||
var count = 0;
|
||||
vals.forEach(function() { count += 1; });
|
||||
return count;
|
||||
}`
|
||||
err := m.update(&mr, bson.M{"section": "download"}, statsColl, DOWNLOADED_COLL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var result []struct {
|
||||
Book bson.ObjectId "_id"
|
||||
}
|
||||
err := m.downloaded.Find(nil).Sort("-value").Limit(num).All(&result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
books := make([]bson.ObjectId, len(result))
|
||||
for i, r := range result {
|
||||
books[i] = r.Book
|
||||
}
|
||||
return books, nil
|
||||
}
|
||||
|
||||
func (m *MR) GetHourVisits(start time.Time, statsColl *mgo.Collection) ([]Visits, error) {
|
||||
if m.isOutdated(HOURLY_VISITS_COLL, MINUTES_UPDATE_HOURLY) {
|
||||
const reduce = `function(date, vals) {
|
||||
var count = 0;
|
||||
vals.forEach(function(v) { count += v; });
|
||||
return count;
|
||||
}`
|
||||
var mr mgo.MapReduce
|
||||
mr.Map = `function() {
|
||||
var date = Date.UTC(this.date.getUTCFullYear(),
|
||||
this.date.getUTCMonth(),
|
||||
this.date.getUTCDate(),
|
||||
this.date.getUTCHours());
|
||||
emit({date: date, session: this.session}, 1);
|
||||
}`
|
||||
mr.Reduce = reduce
|
||||
err := m.update(&mr, bson.M{"date": bson.M{"$gte": start}}, statsColl, HOURLY_VISITS_COLL+"_raw")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var mr2 mgo.MapReduce
|
||||
mr2.Map = `function() {
|
||||
emit(this['_id']['date'], 1);
|
||||
}`
|
||||
mr2.Reduce = reduce
|
||||
err = m.update(&mr2, bson.M{}, m.hourly_raw, HOURLY_VISITS_COLL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var result []Visits
|
||||
err := m.hourly.Find(nil).All(&result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (m *MR) GetDayVisits(start time.Time, statsColl *mgo.Collection) ([]Visits, error) {
|
||||
if m.isOutdated(DAILY_VISITS_COLL, MINUTES_UPDATE_DAILY) {
|
||||
const reduce = `function(date, vals) {
|
||||
var count = 0;
|
||||
vals.forEach(function(v) { count += v; });
|
||||
return count;
|
||||
}`
|
||||
var mr mgo.MapReduce
|
||||
mr.Map = `function() {
|
||||
var date = Date.UTC(this.date.getUTCFullYear(),
|
||||
this.date.getUTCMonth(),
|
||||
this.date.getUTCDate());
|
||||
emit({date: date, session: this.session}, 1);
|
||||
}`
|
||||
mr.Reduce = reduce
|
||||
err := m.update(&mr, bson.M{"date": bson.M{"$gte": start}}, statsColl, DAILY_VISITS_COLL+"_raw")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var mr2 mgo.MapReduce
|
||||
mr2.Map = `function() {
|
||||
emit(this['_id']['date'], 1);
|
||||
}`
|
||||
mr2.Reduce = reduce
|
||||
err = m.update(&mr2, bson.M{}, m.daily_raw, DAILY_VISITS_COLL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var result []Visits
|
||||
err := m.daily.Find(nil).All(&result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (m *MR) GetMonthVisits(start time.Time, statsColl *mgo.Collection) ([]Visits, error) {
|
||||
if m.isOutdated(MONTHLY_VISITS_COLL, MINUTES_UPDATE_MONTHLY) {
|
||||
const reduce = `function(date, vals) {
|
||||
var count = 0;
|
||||
vals.forEach(function(v) { count += v; });
|
||||
return count;
|
||||
}`
|
||||
var mr mgo.MapReduce
|
||||
mr.Map = `function() {
|
||||
var date = Date.UTC(this.date.getUTCFullYear(),
|
||||
this.date.getUTCMonth());
|
||||
emit({date: date, session: this.session}, 1);
|
||||
}`
|
||||
mr.Reduce = reduce
|
||||
err := m.update(&mr, bson.M{"date": bson.M{"$gte": start}}, statsColl, MONTHLY_VISITS_COLL+"_raw")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var mr2 mgo.MapReduce
|
||||
mr2.Map = `function() {
|
||||
emit(this['_id']['date'], 1);
|
||||
}`
|
||||
mr2.Reduce = reduce
|
||||
err = m.update(&mr2, bson.M{}, m.monthly_raw, MONTHLY_VISITS_COLL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var result []Visits
|
||||
err := m.monthly.Find(nil).All(&result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (m *MR) update(mr *mgo.MapReduce, query bson.M, queryColl *mgo.Collection, storeColl string) error {
|
||||
_, err := m.meta.RemoveAll(bson.M{"type": storeColl})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mr.Out = bson.M{"replace": storeColl}
|
||||
_, err = queryColl.Find(query).MapReduce(mr, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.meta.Insert(bson.M{"type": storeColl})
|
||||
}
|
||||
|
||||
func (m *MR) isOutdated(coll string, minutes float64) bool {
|
||||
var result struct {
|
||||
Id bson.ObjectId `bson:"_id"`
|
||||
}
|
||||
err := m.meta.Find(bson.M{"type": coll}).One(&result)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
lastUpdate := result.Id.Time()
|
||||
return time.Since(lastUpdate).Minutes() > minutes
|
||||
}
|
149
trantor.go
149
trantor.go
|
@ -13,26 +13,26 @@ 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)
|
||||
}
|
||||
|
||||
type bookData struct {
|
||||
|
@ -41,62 +41,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 {
|
||||
|
@ -109,70 +108,70 @@ 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("GET")
|
||||
r.HandleFunc("/login/", GatherStats(loginPostHandler)).Methods("POST")
|
||||
r.HandleFunc("/create_user/", GatherStats(createUserHandler)).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("GET")
|
||||
r.HandleFunc("/login/", GatherStats(loginPostHandler, db)).Methods("POST")
|
||||
r.HandleFunc("/create_user/", GatherStats(createUserHandler, 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))
|
||||
|
|
43
upload.go
43
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,16 +40,17 @@ func processFile(req uploadRequest) {
|
|||
}
|
||||
defer epub.Close()
|
||||
|
||||
book := parseFile(epub)
|
||||
book := parseFile(epub, db)
|
||||
title, _ := book["title"].(string)
|
||||
req.file.Seek(0, 0)
|
||||
id, 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
|
||||
}
|
||||
|
||||
book["file"] = id
|
||||
book["filesize"] = size
|
||||
err = db.InsertBook(book)
|
||||
if err != nil {
|
||||
log.Println("Error storing metadata (", title, "):", err)
|
||||
|
@ -56,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
|
||||
}
|
||||
|
@ -74,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 {
|
||||
|
@ -99,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)
|
||||
|
@ -132,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
|
||||
|
|
74
user.go
74
user.go
|
@ -5,71 +5,71 @@ import (
|
|||
"net/http"
|
||||
)
|
||||
|
||||
func loginHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
|
||||
func loginHandler(h handler) {
|
||||
var data statusData
|
||||
data.S = GetStatus(w, r)
|
||||
loadTemplate(w, "login", data)
|
||||
data.S = GetStatus(h)
|
||||
loadTemplate(h.w, "login", data)
|
||||
}
|
||||
|
||||
func loginPostHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
|
||||
user := r.FormValue("user")
|
||||
pass := r.FormValue("pass")
|
||||
if db.UserValid(user, pass) {
|
||||
func loginPostHandler(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)
|
||||
}
|
||||
|
||||
func createUserHandler(w http.ResponseWriter, r *http.Request, sess *Session) {
|
||||
pass := r.FormValue("pass")
|
||||
confirmPass := r.FormValue("confirmPass")
|
||||
func createUserHandler(h handler) {
|
||||
pass := h.r.FormValue("pass")
|
||||
confirmPass := h.r.FormValue("confirmPass")
|
||||
if pass != confirmPass {
|
||||
sess.Notify("Registration error!", "Passwords don't match", "error")
|
||||
h.sess.Notify("Registration error!", "Passwords don't match", "error")
|
||||
} else {
|
||||
user := r.FormValue("user")
|
||||
err := db.AddUser(user, pass)
|
||||
user := h.r.FormValue("user")
|
||||
err := h.db.AddUser(user, pass)
|
||||
if err == nil {
|
||||
sess.Notify("Account created!", "Welcome "+user+". Now you can login", "success")
|
||||
h.sess.Notify("Account created!", "Welcome "+user+". Now you can login", "success")
|
||||
} else {
|
||||
sess.Notify("Registration error!", "There was some database problem, if it keeps happening please inform me", "error")
|
||||
h.sess.Notify("Registration error!", "There was some database problem, if it keeps happening please inform me", "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 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")
|
||||
}
|
||||
sess.Save(w, r)
|
||||
h.sess.Save(h.w, h.r)
|
||||
}
|
||||
|
||||
var data settingsData
|
||||
data.S = GetStatus(w, r)
|
||||
loadTemplate(w, "settings", data)
|
||||
data.S = GetStatus(h)
|
||||
loadTemplate(h.w, "settings", data)
|
||||
}
|
||||
|
|
Reference in a new issue