Refactor database

This commit is contained in:
Las Zenow 2014-06-29 19:41:29 -05:00
parent 533f8241c2
commit 59eaa4e2aa
24 changed files with 712 additions and 436 deletions

2
.gitignore vendored
View file

@ -1,4 +1,4 @@
trantor trantor.git
tools/adduser/adduser tools/adduser/adduser
tools/update/update tools/update/update
tools/togridfs/togridfs tools/togridfs/togridfs

View file

@ -3,6 +3,7 @@ package main
import log "github.com/cihub/seelog" import log "github.com/cihub/seelog"
import ( import (
"git.gitorious.org/trantor/trantor.git/database"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"labix.org/v2/mgo/bson" "labix.org/v2/mgo/bson"
"net/http" "net/http"
@ -25,14 +26,14 @@ func deleteHandler(h handler) {
} }
id := bson.ObjectIdHex(idStr) id := bson.ObjectIdHex(idStr)
books, _, err := h.db.GetBooks(bson.M{"_id": id}) books, _, err := h.db.GetBooks(bson.M{"_id": id}, 0, 0)
if err != nil { if err != nil {
h.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 continue
} }
book := books[0] book := books[0]
DeleteBook(book, h.db) DeleteBook(book, h.db)
h.db.RemoveBook(id) h.db.DeleteBook(id)
if !book.Active { if !book.Active {
isNew = true isNew = true
@ -57,7 +58,7 @@ func editHandler(h handler) {
return return
} }
id := bson.ObjectIdHex(idStr) id := bson.ObjectIdHex(idStr)
books, _, err := h.db.GetBooks(bson.M{"_id": id}) books, _, err := h.db.GetBooks(bson.M{"_id": id}, 0, 0)
if err != nil { if err != nil {
notFound(h) notFound(h)
return return
@ -120,7 +121,7 @@ func saveHandler(h handler) {
type newBook struct { type newBook struct {
TitleFound int TitleFound int
AuthorFound int AuthorFound int
B Book B database.Book
} }
type newData struct { type newData struct {
S Status S Status
@ -161,8 +162,8 @@ func newHandler(h handler) {
} }
for i, b := range res { for i, b := range res {
data.Books[i].B = b data.Books[i].B = b
_, data.Books[i].TitleFound, _ = h.db.GetBooks(buildQuery("title:"+b.Title), 1) _, data.Books[i].TitleFound, _ = h.db.GetBooks(buildQuery("title:"+b.Title), 1, 0)
_, data.Books[i].AuthorFound, _ = h.db.GetBooks(buildQuery("author:"+strings.Join(b.Author, " author:")), 1) _, data.Books[i].AuthorFound, _ = h.db.GetBooks(buildQuery("author:"+strings.Join(b.Author, " author:")), 1, 0)
} }
data.Page = page + 1 data.Page = page + 1
if num > (page+1)*NEW_ITEMS_PAGE { if num > (page+1)*NEW_ITEMS_PAGE {
@ -188,7 +189,7 @@ func storeHandler(h handler) {
} }
id := bson.ObjectIdHex(idStr) id := bson.ObjectIdHex(idStr)
books, _, err := h.db.GetBooks(bson.M{"_id": id}) books, _, err := h.db.GetBooks(bson.M{"_id": id}, 0, 0)
if err != nil { if err != nil {
h.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 continue

View file

@ -3,24 +3,11 @@ package main
const ( const (
PORT = "8080" PORT = "8080"
DB_IP = "127.0.0.1" DB_IP = "127.0.0.1"
DB_NAME = "trantor" DB_NAME = "trantor"
META_COLL = "meta" META_COLL = "meta"
BOOKS_COLL = "books" FS_BOOKS = "fs_books"
TAGS_COLL = "tags" FS_IMGS = "fs_imgs"
VISITED_COLL = "visited"
DOWNLOADED_COLL = "downloaded"
HOURLY_VISITS_COLL = "visits.hourly"
DAILY_VISITS_COLL = "visits.daily"
MONTHLY_VISITS_COLL = "visits.monthly"
HOURLY_DOWNLOADS_COLL = "downloads.hourly"
DAILY_DOWNLOADS_COLL = "downloads.daily"
MONTHLY_DOWNLOADS_COLL = "downloads.monthly"
USERS_COLL = "users"
NEWS_COLL = "news"
STATS_COLL = "statistics"
FS_BOOKS = "fs_books"
FS_IMGS = "fs_imgs"
PASS_SALT = "ImperialLibSalt" PASS_SALT = "ImperialLibSalt"
MINUTES_UPDATE_TAGS = 11 MINUTES_UPDATE_TAGS = 11
@ -33,7 +20,6 @@ const (
MINUTES_UPDATE_DAILY_D = 60*12 + 13 MINUTES_UPDATE_DAILY_D = 60*12 + 13
MINUTES_UPDATE_MONTHLY_D = 60*24 + 17 MINUTES_UPDATE_MONTHLY_D = 60*24 + 17
MINUTES_UPDATE_LOGGER = 5 MINUTES_UPDATE_LOGGER = 5
TAGS_DISPLAY = 50
BOOKS_FRONT_PAGE = 6 BOOKS_FRONT_PAGE = 6
SEARCH_ITEMS_PAGE = 20 SEARCH_ITEMS_PAGE = 20
NEW_ITEMS_PAGE = 50 NEW_ITEMS_PAGE = 50

View file

@ -8,6 +8,7 @@ import _ "image/gif"
import ( import (
"bytes" "bytes"
"git.gitorious.org/go-pkg/epubgo.git" "git.gitorious.org/go-pkg/epubgo.git"
"git.gitorious.org/trantor/trantor.git/database"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/nfnt/resize" "github.com/nfnt/resize"
"image" "image"
@ -27,7 +28,7 @@ func coverHandler(h handler) {
return return
} }
id := bson.ObjectIdHex(vars["id"]) id := bson.ObjectIdHex(vars["id"])
books, _, err := h.db.GetBooks(bson.M{"_id": id}) books, _, err := h.db.GetBooks(bson.M{"_id": id}, 0, 0)
if err != nil || len(books) == 0 { if err != nil || len(books) == 0 {
notFound(h) notFound(h)
return return
@ -61,7 +62,7 @@ func coverHandler(h handler) {
io.Copy(h.w, f) io.Copy(h.w, f)
} }
func GetCover(e *epubgo.Epub, title string, db *DB) (bson.ObjectId, bson.ObjectId) { func GetCover(e *epubgo.Epub, title string, db *database.DB) (bson.ObjectId, bson.ObjectId) {
imgId, smallId := coverFromMetadata(e, title, db) imgId, smallId := coverFromMetadata(e, title, db)
if imgId != "" { if imgId != "" {
return imgId, smallId return imgId, smallId
@ -116,7 +117,7 @@ func GetCover(e *epubgo.Epub, title string, db *DB) (bson.ObjectId, bson.ObjectI
return "", "" return "", ""
} }
func coverFromMetadata(e *epubgo.Epub, title string, db *DB) (bson.ObjectId, bson.ObjectId) { func coverFromMetadata(e *epubgo.Epub, title string, db *database.DB) (bson.ObjectId, bson.ObjectId) {
metaList, _ := e.MetadataAttr("meta") metaList, _ := e.MetadataAttr("meta")
for _, meta := range metaList { for _, meta := range metaList {
if meta["name"] == "cover" { if meta["name"] == "cover" {
@ -130,7 +131,7 @@ func coverFromMetadata(e *epubgo.Epub, title string, db *DB) (bson.ObjectId, bso
return "", "" return "", ""
} }
func searchCommonCoverNames(e *epubgo.Epub, title string, db *DB) (bson.ObjectId, bson.ObjectId) { func searchCommonCoverNames(e *epubgo.Epub, title string, db *database.DB) (bson.ObjectId, bson.ObjectId) {
for _, p := range []string{"cover.jpg", "Images/cover.jpg", "images/cover.jpg", "cover.jpeg", "cover1.jpg", "cover1.jpeg"} { for _, p := range []string{"cover.jpg", "Images/cover.jpg", "images/cover.jpg", "cover.jpeg", "cover1.jpg", "cover1.jpeg"} {
img, err := e.OpenFile(p) img, err := e.OpenFile(p)
if err == nil { if err == nil {
@ -141,7 +142,7 @@ func searchCommonCoverNames(e *epubgo.Epub, title string, db *DB) (bson.ObjectId
return "", "" return "", ""
} }
func storeImg(img io.Reader, title string, db *DB) (bson.ObjectId, bson.ObjectId) { func storeImg(img io.Reader, title string, db *database.DB) (bson.ObjectId, bson.ObjectId) {
/* open the files */ /* open the files */
fBig, err := createCoverFile(title, db) fBig, err := createCoverFile(title, db)
if err != nil { if err != nil {
@ -187,7 +188,7 @@ func storeImg(img io.Reader, title string, db *DB) (bson.ObjectId, bson.ObjectId
return idBig, idSmall return idBig, idSmall
} }
func createCoverFile(title string, db *DB) (*mgo.GridFile, error) { func createCoverFile(title string, db *database.DB) (*mgo.GridFile, error) {
fs := db.GetFS(FS_IMGS) fs := db.GetFS(FS_IMGS)
return fs.Create(title + ".jpg") return fs.Create(title + ".jpg")
} }

View file

@ -1,345 +0,0 @@
package main
import log "github.com/cihub/seelog"
import (
"crypto/md5"
"errors"
"labix.org/v2/mgo"
"labix.org/v2/mgo/bson"
"os"
"time"
)
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
}
func initDB() *DB {
var err error
d := new(DB)
d.session, err = mgo.Dial(DB_IP)
if err != nil {
log.Critical(err)
os.Exit(1)
}
return d
}
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))
return hash
}
func (d *DB) SetPassword(user string, pass string) error {
hash := md5Pass(pass)
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)
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
}
return n != 0
}
func (d *DB) AddUser(user string, pass string) error {
hash := md5Pass(pass)
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 {
type result struct {
Role string
}
res := result{}
userColl := d.session.DB(DB_NAME).C(USERS_COLL)
err := userColl.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()
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) {
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}}
}
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 {
statsColl := d.session.DB(DB_NAME).C(STATS_COLL)
return statsColl.Insert(stats)
}
func (d *DB) InsertBook(book interface{}) error {
booksColl := d.session.DB(DB_NAME).C(BOOKS_COLL)
return booksColl.Insert(book)
}
func (d *DB) RemoveBook(id bson.ObjectId) error {
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 {
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
*
* 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]
}
}
booksColl := d.session.DB(DB_NAME).C(BOOKS_COLL)
q := booksColl.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() (books []Book, err error) {
visitedColl := d.session.DB(DB_NAME).C(VISITED_COLL)
bookId, err := GetBooksVisited(visitedColl)
if err != nil {
return nil, err
}
books = make([]Book, len(bookId))
for i, id := range bookId {
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
}
func (d *DB) UpdateMostVisited() error {
var u DBUpdate
u.src = d.session.DB(DB_NAME).C(STATS_COLL)
u.dst = d.session.DB(DB_NAME).C(VISITED_COLL)
return u.UpdateMostBooks("book")
}
/* Get the most downloaded books
*/
func (d *DB) GetDownloadedBooks() (books []Book, err error) {
downloadedColl := d.session.DB(DB_NAME).C(DOWNLOADED_COLL)
bookId, err := GetBooksVisited(downloadedColl)
if err != nil {
return nil, err
}
books = make([]Book, len(bookId))
for i, id := range bookId {
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
}
func (d *DB) UpdateDownloadedBooks() error {
var u DBUpdate
u.src = d.session.DB(DB_NAME).C(STATS_COLL)
u.dst = d.session.DB(DB_NAME).C(DOWNLOADED_COLL)
return u.UpdateMostBooks("download")
}
/* 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
booksColl := d.session.DB(DB_NAME).C(BOOKS_COLL)
err := booksColl.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() ([]string, error) {
tagsColl := d.session.DB(DB_NAME).C(TAGS_COLL)
return GetTags(tagsColl)
}
func (d *DB) UpdateTags() error {
var u DBUpdate
u.src = d.session.DB(DB_NAME).C(BOOKS_COLL)
u.dst = d.session.DB(DB_NAME).C(TAGS_COLL)
return u.UpdateTags()
}
type VisitType int
const (
hourly_visits = iota
daily_visits
monthly_visits
hourly_downloads
daily_downloads
monthly_downloads
)
type Visits struct {
Date time.Time "date"
Count int "count"
}
func (d *DB) GetVisits(visitType VisitType) ([]Visits, error) {
var coll *mgo.Collection
switch visitType {
case hourly_visits:
coll = d.session.DB(DB_NAME).C(HOURLY_VISITS_COLL)
case daily_visits:
coll = d.session.DB(DB_NAME).C(DAILY_VISITS_COLL)
case monthly_visits:
coll = d.session.DB(DB_NAME).C(MONTHLY_VISITS_COLL)
case hourly_downloads:
coll = d.session.DB(DB_NAME).C(HOURLY_DOWNLOADS_COLL)
case daily_downloads:
coll = d.session.DB(DB_NAME).C(DAILY_DOWNLOADS_COLL)
case monthly_downloads:
coll = d.session.DB(DB_NAME).C(MONTHLY_DOWNLOADS_COLL)
default:
return nil, errors.New("Not valid VisitType")
}
return GetVisits(coll)
}
func (d *DB) UpdateHourVisits() error {
var u DBUpdate
u.src = d.session.DB(DB_NAME).C(STATS_COLL)
u.dst = d.session.DB(DB_NAME).C(HOURLY_VISITS_COLL)
return u.UpdateHourVisits(false)
}
func (d *DB) UpdateDayVisits() error {
var u DBUpdate
u.src = d.session.DB(DB_NAME).C(STATS_COLL)
u.dst = d.session.DB(DB_NAME).C(DAILY_VISITS_COLL)
return u.UpdateDayVisits(false)
}
func (d *DB) UpdateMonthVisits() error {
var u DBUpdate
u.src = d.session.DB(DB_NAME).C(STATS_COLL)
u.dst = d.session.DB(DB_NAME).C(MONTHLY_VISITS_COLL)
return u.UpdateMonthVisits(false)
}
func (d *DB) UpdateHourDownloads() error {
var u DBUpdate
u.src = d.session.DB(DB_NAME).C(STATS_COLL)
u.dst = d.session.DB(DB_NAME).C(HOURLY_DOWNLOADS_COLL)
return u.UpdateHourVisits(true)
}
func (d *DB) UpdateDayDownloads() error {
var u DBUpdate
u.src = d.session.DB(DB_NAME).C(STATS_COLL)
u.dst = d.session.DB(DB_NAME).C(DAILY_DOWNLOADS_COLL)
return u.UpdateDayVisits(true)
}
func (d *DB) UpdateMonthDownloads() error {
var u DBUpdate
u.src = d.session.DB(DB_NAME).C(STATS_COLL)
u.dst = d.session.DB(DB_NAME).C(MONTHLY_DOWNLOADS_COLL)
return u.UpdateMonthVisits(true)
}

77
database/books.go Normal file
View file

@ -0,0 +1,77 @@
package database
import (
"labix.org/v2/mgo"
"labix.org/v2/mgo/bson"
)
const (
books_coll = "books"
)
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
}
func addBook(coll *mgo.Collection, book interface{}) error {
return coll.Insert(book)
}
func getBooks(coll *mgo.Collection, query bson.M, length int, start int) (books []Book, num int, err error) {
q := coll.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
}
func deleteBook(coll *mgo.Collection, id bson.ObjectId) error {
return coll.Remove(bson.M{"_id": id})
}
func updateBook(coll *mgo.Collection, id bson.ObjectId, data interface{}) error {
return coll.Update(bson.M{"_id": id}, bson.M{"$set": data})
}
func bookActive(coll *mgo.Collection, id bson.ObjectId) bool {
var book Book
err := coll.Find(bson.M{"_id": id}).One(&book)
if err != nil {
return false
}
return book.Active
}

40
database/books_test.go Normal file
View file

@ -0,0 +1,40 @@
package database
import "testing"
import (
"labix.org/v2/mgo/bson"
)
var book = map[string]interface{}{
"title": "some title",
"author": []string{"Alice", "Bob"},
}
func TestAddBook(t *testing.T) {
db := Init(test_host, test_coll)
defer db.del()
tAddBook(t, db)
books, num, err := db.GetBooks(bson.M{}, 1, 0)
if err != nil {
t.Fatalf("db.GetBooks() return an error: ", err)
}
if num < 1 {
t.Fatalf("db.GetBooks() didn't find any result.")
}
if len(books) < 1 {
t.Fatalf("db.GetBooks() didn't return any result.")
}
if books[0].Title != book["title"] {
t.Errorf("Book title don't match : '", books[0].Title, "' <=> '", book["title"], "'")
}
}
func tAddBook(t *testing.T, db *DB) {
err := db.AddBook(book)
if err != nil {
t.Errorf("db.AddBook(", book, ") return an error: ", err)
}
}

236
database/database.go Normal file
View file

@ -0,0 +1,236 @@
package database
import log "github.com/cihub/seelog"
import (
"errors"
"labix.org/v2/mgo"
"labix.org/v2/mgo/bson"
"os"
)
const (
visited_coll = "visited"
downloaded_coll = "downloaded"
tags_coll = "tags"
)
type DB struct {
session *mgo.Session
name string
}
func Init(host string, name string) *DB {
var err error
db := new(DB)
db.session, err = mgo.Dial(host)
if err != nil {
log.Critical(err)
os.Exit(1)
}
db.name = name
return db
}
func (db *DB) Close() {
db.session.Close()
}
func (db *DB) Copy() *DB {
dbCopy := new(DB)
dbCopy.session = db.session.Copy()
dbCopy.name = db.name
return dbCopy
}
func (db *DB) AddBook(book interface{}) error {
booksColl := db.session.DB(db.name).C(books_coll)
return addBook(booksColl, book)
}
// FIXME: don't export bson data
func (db *DB) GetBooks(query bson.M, length int, start int) (books []Book, num int, err error) {
booksColl := db.session.DB(db.name).C(books_coll)
return getBooks(booksColl, query, length, start)
}
func (db *DB) DeleteBook(id bson.ObjectId) error {
booksColl := db.session.DB(db.name).C(books_coll)
return deleteBook(booksColl, id)
}
func (db *DB) UpdateBook(id bson.ObjectId, data interface{}) error {
booksColl := db.session.DB(db.name).C(books_coll)
return updateBook(booksColl, id, data)
}
func (db *DB) GetNewBooks(length int, start int) (books []Book, num int, err error) {
booksColl := db.session.DB(db.name).C(books_coll)
return getBooks(booksColl, bson.M{"$nor": []bson.M{{"active": true}}}, length, start)
}
func (db *DB) BookActive(id bson.ObjectId) bool {
booksColl := db.session.DB(db.name).C(books_coll)
return bookActive(booksColl, id)
}
func (db *DB) User(name string) *User {
userColl := db.session.DB(db.name).C(user_coll)
return getUser(userColl, name)
}
func (db *DB) AddUser(name string, pass string) error {
userColl := db.session.DB(db.name).C(user_coll)
return addUser(userColl, name, pass)
}
func (db *DB) AddNews(text string) error {
newsColl := db.session.DB(db.name).C(news_coll)
return addNews(newsColl, text)
}
func (db *DB) GetNews(num int, days int) (news []News, err error) {
newsColl := db.session.DB(db.name).C(news_coll)
return getNews(newsColl, num, days)
}
// TODO: split code in files
func (db *DB) AddStats(stats interface{}) error {
statsColl := db.session.DB(db.name).C(stats_coll)
return statsColl.Insert(stats)
}
/* Get the most visited books
*/
func (db *DB) GetVisitedBooks() (books []Book, err error) {
visitedColl := db.session.DB(db.name).C(visited_coll)
bookId, err := GetBooksVisited(visitedColl)
if err != nil {
return nil, err
}
books = make([]Book, len(bookId))
for i, id := range bookId {
booksColl := db.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
}
func (db *DB) UpdateMostVisited() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(visited_coll)
return u.UpdateMostBooks("book")
}
/* Get the most downloaded books
*/
func (db *DB) GetDownloadedBooks() (books []Book, err error) {
downloadedColl := db.session.DB(db.name).C(downloaded_coll)
bookId, err := GetBooksVisited(downloadedColl)
if err != nil {
return nil, err
}
books = make([]Book, len(bookId))
for i, id := range bookId {
booksColl := db.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
}
func (db *DB) UpdateDownloadedBooks() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(downloaded_coll)
return u.UpdateMostBooks("download")
}
func (db *DB) GetFS(prefix string) *mgo.GridFS {
return db.session.DB(db.name).GridFS(prefix)
}
func (db *DB) GetTags() ([]string, error) {
tagsColl := db.session.DB(db.name).C(tags_coll)
return GetTags(tagsColl)
}
func (db *DB) UpdateTags() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(books_coll)
u.dst = db.session.DB(db.name).C(tags_coll)
return u.UpdateTags()
}
func (db *DB) GetVisits(visitType VisitType) ([]Visits, error) {
var coll *mgo.Collection
switch visitType {
case Hourly_visits:
coll = db.session.DB(db.name).C(hourly_visits_coll)
case Daily_visits:
coll = db.session.DB(db.name).C(daily_visits_coll)
case Monthly_visits:
coll = db.session.DB(db.name).C(monthly_visits_coll)
case Hourly_downloads:
coll = db.session.DB(db.name).C(hourly_downloads_coll)
case Daily_downloads:
coll = db.session.DB(db.name).C(daily_downloads_coll)
case Monthly_downloads:
coll = db.session.DB(db.name).C(monthly_downloads_coll)
default:
return nil, errors.New("Not valid VisitType")
}
return GetVisits(coll)
}
func (db *DB) UpdateHourVisits() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(hourly_visits_coll)
return u.UpdateHourVisits(false)
}
func (db *DB) UpdateDayVisits() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(daily_visits_coll)
return u.UpdateDayVisits(false)
}
func (db *DB) UpdateMonthVisits() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(monthly_visits_coll)
return u.UpdateMonthVisits(false)
}
func (db *DB) UpdateHourDownloads() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(hourly_downloads_coll)
return u.UpdateHourVisits(true)
}
func (db *DB) UpdateDayDownloads() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(daily_downloads_coll)
return u.UpdateDayVisits(true)
}
func (db *DB) UpdateMonthDownloads() error {
var u dbUpdate
u.src = db.session.DB(db.name).C(stats_coll)
u.dst = db.session.DB(db.name).C(monthly_downloads_coll)
return u.UpdateMonthVisits(true)
}
// function defined for the tests
func (db *DB) del() {
defer db.Close()
db.session.DB(db.name).DropDatabase()
}

40
database/database_test.go Normal file
View file

@ -0,0 +1,40 @@
package database
import "testing"
const (
test_coll = "test_trantor"
test_host = "127.0.0.1"
)
func TestInit(t *testing.T) {
db := Init(test_host, test_coll)
defer db.Close()
}
func TestCopy(t *testing.T) {
db := Init(test_host, test_coll)
defer db.del()
db2 := db.Copy()
if db.name != db2.name {
t.Errorf("Names don't match")
}
names1, err := db.session.DatabaseNames()
if err != nil {
t.Errorf("Error on db1: ", err)
}
names2, err := db2.session.DatabaseNames()
if err != nil {
t.Errorf("Error on db1: ", err)
}
if len(names1) != len(names2) {
t.Errorf("len(names) don't match")
}
for i, _ := range names1 {
if names1[i] != names2[i] {
t.Errorf("Names don't match")
}
}
}

35
database/news.go Normal file
View file

@ -0,0 +1,35 @@
package database
import (
"labix.org/v2/mgo"
"labix.org/v2/mgo/bson"
"time"
)
const (
news_coll = "news"
)
type News struct {
Date time.Time
Text string
}
func addNews(coll *mgo.Collection, text string) error {
var news News
news.Text = text
news.Date = time.Now()
return coll.Insert(news)
}
func getNews(coll *mgo.Collection, 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 := coll.Find(query).Sort("-date").Limit(num)
err = q.All(&news)
return
}

26
database/news_test.go Normal file
View file

@ -0,0 +1,26 @@
package database
import "testing"
func TestNews(t *testing.T) {
const text = "Some news text"
db := Init(test_host, test_coll)
defer db.del()
err := db.AddNews(text)
if err != nil {
t.Errorf("db.News(", text, ") return an error: ", err)
}
news, err := db.GetNews(1, 1)
if err != nil {
t.Fatalf("db.GetNews() return an error: ", err)
}
if len(news) < 1 {
t.Fatalf("No news found.")
}
if news[0].Text != text {
t.Errorf("News text don't match : '", news[0].Text, "' <=> '", text, "'")
}
}

View file

@ -1,4 +1,4 @@
package main package database
import ( import (
"labix.org/v2/mgo" "labix.org/v2/mgo"
@ -6,11 +6,41 @@ import (
"time" "time"
) )
type DBUpdate struct { const (
stats_coll = "statistics"
hourly_visits_coll = "visits.hourly"
daily_visits_coll = "visits.daily"
monthly_visits_coll = "visits.monthly"
hourly_downloads_coll = "downloads.hourly"
daily_downloads_coll = "downloads.daily"
monthly_downloads_coll = "downloads.monthly"
// FIXME: this should return to the config.go
TAGS_DISPLAY = 50
BOOKS_FRONT_PAGE = 6
)
type dbUpdate struct {
src *mgo.Collection src *mgo.Collection
dst *mgo.Collection dst *mgo.Collection
} }
type VisitType int
const (
Hourly_visits = iota
Daily_visits
Monthly_visits
Hourly_downloads
Daily_downloads
Monthly_downloads
)
type Visits struct {
Date time.Time "date"
Count int "count"
}
func GetTags(tagsColl *mgo.Collection) ([]string, error) { func GetTags(tagsColl *mgo.Collection) ([]string, error) {
var result []struct { var result []struct {
Tag string "_id" Tag string "_id"
@ -49,7 +79,7 @@ func GetVisits(visitsColl *mgo.Collection) ([]Visits, error) {
return result, err return result, err
} }
func (u *DBUpdate) UpdateTags() error { func (u *dbUpdate) UpdateTags() error {
var tags []struct { var tags []struct {
Tag string "_id" Tag string "_id"
Count int "count" Count int "count"
@ -75,7 +105,7 @@ func (u *DBUpdate) UpdateTags() error {
return nil return nil
} }
func (u *DBUpdate) UpdateMostBooks(section string) error { func (u *dbUpdate) UpdateMostBooks(section string) error {
const numDays = 30 const numDays = 30
start := time.Now().UTC().Add(-numDays * 24 * time.Hour) start := time.Now().UTC().Add(-numDays * 24 * time.Hour)
@ -104,19 +134,19 @@ func (u *DBUpdate) UpdateMostBooks(section string) error {
return nil return nil
} }
func (u *DBUpdate) UpdateHourVisits(isDownloads bool) error { func (u *dbUpdate) UpdateHourVisits(isDownloads bool) error {
const numDays = 2 const numDays = 2
spanStore := numDays * 24 * time.Hour spanStore := numDays * 24 * time.Hour
return u.updateVisits(hourInc, spanStore, isDownloads) return u.updateVisits(hourInc, spanStore, isDownloads)
} }
func (u *DBUpdate) UpdateDayVisits(isDownloads bool) error { func (u *dbUpdate) UpdateDayVisits(isDownloads bool) error {
const numDays = 30 const numDays = 30
spanStore := numDays * 24 * time.Hour spanStore := numDays * 24 * time.Hour
return u.updateVisits(dayInc, spanStore, isDownloads) return u.updateVisits(dayInc, spanStore, isDownloads)
} }
func (u *DBUpdate) UpdateMonthVisits(isDownloads bool) error { func (u *dbUpdate) UpdateMonthVisits(isDownloads bool) error {
const numDays = 365 const numDays = 365
spanStore := numDays * 24 * time.Hour spanStore := numDays * 24 * time.Hour
return u.updateVisits(monthInc, spanStore, isDownloads) return u.updateVisits(monthInc, spanStore, isDownloads)
@ -137,7 +167,7 @@ func monthInc(date time.Time) time.Time {
return date.AddDate(0, 1, 1-date.Day()).Truncate(span) return date.AddDate(0, 1, 1-date.Day()).Truncate(span)
} }
func (u *DBUpdate) updateVisits(incTime func(time.Time) time.Time, spanStore time.Duration, isDownloads bool) error { func (u *dbUpdate) updateVisits(incTime func(time.Time) time.Time, spanStore time.Duration, isDownloads bool) error {
start := u.calculateStart(spanStore) start := u.calculateStart(spanStore)
for start.Before(time.Now().UTC()) { for start.Before(time.Now().UTC()) {
stop := incTime(start) stop := incTime(start)
@ -165,7 +195,7 @@ func (u *DBUpdate) updateVisits(incTime func(time.Time) time.Time, spanStore tim
return err return err
} }
func (u *DBUpdate) calculateStart(spanStore time.Duration) time.Time { func (u *dbUpdate) calculateStart(spanStore time.Duration) time.Time {
var date struct { var date struct {
Id bson.ObjectId `bson:"_id"` Id bson.ObjectId `bson:"_id"`
Date time.Time `bson:"date"` Date time.Time `bson:"date"`
@ -178,7 +208,7 @@ func (u *DBUpdate) calculateStart(spanStore time.Duration) time.Time {
return time.Now().UTC().Add(-spanStore).Truncate(time.Hour) return time.Now().UTC().Add(-spanStore).Truncate(time.Hour)
} }
func (u *DBUpdate) countVisits(start time.Time, stop time.Time) int { func (u *dbUpdate) countVisits(start time.Time, stop time.Time) int {
var result struct { var result struct {
Count int "count" Count int "count"
} }
@ -194,7 +224,7 @@ func (u *DBUpdate) countVisits(start time.Time, stop time.Time) int {
return result.Count return result.Count
} }
func (u *DBUpdate) countDownloads(start time.Time, stop time.Time) (int, error) { func (u *dbUpdate) countDownloads(start time.Time, stop time.Time) (int, error) {
query := bson.M{"date": bson.M{"$gte": start, "$lt": stop}, "section": "download"} query := bson.M{"date": bson.M{"$gte": start, "$lt": stop}, "section": "download"}
return u.src.Find(query).Count() return u.src.Find(query).Count()
} }

96
database/users.go Normal file
View file

@ -0,0 +1,96 @@
package database
import log "github.com/cihub/seelog"
import (
"bytes"
"crypto/md5"
"errors"
"labix.org/v2/mgo"
"labix.org/v2/mgo/bson"
)
const (
user_coll = "users"
pass_salt = "ImperialLibSalt"
)
type User struct {
user db_user
err error
coll *mgo.Collection
}
type db_user struct {
User string
Pass []byte
Role string
}
func getUser(coll *mgo.Collection, name string) *User {
u := new(User)
if !validUserName(name) {
u.err = errors.New("Invalid username")
return u
}
u.coll = coll
err := u.coll.Find(bson.M{"user": name}).One(&u.user)
if err != nil {
log.Warn("Error on database checking user ", name, ": ", err)
u.err = errors.New("User not found")
return u
}
return u
}
func addUser(coll *mgo.Collection, name string, pass string) error {
if !validUserName(name) {
return errors.New("Invalid user name")
}
num, err := coll.Find(bson.M{"user": name}).Count()
if err != nil {
log.Error("Error on database checking user ", name, ": ", err)
return errors.New("An error happen on the database")
}
if num != 0 {
return errors.New("User name already exist")
}
var user db_user
user.Pass = md5Pass(pass)
user.User = name
user.Role = ""
return coll.Insert(user)
}
func validUserName(name string) bool {
return name != ""
}
func (u User) Valid(pass string) bool {
if u.err != nil {
return false
}
hash := md5Pass(pass)
return bytes.Compare(u.user.Pass, hash) == 0
}
func (u User) Role() string {
return u.user.Role
}
func (u *User) SetPassword(pass string) error {
if u.err != nil {
return u.err
}
hash := md5Pass(pass)
return u.coll.Update(bson.M{"user": u.user.User}, bson.M{"$set": bson.M{"pass": hash}})
}
// FIXME: use a proper salting algorithm
func md5Pass(pass string) []byte {
h := md5.New()
hash := h.Sum(([]byte)(pass_salt + pass))
return hash
}

43
database/users_test.go Normal file
View file

@ -0,0 +1,43 @@
package database
import "testing"
const (
name, pass = "user", "mypass"
)
func TestUserEmpty(t *testing.T) {
db := Init(test_host, test_coll)
defer db.del()
if db.User("").Valid("") {
t.Errorf("user.Valid() with an empty password return true")
}
}
func TestAddUser(t *testing.T) {
db := Init(test_host, test_coll)
defer db.del()
tAddUser(t, db)
if !db.User(name).Valid(pass) {
t.Errorf("user.Valid() return false for a valid user")
}
}
func TestEmptyUsername(t *testing.T) {
db := Init(test_host, test_coll)
defer db.del()
tAddUser(t, db)
if db.User("").Valid(pass) {
t.Errorf("user.Valid() return true for an invalid user")
}
}
func tAddUser(t *testing.T, db *DB) {
err := db.AddUser(name, pass)
if err != nil {
t.Errorf("db.Adduser(", name, ", ", pass, ") return an error: ", err)
}
}

View file

@ -1,6 +1,7 @@
package main package main
import ( import (
"git.gitorious.org/trantor/trantor.git/database"
"net/http" "net/http"
) )
@ -57,7 +58,7 @@ func postNewsHandler(h handler) {
http.Redirect(h.w, h.r, "/news/", http.StatusFound) http.Redirect(h.w, h.r, "/news/", http.StatusFound)
} }
func getNews(num int, days int, db *DB) []newsEntry { func getNews(num int, days int, db *database.DB) []newsEntry {
dbnews, _ := db.GetNews(num, days) dbnews, _ := db.GetNews(num, days)
news := make([]newsEntry, len(dbnews)) news := make([]newsEntry, len(dbnews))
for i, n := range dbnews { for i, n := range dbnews {

View file

@ -2,6 +2,7 @@ package main
import ( import (
"git.gitorious.org/go-pkg/epubgo.git" "git.gitorious.org/go-pkg/epubgo.git"
"git.gitorious.org/trantor/trantor.git/database"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"io" "io"
"labix.org/v2/mgo/bson" "labix.org/v2/mgo/bson"
@ -21,7 +22,7 @@ type chapter struct {
type readData struct { type readData struct {
S Status S Status
Book Book Book database.Book
Content string Content string
Chapters []chapter Chapters []chapter
Next string Next string
@ -171,13 +172,13 @@ func readHandler(h handler) {
loadTemplate(h.w, "read", data) loadTemplate(h.w, "read", data)
} }
func openReadEpub(h handler) (*epubgo.Epub, Book) { func openReadEpub(h handler) (*epubgo.Epub, database.Book) {
var book Book var book database.Book
id := mux.Vars(h.r)["id"] id := mux.Vars(h.r)["id"]
if !bson.IsObjectIdHex(id) { if !bson.IsObjectIdHex(id) {
return nil, book return nil, book
} }
books, _, err := h.db.GetBooks(bson.M{"_id": bson.ObjectIdHex(id)}) books, _, err := h.db.GetBooks(bson.M{"_id": bson.ObjectIdHex(id)}, 0, 0)
if err != nil || len(books) == 0 { if err != nil || len(books) == 0 {
return nil, book return nil, book
} }
@ -204,7 +205,7 @@ func contentHandler(h handler) {
return return
} }
books, _, err := h.db.GetBooks(bson.M{"_id": bson.ObjectIdHex(id)}) books, _, err := h.db.GetBooks(bson.M{"_id": bson.ObjectIdHex(id)}, 0, 0)
if err != nil || len(books) == 0 { if err != nil || len(books) == 0 {
notFound(h) notFound(h)
return return

View file

@ -1,6 +1,7 @@
package main package main
import ( import (
"git.gitorious.org/trantor/trantor.git/database"
"labix.org/v2/mgo/bson" "labix.org/v2/mgo/bson"
"net/http" "net/http"
"strconv" "strconv"
@ -28,7 +29,7 @@ func buildQuery(q string) bson.M {
type searchData struct { type searchData struct {
S Status S Status
Found int Found int
Books []Book Books []database.Book
ItemsPage int ItemsPage int
Page int Page int
Next string Next string

View file

@ -2,6 +2,7 @@ package main
import ( import (
"encoding/hex" "encoding/hex"
"git.gitorious.org/trantor/trantor.git/database"
"github.com/gorilla/securecookie" "github.com/gorilla/securecookie"
"github.com/gorilla/sessions" "github.com/gorilla/sessions"
"net/http" "net/http"
@ -21,13 +22,13 @@ type Session struct {
S *sessions.Session S *sessions.Session
} }
func GetSession(r *http.Request, db *DB) (s *Session) { func GetSession(r *http.Request, db *database.DB) (s *Session) {
s = new(Session) s = new(Session)
var err error var err error
s.S, err = sesStore.Get(r, "session") s.S, err = sesStore.Get(r, "session")
if err == nil && !s.S.IsNew { if err == nil && !s.S.IsNew {
s.User, _ = s.S.Values["user"].(string) s.User, _ = s.S.Values["user"].(string)
s.Role = db.UserRole(s.User) s.Role = db.User(s.User).Role()
} }
if s.S.IsNew { if s.S.IsNew {

View file

@ -3,6 +3,7 @@ package main
import log "github.com/cihub/seelog" import log "github.com/cihub/seelog"
import ( import (
"git.gitorious.org/trantor/trantor.git/database"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"labix.org/v2/mgo/bson" "labix.org/v2/mgo/bson"
"net/http" "net/http"
@ -15,15 +16,15 @@ type handler struct {
w http.ResponseWriter w http.ResponseWriter
r *http.Request r *http.Request
sess *Session sess *Session
db *DB db *database.DB
} }
func InitStats(database *DB) { func InitStats(database *database.DB) {
statsChannel = make(chan statsRequest, CHAN_SIZE) statsChannel = make(chan statsRequest, CHAN_SIZE)
go statsWorker(database) go statsWorker(database)
} }
func GatherStats(function func(handler), database *DB) func(http.ResponseWriter, *http.Request) { func GatherStats(function func(handler), database *database.DB) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
log.Info("Query ", r.Method, " ", r.RequestURI) log.Info("Query ", r.Method, " ", r.RequestURI)
@ -49,7 +50,7 @@ type statsRequest struct {
r *http.Request r *http.Request
} }
func statsWorker(database *DB) { func statsWorker(database *database.DB) {
db := database.Copy() db := database.Copy()
defer db.Close() defer db.Close()
@ -61,7 +62,7 @@ func statsWorker(database *DB) {
appendSession(req.sess, stats) appendSession(req.sess, stats)
stats["method"] = req.r.Method stats["method"] = req.r.Method
stats["date"] = req.date stats["date"] = req.date
db.InsertStats(stats) db.AddStats(stats)
} }
} }
@ -69,12 +70,12 @@ func statsHandler(h handler) {
var data statsData var data statsData
data.S = GetStatus(h) data.S = GetStatus(h)
data.S.Stats = true data.S.Stats = true
data.HVisits = getVisits(hourlyLabel, h.db, hourly_visits) data.HVisits = getVisits(hourlyLabel, h.db, database.Hourly_visits)
data.DVisits = getVisits(dailyLabel, h.db, daily_visits) data.DVisits = getVisits(dailyLabel, h.db, database.Daily_visits)
data.MVisits = getVisits(monthlyLabel, h.db, monthly_visits) data.MVisits = getVisits(monthlyLabel, h.db, database.Monthly_visits)
data.HDownloads = getVisits(hourlyLabel, h.db, hourly_downloads) data.HDownloads = getVisits(hourlyLabel, h.db, database.Hourly_downloads)
data.DDownloads = getVisits(dailyLabel, h.db, daily_downloads) data.DDownloads = getVisits(dailyLabel, h.db, database.Daily_downloads)
data.MDownloads = getVisits(monthlyLabel, h.db, monthly_downloads) data.MDownloads = getVisits(monthlyLabel, h.db, database.Monthly_downloads)
loadTemplate(h.w, "stats", data) loadTemplate(h.w, "stats", data)
} }
@ -106,7 +107,7 @@ func monthlyLabel(date time.Time) string {
return date.Month().String() return date.Month().String()
} }
func getVisits(funcLabel func(time.Time) string, db *DB, visitType VisitType) []visitData { func getVisits(funcLabel func(time.Time) string, db *database.DB, visitType database.VisitType) []visitData {
var visits []visitData var visits []visitData
visit, err := db.GetVisits(visitType) visit, err := db.GetVisits(visitType)

View file

@ -3,6 +3,7 @@ package main
import ( import (
"bytes" "bytes"
"git.gitorious.org/go-pkg/epubgo.git" "git.gitorious.org/go-pkg/epubgo.git"
"git.gitorious.org/trantor/trantor.git/database"
"io" "io"
"io/ioutil" "io/ioutil"
"labix.org/v2/mgo/bson" "labix.org/v2/mgo/bson"
@ -10,7 +11,7 @@ import (
"strings" "strings"
) )
func OpenBook(id bson.ObjectId, db *DB) (*epubgo.Epub, error) { func OpenBook(id bson.ObjectId, db *database.DB) (*epubgo.Epub, error) {
fs := db.GetFS(FS_BOOKS) fs := db.GetFS(FS_BOOKS)
f, err := fs.OpenId(id) f, err := fs.OpenId(id)
if err != nil { if err != nil {
@ -24,7 +25,7 @@ func OpenBook(id bson.ObjectId, db *DB) (*epubgo.Epub, error) {
return epubgo.Load(reader, int64(len(buff))) return epubgo.Load(reader, int64(len(buff)))
} }
func StoreNewFile(name string, file io.Reader, db *DB) (bson.ObjectId, int64, error) { func StoreNewFile(name string, file io.Reader, db *database.DB) (bson.ObjectId, int64, error) {
fs := db.GetFS(FS_BOOKS) fs := db.GetFS(FS_BOOKS)
fw, err := fs.Create(name) fw, err := fs.Create(name)
if err != nil { if err != nil {
@ -37,17 +38,17 @@ func StoreNewFile(name string, file io.Reader, db *DB) (bson.ObjectId, int64, er
return id, size, err return id, size, err
} }
func DeleteFile(id bson.ObjectId, db *DB) error { func DeleteFile(id bson.ObjectId, db *database.DB) error {
fs := db.GetFS(FS_BOOKS) fs := db.GetFS(FS_BOOKS)
return fs.RemoveId(id) return fs.RemoveId(id)
} }
func DeleteCover(id bson.ObjectId, db *DB) error { func DeleteCover(id bson.ObjectId, db *database.DB) error {
fs := db.GetFS(FS_IMGS) fs := db.GetFS(FS_IMGS)
return fs.RemoveId(id) return fs.RemoveId(id)
} }
func DeleteBook(book Book, db *DB) { func DeleteBook(book database.Book, db *database.DB) {
if book.Cover != "" { if book.Cover != "" {
DeleteCover(book.Cover, db) DeleteCover(book.Cover, db)
} }

View file

@ -3,10 +3,11 @@ package main
import log "github.com/cihub/seelog" import log "github.com/cihub/seelog"
import ( import (
"git.gitorious.org/trantor/trantor.git/database"
"time" "time"
) )
func InitTasks(db *DB) { func InitTasks(db *database.DB) {
periodicTask(updateLogger, MINUTES_UPDATE_LOGGER*time.Minute) periodicTask(updateLogger, MINUTES_UPDATE_LOGGER*time.Minute)
periodicTask(db.UpdateTags, MINUTES_UPDATE_TAGS*time.Minute) periodicTask(db.UpdateTags, MINUTES_UPDATE_TAGS*time.Minute)
periodicTask(db.UpdateMostVisited, MINUTES_UPDATE_VISITED*time.Minute) periodicTask(db.UpdateMostVisited, MINUTES_UPDATE_VISITED*time.Minute)

View file

@ -3,6 +3,7 @@ package main
import log "github.com/cihub/seelog" import log "github.com/cihub/seelog"
import ( import (
"git.gitorious.org/trantor/trantor.git/database"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"io" "io"
"labix.org/v2/mgo/bson" "labix.org/v2/mgo/bson"
@ -38,7 +39,7 @@ func logoutHandler(h handler) {
type bookData struct { type bookData struct {
S Status S Status
Book Book Book database.Book
Description []string Description []string
} }
@ -52,7 +53,7 @@ func bookHandler(h handler) {
var data bookData var data bookData
data.S = GetStatus(h) data.S = GetStatus(h)
id := bson.ObjectIdHex(idStr) id := bson.ObjectIdHex(idStr)
books, _, err := h.db.GetBooks(bson.M{"_id": id}) books, _, err := h.db.GetBooks(bson.M{"_id": id}, 0, 0)
if err != nil || len(books) == 0 { if err != nil || len(books) == 0 {
notFound(h) notFound(h)
return return
@ -70,7 +71,7 @@ func downloadHandler(h handler) {
} }
id := bson.ObjectIdHex(idStr) id := bson.ObjectIdHex(idStr)
books, _, err := h.db.GetBooks(bson.M{"_id": id}) books, _, err := h.db.GetBooks(bson.M{"_id": id}, 0, 0)
if err != nil || len(books) == 0 { if err != nil || len(books) == 0 {
notFound(h) notFound(h)
return return
@ -101,9 +102,9 @@ func downloadHandler(h handler) {
type indexData struct { type indexData struct {
S Status S Status
Books []Book Books []database.Book
VisitedBooks []Book VisitedBooks []database.Book
DownloadedBooks []Book DownloadedBooks []database.Book
Count int Count int
Tags []string Tags []string
News []newsEntry News []newsEntry
@ -115,7 +116,7 @@ func indexHandler(h handler) {
data.Tags, _ = h.db.GetTags() data.Tags, _ = h.db.GetTags()
data.S = GetStatus(h) data.S = GetStatus(h)
data.S.Home = true data.S.Home = true
data.Books, data.Count, _ = h.db.GetBooks(bson.M{"active": true}, BOOKS_FRONT_PAGE) data.Books, data.Count, _ = h.db.GetBooks(bson.M{"active": true}, BOOKS_FRONT_PAGE, 0)
data.VisitedBooks, _ = h.db.GetVisitedBooks() data.VisitedBooks, _ = h.db.GetVisitedBooks()
data.DownloadedBooks, _ = h.db.GetDownloadedBooks() data.DownloadedBooks, _ = h.db.GetDownloadedBooks()
data.News = getNews(1, DAYS_NEWS_INDEXPAGE, h.db) data.News = getNews(1, DAYS_NEWS_INDEXPAGE, h.db)
@ -147,7 +148,7 @@ func main() {
} }
log.Info("Start the imperial library of trantor") log.Info("Start the imperial library of trantor")
db := initDB() db := database.Init(DB_IP, DB_NAME)
defer db.Close() defer db.Close()
InitTasks(db) InitTasks(db)
@ -158,7 +159,7 @@ func main() {
log.Error(http.ListenAndServe(":"+PORT, nil)) log.Error(http.ListenAndServe(":"+PORT, nil))
} }
func initRouter(db *DB) { func initRouter(db *database.DB) {
r := mux.NewRouter() r := mux.NewRouter()
var notFoundHandler http.HandlerFunc var notFoundHandler http.HandlerFunc
notFoundHandler = GatherStats(notFound, db) notFoundHandler = GatherStats(notFound, db)

View file

@ -5,12 +5,13 @@ import log "github.com/cihub/seelog"
import ( import (
"bytes" "bytes"
"git.gitorious.org/go-pkg/epubgo.git" "git.gitorious.org/go-pkg/epubgo.git"
"git.gitorious.org/trantor/trantor.git/database"
"io/ioutil" "io/ioutil"
"mime/multipart" "mime/multipart"
"strings" "strings"
) )
func InitUpload(database *DB) { func InitUpload(database *database.DB) {
uploadChannel = make(chan uploadRequest, CHAN_SIZE) uploadChannel = make(chan uploadRequest, CHAN_SIZE)
go uploadWorker(database) go uploadWorker(database)
} }
@ -22,7 +23,7 @@ type uploadRequest struct {
filename string filename string
} }
func uploadWorker(database *DB) { func uploadWorker(database *database.DB) {
db := database.Copy() db := database.Copy()
defer db.Close() defer db.Close()
@ -31,7 +32,7 @@ func uploadWorker(database *DB) {
} }
} }
func processFile(req uploadRequest, db *DB) { func processFile(req uploadRequest, db *database.DB) {
defer req.file.Close() defer req.file.Close()
epub, err := openMultipartEpub(req.file) epub, err := openMultipartEpub(req.file)
@ -52,7 +53,7 @@ func processFile(req uploadRequest, db *DB) {
book["file"] = id book["file"] = id
book["filesize"] = size book["filesize"] = size
err = db.InsertBook(book) err = db.AddBook(book)
if err != nil { if err != nil {
log.Error("Error storing metadata (", title, "): ", err) log.Error("Error storing metadata (", title, "): ", err)
return return
@ -103,7 +104,7 @@ func openMultipartEpub(file multipart.File) (*epubgo.Epub, error) {
return epubgo.Load(reader, int64(len(buff))) return epubgo.Load(reader, int64(len(buff)))
} }
func parseFile(epub *epubgo.Epub, db *DB) map[string]interface{} { func parseFile(epub *epubgo.Epub, db *database.DB) map[string]interface{} {
book := map[string]interface{}{} book := map[string]interface{}{}
for _, m := range epub.MetadataFields() { for _, m := range epub.MetadataFields() {
data, err := epub.Metadata(m) data, err := epub.Metadata(m)

13
user.go
View file

@ -1,7 +1,8 @@
package main package main
import log "github.com/cihub/seelog"
import ( import (
"log"
"net/http" "net/http"
) )
@ -19,12 +20,12 @@ func loginHandler(h handler) {
func loginPostHandler(h handler) { func loginPostHandler(h handler) {
user := h.r.FormValue("user") user := h.r.FormValue("user")
pass := h.r.FormValue("pass") pass := h.r.FormValue("pass")
if h.db.UserValid(user, pass) { if h.db.User(user).Valid(pass) {
log.Println("User", user, "log in") log.Info("User ", user, " log in")
h.sess.LogIn(user) h.sess.LogIn(user)
h.sess.Notify("Successful login!", "Welcome "+user, "success") h.sess.Notify("Successful login!", "Welcome "+user, "success")
} else { } else {
log.Println("User", user, "bad user or password") log.Warn("User ", user, " bad user or password")
h.sess.Notify("Invalid login!", "user or password invalid", "error") h.sess.Notify("Invalid login!", "user or password invalid", "error")
} }
h.sess.Save(h.w, h.r) h.sess.Save(h.w, h.r)
@ -71,12 +72,12 @@ func settingsHandler(h handler) {
pass1 := h.r.FormValue("password1") pass1 := h.r.FormValue("password1")
pass2 := h.r.FormValue("password2") pass2 := h.r.FormValue("password2")
switch { switch {
case !h.db.UserValid(h.sess.User, current_pass): case !h.db.User(h.sess.User).Valid(current_pass):
h.sess.Notify("Password error!", "The current password given don't match with the user password. Try again", "error") h.sess.Notify("Password error!", "The current password given don't match with the user password. Try again", "error")
case pass1 != pass2: case pass1 != pass2:
h.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: default:
h.db.SetPassword(h.sess.User, pass1) h.db.User(h.sess.User).SetPassword(pass1)
h.sess.Notify("Password updated!", "Your new password is correctly set.", "success") h.sess.Notify("Password updated!", "Your new password is correctly set.", "success")
} }
h.sess.Save(h.w, h.r) h.sess.Save(h.w, h.r)