package trantor import ( log "github.com/cihub/seelog" "fmt" "net/http" "path" "strings" "github.com/gorilla/mux" "gitlab.com/trantor/trantor/lib/database" ) const ( daysNewsIndexpage = 15 cacheMaxAge = 1800 epubFile = "book.epub" ) type statusData struct { S Status } func aboutHandler(h handler) { var data statusData data.S = GetStatus(h) data.S.Title = "About -- " + data.S.Title data.S.About = true h.load("about", data) } func helpHandler(h handler) { var data statusData data.S = GetStatus(h) data.S.Title = "Help -- " + data.S.Title data.S.Help = true h.load("help", data) } func logoutHandler(h handler) { h.sess.LogOut() h.sess.Notify("Log out!", "Bye bye "+h.sess.User, "success") h.sess.Save(h.w, h.r) log.Info("User ", h.sess.User, " log out") http.Redirect(h.w, h.r, "/", http.StatusFound) } type bookData struct { S Status Book database.Book Description []string DownloadCounter int Lists []database.BookList UserLists []string } func bookHandler(h handler) { id := mux.Vars(h.r)["id"] var data bookData data.S = GetStatus(h) book, err := h.db.GetBookID(id) if err != nil { notFound(h) return } data.Book = book author := "" if len(book.Authors) > 0 { author = " by " + book.Authors[0] } data.S.Title = book.Title + author + " -- " + data.S.Title data.DownloadCounter, err = h.db.GetDownloadCounter(id) if err != nil { log.Error("Error getting download counter: ", err) } data.Lists, err = h.db.GetListsByBook(id) if err != nil { log.Error("Error getting lists: ", err) } if h.sess.User != "" { userLists, err := h.db.GetListsByUser(h.sess.User) if err != nil { log.Error("Error getting lists: ", err) } else { data.UserLists = make([]string, len(userLists)) for i, l := range userLists { data.UserLists[i] = l.Title } } } data.Description = strings.Split(data.Book.Description, "\n") h.load("book", data) } func downloadHandler(h handler) { id := mux.Vars(h.r)["id"] book, err := h.db.GetBookID(id) if err != nil { notFound(h) return } f, err := h.store.Get(book.ID, epubFile) if err != nil { notFound(h) return } defer f.Close() addCacheControlHeader(h.w, false) h.w.Header().Set("Content-Type", "application/epub+zip") http.ServeContent(h.w, h.r, bookFileName(book), book.UploadDate, f) } type indexData struct { S Status Books []database.Book VisitedBooks []database.Book DownloadedBooks []database.Book Count int Tags []string News []newsEntry } func indexHandler(h handler) { var data indexData data.S = GetStatus(h) data.S.Home = true data.News = getNews(1, daysNewsIndexpage, h.db) frontPage := h.db.GetFrontPage() data.Tags = frontPage.Tags data.Books = frontPage.Last data.Count = frontPage.Count data.VisitedBooks = frontPage.Visited data.DownloadedBooks = frontPage.Download h.load("index", data) } func notFound(h handler) { var data statusData data.S = GetStatus(h) data.S.Title = "Not found --" + data.S.Title h.w.WriteHeader(http.StatusNotFound) h.load("404", data) } func UpdateLogger(loggerConfig string) error { logger, err := log.LoggerFromConfigAsFile(loggerConfig) if err != nil { return err } return log.ReplaceLogger(logger) } func InitRouter(db database.DB, sg *StatsGatherer, assetsPath string) http.Handler { const idPattern = "[0-9a-zA-Z\\-\\_]{16}" r := mux.NewRouter() var notFoundHandler http.HandlerFunc notFoundHandler = sg.Gather(notFound) r.NotFoundHandler = notFoundHandler r.HandleFunc("/", sg.Gather(indexHandler)) for _, file := range []string{"robots.txt", "description.json", "opensearch.xml", "key.asc"} { path := path.Join(assetsPath, file) serveFunc := func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, path) } r.HandleFunc("/"+file, serveFunc) } for _, folder := range []string{"img", "css", "js"} { r.HandleFunc("/"+folder+"/{"+folder+"}", fileServer(path.Join(assetsPath, folder), "/"+folder+"/")) } r.HandleFunc("/book/{id:"+idPattern+"}", sg.Gather(bookHandler)) r.HandleFunc("/search/", sg.Gather(searchHandler)) r.HandleFunc("/upload/", sg.Gather(uploadHandler)).Methods("GET") r.HandleFunc("/upload/", sg.Gather(uploadPostHandler)).Methods("POST") r.HandleFunc("/submission/", sg.Gather(userSubmissionsHandler)) r.HandleFunc("/submission/{submissionID:"+idPattern+"}", sg.Gather(submissionHandler)) r.HandleFunc("/submission/{submissionID:"+idPattern+"}/comment/{id:"+idPattern+"}", sg.Gather(submissionCommentHandler)).Methods("POST") r.HandleFunc("/submission/{submissionID:"+idPattern+"}/edit/{id:"+idPattern+"}", sg.Gather(editHandler)) r.HandleFunc("/submission/{submissionID:"+idPattern+"}/save/{id:"+idPattern+"}", sg.Gather(saveHandler)).Methods("POST") r.HandleFunc("/submission/{submissionID:"+idPattern+"}/delete/{ids:(?:"+idPattern+"/)+}", sg.Gather(deleteHandler)) r.HandleFunc("/submission/moderate/", sg.Gather(moderateSubmissionsHandler)) r.HandleFunc("/read/{id:"+idPattern+"}", sg.Gather(readStartHandler)) r.HandleFunc("/read/{id:"+idPattern+"}/{file:.*}", sg.Gather(readHandler)) r.HandleFunc("/content/{id:"+idPattern+"}/{file:.*}", sg.Gather(contentHandler)) r.HandleFunc("/about/", sg.Gather(aboutHandler)) r.HandleFunc("/help/", sg.Gather(helpHandler)) r.HandleFunc("/download/{id:"+idPattern+"}/{epub:.*}", sg.Gather(downloadHandler)) r.HandleFunc("/cover/{id:"+idPattern+"}/{size}/{img:.*}", sg.Gather(coverHandler)) r.HandleFunc("/login/", sg.Gather(loginHandler)).Methods("GET") r.HandleFunc("/login/", sg.Gather(loginPostHandler)).Methods("POST") r.HandleFunc("/create_user/", sg.Gather(createUserHandler)).Methods("POST") r.HandleFunc("/logout/", sg.Gather(logoutHandler)) r.HandleFunc("/dashboard/", sg.Gather(dashboardHandler)) r.HandleFunc("/settings/", sg.Gather(settingsHandler)) r.HandleFunc("/list/{listID:"+idPattern+"}", sg.Gather(listHandler)).Methods("GET") r.HandleFunc("/list/{listID:"+idPattern+"}/{action:edit}", sg.Gather(listHandler)).Methods("GET") r.HandleFunc("/list/", sg.Gather(listPostHandler)).Methods("POST") r.HandleFunc("/list/{listID:"+idPattern+"}", sg.Gather(listEditPostHandler)).Methods("POST") r.HandleFunc("/list/{listID:"+idPattern+"}/remove/{bookID:"+idPattern+"}", sg.Gather(listRemoveHandler)) r.HandleFunc("/save/{id:"+idPattern+"}", sg.Gather(saveHandler)).Methods("POST") r.HandleFunc("/edit/{id:"+idPattern+"}", sg.Gather(editHandler)) r.HandleFunc("/store/{ids:(?:"+idPattern+"/)+}", sg.Gather(storeHandler)) r.HandleFunc("/delete/{ids:(?:"+idPattern+"/)+}", sg.Gather(deleteHandler)) r.HandleFunc("/admin/users/", sg.Gather(userAdminHandler)).Methods("GET") r.HandleFunc("/admin/users/", sg.Gather(userAdminPostHandler)).Methods("POST") r.HandleFunc("/admin/users/add/", sg.Gather(addUserHandler)).Methods("POST") r.HandleFunc("/news/", sg.Gather(newsHandler)) r.HandleFunc("/news/edit", sg.Gather(editNewsHandler)).Methods("GET") r.HandleFunc("/news/edit", sg.Gather(postNewsHandler)).Methods("POST") return r } func fileServer(servePath string, prefix string) func(w http.ResponseWriter, r *http.Request) { // FIXME: is there a cleaner way without handler? h := http.FileServer(http.Dir(servePath)) handler := http.StripPrefix(prefix, h) return func(w http.ResponseWriter, r *http.Request) { addCacheControlHeader(w, false) handler.ServeHTTP(w, r) } } func addCacheControlHeader(w http.ResponseWriter, private bool) { if private { w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d, private", cacheMaxAge)) } else { w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d, public", cacheMaxAge)) } }