From 147ed57fc87465a9beb07f4afdb2ed7ef7932558 Mon Sep 17 00:00:00 2001 From: Las Zenow Date: Tue, 3 Sep 2013 14:16:19 +0200 Subject: [PATCH 1/7] Remove the notifications once they are shown --- template.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template.go b/template.go index 30a820b..eb40b9b 100644 --- a/template.go +++ b/template.go @@ -25,12 +25,12 @@ type Status struct { func GetStatus(w http.ResponseWriter, r *http.Request) Status { var s Status sess := GetSession(r) - sess.Save(w, 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) return s } From 9e4b92772cfe2ff0b8406bff0a8fc4509eb02f56 Mon Sep 17 00:00:00 2001 From: Las Zenow Date: Wed, 18 Sep 2013 23:56:49 +0200 Subject: [PATCH 2/7] Add filesize to the books collection --- database.go | 1 + store.go | 8 ++++---- upload.go | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/database.go b/database.go index 662860c..e22693f 100644 --- a/database.go +++ b/database.go @@ -28,6 +28,7 @@ type Book struct { Rights string Meta string File bson.ObjectId + FileSize int Cover bson.ObjectId CoverSmall bson.ObjectId Active bool diff --git a/store.go b/store.go index e50e65f..ea638fb 100644 --- a/store.go +++ b/store.go @@ -24,17 +24,17 @@ 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) (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 { diff --git a/upload.go b/upload.go index 3c0dfaa..35a0bcc 100644 --- a/upload.go +++ b/upload.go @@ -41,13 +41,14 @@ func processFile(req uploadRequest) { book := parseFile(epub) title, _ := book["title"].(string) req.file.Seek(0, 0) - id, err := StoreNewFile(title+".epub", req.file) + id, size, err := StoreNewFile(title+".epub", req.file) 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) From 43856dcd9bdde69df164c5403323cf57c211ea41 Mon Sep 17 00:00:00 2001 From: Las Zenow Date: Wed, 18 Sep 2013 23:58:05 +0200 Subject: [PATCH 3/7] Add enclosure to the rss feed --- templates/search_rss.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/search_rss.xml b/templates/search_rss.xml index 3effdad..94255fa 100644 --- a/templates/search_rss.xml +++ b/templates/search_rss.xml @@ -20,6 +20,7 @@ {{if .Isbn}} ISBN: {{.Isbn}} {{end}} + {{range .Subject}} {{if .}} {{.}} From f5ae093e02af594f4678527bdfad31928016507f Mon Sep 17 00:00:00 2001 From: Las Zenow Date: Thu, 19 Sep 2013 00:49:48 +0200 Subject: [PATCH 4/7] Script to add the size of the file to the book metadata --- .gitignore | 1 + tools/README | 2 + tools/addsize/addsize.go | 38 ++++++ tools/addsize/config.go | 45 +++++++ tools/addsize/database.go | 243 +++++++++++++++++++++++++++++++++ tools/addsize/mapreduce.go | 266 +++++++++++++++++++++++++++++++++++++ 6 files changed, 595 insertions(+) create mode 100644 tools/addsize/addsize.go create mode 100644 tools/addsize/config.go create mode 100644 tools/addsize/database.go create mode 100644 tools/addsize/mapreduce.go diff --git a/.gitignore b/.gitignore index 2392860..9510703 100644 --- a/.gitignore +++ b/.gitignore @@ -7,5 +7,6 @@ tools/update/update tools/togridfs/togridfs tools/getISBNnDesc/getISBNnDesc tools/coverNew/coverNew +tools/addsize/addsize tags .*.swp diff --git a/tools/README b/tools/README index ece1903..d0b3b4b 100644 --- a/tools/README +++ b/tools/README @@ -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 diff --git a/tools/addsize/addsize.go b/tools/addsize/addsize.go new file mode 100644 index 0000000..0c8d6a9 --- /dev/null +++ b/tools/addsize/addsize.go @@ -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 +} diff --git a/tools/addsize/config.go b/tools/addsize/config.go new file mode 100644 index 0000000..f92f3a3 --- /dev/null +++ b/tools/addsize/config.go @@ -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 +) diff --git a/tools/addsize/database.go b/tools/addsize/database.go new file mode 100644 index 0000000..e22693f --- /dev/null +++ b/tools/addsize/database.go @@ -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) +} diff --git a/tools/addsize/mapreduce.go b/tools/addsize/mapreduce.go new file mode 100644 index 0000000..dbadd19 --- /dev/null +++ b/tools/addsize/mapreduce.go @@ -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 +} From cef68a8f6eacf75443cb040f3df3f6c09284679b Mon Sep 17 00:00:00 2001 From: Las Zenow Date: Mon, 23 Sep 2013 13:42:39 +0200 Subject: [PATCH 5/7] Don't keep the collections open --- database.go | 75 ++++++++++++++++++++++++++++++---------------------- mapreduce.go | 57 ++++++++++++++++++--------------------- 2 files changed, 69 insertions(+), 63 deletions(-) diff --git a/database.go b/database.go index e22693f..deadfcf 100644 --- a/database.go +++ b/database.go @@ -42,11 +42,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 { @@ -56,13 +51,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 } @@ -78,12 +66,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 } @@ -95,7 +85,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 "" } @@ -106,7 +97,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) { @@ -116,25 +108,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 @@ -149,7 +146,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 @@ -171,14 +169,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 @@ -187,14 +188,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 @@ -210,7 +214,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 } @@ -222,7 +227,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 { @@ -231,13 +238,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) } diff --git a/mapreduce.go b/mapreduce.go index dbadd19..4f4b55c 100644 --- a/mapreduce.go +++ b/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 } From 41b376992a73a671086dbbb88b9233ed1b2bae6b Mon Sep 17 00:00:00 2001 From: Las Zenow Date: Mon, 23 Sep 2013 16:27:31 +0200 Subject: [PATCH 6/7] Database stops being a global variable With it now every handler creates it's own copy of the session. --- admin.go | 150 +++++++++++++++++++++++------------------------ cover.go | 48 ++++++++-------- database.go | 8 ++- news.go | 40 ++++++------- reader.go | 58 +++++++++---------- search.go | 24 ++++---- session.go | 2 +- stats.go | 51 ++++++++++------ store.go | 16 +++--- template.go | 15 +++-- trantor.go | 163 ++++++++++++++++++++++++++-------------------------- upload.go | 42 +++++++------- 12 files changed, 318 insertions(+), 299 deletions(-) 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 From 173ee045923d8be34af616eb8a3b419b14ed7548 Mon Sep 17 00:00:00 2001 From: Las Zenow Date: Tue, 24 Sep 2013 10:04:48 +0200 Subject: [PATCH 7/7] Add a copyright statement --- templates/about.html | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/templates/about.html b/templates/about.html index bd383fb..8240419 100644 --- a/templates/about.html +++ b/templates/about.html @@ -1,17 +1,23 @@ {{template "header.html" .S}} -

The Imperial Library of Trantor (also known as Galactic Library) is a repository of ebooks on ePub format.

+

The Imperial Library of Trantor (also known as Galactic Library) is a repository of DRM-free ebooks on ePub format.

You can upload 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.

-

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.

-

Status

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.

Any help is welcome. You can write us comments to our email address (zenow@riseup.net), upload your epubs, download our source code hack it and send us patches, ...

+

Copyright

+ +

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.

+ +

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.

+ +

The Imperial Library of Trantor won't listen to any content remove request from corporations, editorials, right management organizations or any other blood-suckers.

+

Donations

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: