Refactor database
This commit is contained in:
parent
533f8241c2
commit
59eaa4e2aa
24 changed files with 712 additions and 436 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -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
|
||||||
|
|
15
admin.go
15
admin.go
|
@ -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
|
||||||
|
|
24
config.go
24
config.go
|
@ -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
|
||||||
|
|
13
cover.go
13
cover.go
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
345
database.go
345
database.go
|
@ -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
77
database/books.go
Normal 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
40
database/books_test.go
Normal 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
236
database/database.go
Normal 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
40
database/database_test.go
Normal 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
35
database/news.go
Normal 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
26
database/news_test.go
Normal 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, "'")
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
96
database/users.go
Normal 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
43
database/users_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
3
news.go
3
news.go
|
@ -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 {
|
||||||
|
|
11
reader.go
11
reader.go
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
25
stats.go
25
stats.go
|
@ -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)
|
||||||
|
|
11
store.go
11
store.go
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
19
trantor.go
19
trantor.go
|
@ -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)
|
||||||
|
|
11
upload.go
11
upload.go
|
@ -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
13
user.go
|
@ -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)
|
||||||
|
|
Reference in a new issue