switch to the new router

This commit is contained in:
Nazar Kanaev 2021-03-16 21:49:35 +00:00
parent e53265472f
commit 1a490a8e7a
4 changed files with 196 additions and 260 deletions

21
src/server/forms.go Normal file
View File

@ -0,0 +1,21 @@
package server
import "github.com/nkanaev/yarr/src/storage"
type ItemUpdateForm struct {
Status *storage.ItemStatus `json:"status,omitempty"`
}
type FolderCreateForm struct {
Title string `json:"title"`
}
type FolderUpdateForm struct {
Title *string `json:"title,omitempty"`
IsExpanded *bool `json:"is_expanded,omitempty"`
}
type FeedCreateForm struct {
Url string `json:"url"`
FolderID *int64 `json:"folder_id,omitempty"`
}

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"github.com/nkanaev/yarr/src/storage" "github.com/nkanaev/yarr/src/storage"
"github.com/nkanaev/yarr/src/assets" "github.com/nkanaev/yarr/src/assets"
"github.com/nkanaev/yarr/src/router"
"html" "html"
"io/ioutil" "io/ioutil"
"log" "log"
@ -15,272 +16,257 @@ import (
"strings" "strings"
) )
var routes []Route = []Route{ func (s *Server) handler() http.Handler {
p("/", IndexHandler).ManualAuth(), r := router.NewRouter()
p("/static/*path", StaticHandler).ManualAuth(),
p("/api/status", StatusHandler), // TODO: auth, base, security
p("/api/folders", FolderListHandler),
p("/api/folders/:id", FolderHandler), r.For("/", s.handleIndex)
p("/api/feeds", FeedListHandler), r.For("/static/*path", s.handleStatic)
p("/api/feeds/find", FeedHandler), r.For("/api/status", s.handleStatus)
p("/api/feeds/refresh", FeedRefreshHandler), r.For("/api/folders", s.handleFolderList)
p("/api/feeds/errors", FeedErrorsHandler), r.For("/api/folders/:id", s.handleFolder)
p("/api/feeds/:id/icon", FeedIconHandler), r.For("/api/feeds", s.handleFeedList)
p("/api/feeds/:id", FeedHandler), r.For("/api/feeds/refresh", s.handleFeedRefresh)
p("/api/items", ItemListHandler), r.For("/api/feeds/errors", s.handleFeedErrors)
p("/api/items/:id", ItemHandler), r.For("/api/feeds/:id/icon", s.handleFeedIcon)
p("/api/settings", SettingsHandler), r.For("/api/feeds/:id", s.handleFeed)
p("/opml/import", OPMLImportHandler), r.For("/api/items", s.handleItemList)
p("/opml/export", OPMLExportHandler), r.For("/api/items/:id", s.handleItem)
p("/page", PageCrawlHandler), r.For("/api/settings", s.handleSettings)
p("/logout", LogoutHandler), r.For("/opml/import", s.handleOPMLImport)
r.For("/opml/export", s.handleOPMLExport)
r.For("/page", s.handlePageCrawl)
r.For("/logout", s.handleLogout)
return r
} }
type FolderCreateForm struct { func (s *Server) handleIndex(c *router.Context) {
Title string `json:"title"` if s.requiresAuth() && !userIsAuthenticated(c.Req, s.Username, s.Password) {
} if c.Req.Method == "POST" {
username := c.Req.FormValue("username")
type FolderUpdateForm struct { password := c.Req.FormValue("password")
Title *string `json:"title,omitempty"` if stringsEqual(username, s.Username) && stringsEqual(password, s.Password) {
IsExpanded *bool `json:"is_expanded,omitempty"` userAuthenticate(c.Out, username, password)
} http.Redirect(c.Out, c.Req, c.Req.URL.Path, http.StatusFound)
type FeedCreateForm struct {
Url string `json:"url"`
FolderID *int64 `json:"folder_id,omitempty"`
}
type ItemUpdateForm struct {
Status *storage.ItemStatus `json:"status,omitempty"`
}
func IndexHandler(rw http.ResponseWriter, req *http.Request) {
h := handler(req)
if h.requiresAuth() && !userIsAuthenticated(req, h.Username, h.Password) {
if req.Method == "POST" {
username := req.FormValue("username")
password := req.FormValue("password")
if stringsEqual(username, h.Username) && stringsEqual(password, h.Password) {
userAuthenticate(rw, username, password)
http.Redirect(rw, req, req.URL.Path, http.StatusFound)
return return
} }
} }
rw.Header().Set("Content-Type", "text/html") c.Out.Header().Set("Content-Type", "text/html")
assets.Render("login.html", rw, nil) assets.Render("login.html", c.Out, nil)
return return
} }
rw.Header().Set("Content-Type", "text/html") c.Out.Header().Set("Content-Type", "text/html")
assets.Render("index.html", rw, nil) assets.Render("index.html", c.Out, nil)
} }
func StaticHandler(rw http.ResponseWriter, req *http.Request) { func (s *Server) handleStatic(c *router.Context) {
// TODO: gzip? // TODO: gzip?
http.StripPrefix(BasePath+"/static/", http.FileServer(http.FS(assets.FS))).ServeHTTP(rw, req) http.StripPrefix(BasePath+"/static/", http.FileServer(http.FS(assets.FS))).ServeHTTP(c.Out, c.Req)
} }
func StatusHandler(rw http.ResponseWriter, req *http.Request) { func (s *Server) handleStatus(c *router.Context) {
writeJSON(rw, map[string]interface{}{ writeJSON(c.Out, map[string]interface{}{
"running": *handler(req).queueSize, "running": *s.queueSize,
"stats": db(req).FeedStats(), "stats": s.db.FeedStats(),
}) })
} }
func FolderListHandler(rw http.ResponseWriter, req *http.Request) { func (s *Server) handleFolderList(c *router.Context) {
if req.Method == "GET" { if c.Req.Method == "GET" {
list := db(req).ListFolders() list := s.db.ListFolders()
writeJSON(rw, list) writeJSON(c.Out, list)
} else if req.Method == "POST" { } else if c.Req.Method == "POST" {
var body FolderCreateForm var body FolderCreateForm
if err := json.NewDecoder(req.Body).Decode(&body); err != nil { if err := json.NewDecoder(c.Req.Body).Decode(&body); err != nil {
log.Print(err) log.Print(err)
rw.WriteHeader(http.StatusBadRequest) c.Out.WriteHeader(http.StatusBadRequest)
return return
} }
if len(body.Title) == 0 { if len(body.Title) == 0 {
rw.WriteHeader(http.StatusBadRequest) c.Out.WriteHeader(http.StatusBadRequest)
writeJSON(rw, map[string]string{"error": "Folder title missing."}) writeJSON(c.Out, map[string]string{"error": "Folder title missing."})
return return
} }
folder := db(req).CreateFolder(body.Title) folder := s.db.CreateFolder(body.Title)
rw.WriteHeader(http.StatusCreated) c.Out.WriteHeader(http.StatusCreated)
writeJSON(rw, folder) writeJSON(c.Out, folder)
} else { } else {
rw.WriteHeader(http.StatusMethodNotAllowed) c.Out.WriteHeader(http.StatusMethodNotAllowed)
} }
} }
func FolderHandler(rw http.ResponseWriter, req *http.Request) { func (s *Server) handleFolder(c *router.Context) {
id, err := strconv.ParseInt(Vars(req)["id"], 10, 64) id, err := strconv.ParseInt(c.Vars["id"], 10, 64)
if err != nil { if err != nil {
rw.WriteHeader(http.StatusBadRequest) c.Out.WriteHeader(http.StatusBadRequest)
return return
} }
if req.Method == "PUT" { if c.Req.Method == "PUT" {
var body FolderUpdateForm var body FolderUpdateForm
if err := json.NewDecoder(req.Body).Decode(&body); err != nil { if err := json.NewDecoder(c.Req.Body).Decode(&body); err != nil {
log.Print(err) log.Print(err)
rw.WriteHeader(http.StatusBadRequest) c.Out.WriteHeader(http.StatusBadRequest)
return return
} }
if body.Title != nil { if body.Title != nil {
db(req).RenameFolder(id, *body.Title) s.db.RenameFolder(id, *body.Title)
} }
if body.IsExpanded != nil { if body.IsExpanded != nil {
db(req).ToggleFolderExpanded(id, *body.IsExpanded) s.db.ToggleFolderExpanded(id, *body.IsExpanded)
} }
rw.WriteHeader(http.StatusOK) c.Out.WriteHeader(http.StatusOK)
} else if req.Method == "DELETE" { } else if c.Req.Method == "DELETE" {
db(req).DeleteFolder(id) s.db.DeleteFolder(id)
rw.WriteHeader(http.StatusNoContent) c.Out.WriteHeader(http.StatusNoContent)
} }
} }
func FeedRefreshHandler(rw http.ResponseWriter, req *http.Request) { func (s *Server) handleFeedRefresh(c *router.Context) {
if req.Method == "POST" { if c.Req.Method == "POST" {
handler(req).fetchAllFeeds() s.fetchAllFeeds()
rw.WriteHeader(http.StatusOK) c.Out.WriteHeader(http.StatusOK)
} else { } else {
rw.WriteHeader(http.StatusMethodNotAllowed) c.Out.WriteHeader(http.StatusMethodNotAllowed)
} }
} }
func FeedErrorsHandler(rw http.ResponseWriter, req *http.Request) { func (s *Server) handleFeedErrors(c *router.Context) {
errors := db(req).GetFeedErrors() errors := s.db.GetFeedErrors()
writeJSON(rw, errors) writeJSON(c.Out, errors)
} }
func FeedIconHandler(rw http.ResponseWriter, req *http.Request) { func (s *Server) handleFeedIcon(c *router.Context) {
id, err := strconv.ParseInt(Vars(req)["id"], 10, 64) id, err := strconv.ParseInt(c.Vars["id"], 10, 64)
if err != nil { if err != nil {
rw.WriteHeader(http.StatusBadRequest) c.Out.WriteHeader(http.StatusBadRequest)
return return
} }
feed := db(req).GetFeed(id) feed := s.db.GetFeed(id)
if feed != nil && feed.Icon != nil { if feed != nil && feed.Icon != nil {
rw.Header().Set("Content-Type", http.DetectContentType(*feed.Icon)) c.Out.Header().Set("Content-Type", http.DetectContentType(*feed.Icon))
rw.Header().Set("Content-Length", strconv.Itoa(len(*feed.Icon))) c.Out.Header().Set("Content-Length", strconv.Itoa(len(*feed.Icon)))
rw.Write(*feed.Icon) c.Out.Write(*feed.Icon)
} else { } else {
rw.WriteHeader(http.StatusNotFound) c.Out.WriteHeader(http.StatusNotFound)
} }
} }
func FeedListHandler(rw http.ResponseWriter, req *http.Request) { func (s *Server) handleFeedList(c *router.Context) {
if req.Method == "GET" { if c.Req.Method == "GET" {
list := db(req).ListFeeds() list := s.db.ListFeeds()
writeJSON(rw, list) writeJSON(c.Out, list)
} else if req.Method == "POST" { } else if c.Req.Method == "POST" {
var form FeedCreateForm var form FeedCreateForm
if err := json.NewDecoder(req.Body).Decode(&form); err != nil { if err := json.NewDecoder(c.Req.Body).Decode(&form); err != nil {
log.Print(err) log.Print(err)
rw.WriteHeader(http.StatusBadRequest) c.Out.WriteHeader(http.StatusBadRequest)
return return
} }
feed, sources, err := discoverFeed(form.Url) feed, sources, err := discoverFeed(form.Url)
if err != nil { if err != nil {
log.Print(err) log.Print(err)
writeJSON(rw, map[string]string{"status": "notfound"}) writeJSON(c.Out, map[string]string{"status": "notfound"})
return return
} }
if feed != nil { if feed != nil {
storedFeed := db(req).CreateFeed( storedFeed := s.db.CreateFeed(
feed.Title, feed.Title,
feed.Description, feed.Description,
feed.Link, feed.Link,
feed.FeedLink, feed.FeedLink,
form.FolderID, form.FolderID,
) )
db(req).CreateItems(convertItems(feed.Items, *storedFeed)) s.db.CreateItems(convertItems(feed.Items, *storedFeed))
icon, err := findFavicon(storedFeed.Link, storedFeed.FeedLink) icon, err := findFavicon(storedFeed.Link, storedFeed.FeedLink)
if icon != nil { if icon != nil {
db(req).UpdateFeedIcon(storedFeed.Id, icon) s.db.UpdateFeedIcon(storedFeed.Id, icon)
} }
if err != nil { if err != nil {
log.Printf("Failed to find favicon for %s (%d): %s", storedFeed.FeedLink, storedFeed.Id, err) log.Printf("Failed to find favicon for %s (%d): %s", storedFeed.FeedLink, storedFeed.Id, err)
} }
writeJSON(rw, map[string]string{"status": "success"}) writeJSON(c.Out, map[string]string{"status": "success"})
} else if sources != nil { } else if sources != nil {
writeJSON(rw, map[string]interface{}{"status": "multiple", "choice": sources}) writeJSON(c.Out, map[string]interface{}{"status": "multiple", "choice": sources})
} else { } else {
writeJSON(rw, map[string]string{"status": "notfound"}) writeJSON(c.Out, map[string]string{"status": "notfound"})
} }
} }
} }
func FeedHandler(rw http.ResponseWriter, req *http.Request) { func (s *Server) handleFeed(c *router.Context) {
id, err := strconv.ParseInt(Vars(req)["id"], 10, 64) id, err := strconv.ParseInt(c.Vars["id"], 10, 64)
if err != nil { if err != nil {
rw.WriteHeader(http.StatusBadRequest) c.Out.WriteHeader(http.StatusBadRequest)
return return
} }
if req.Method == "PUT" { if c.Req.Method == "PUT" {
feed := db(req).GetFeed(id) feed := s.db.GetFeed(id)
if feed == nil { if feed == nil {
rw.WriteHeader(http.StatusBadRequest) c.Out.WriteHeader(http.StatusBadRequest)
return return
} }
body := make(map[string]interface{}) body := make(map[string]interface{})
if err := json.NewDecoder(req.Body).Decode(&body); err != nil { if err := json.NewDecoder(c.Req.Body).Decode(&body); err != nil {
log.Print(err) log.Print(err)
rw.WriteHeader(http.StatusBadRequest) c.Out.WriteHeader(http.StatusBadRequest)
return return
} }
if title, ok := body["title"]; ok { if title, ok := body["title"]; ok {
if reflect.TypeOf(title).Kind() == reflect.String { if reflect.TypeOf(title).Kind() == reflect.String {
db(req).RenameFeed(id, title.(string)) s.db.RenameFeed(id, title.(string))
} }
} }
if f_id, ok := body["folder_id"]; ok { if f_id, ok := body["folder_id"]; ok {
if f_id == nil { if f_id == nil {
db(req).UpdateFeedFolder(id, nil) s.db.UpdateFeedFolder(id, nil)
} else if reflect.TypeOf(f_id).Kind() == reflect.Float64 { } else if reflect.TypeOf(f_id).Kind() == reflect.Float64 {
folderId := int64(f_id.(float64)) folderId := int64(f_id.(float64))
db(req).UpdateFeedFolder(id, &folderId) s.db.UpdateFeedFolder(id, &folderId)
} }
} }
rw.WriteHeader(http.StatusOK) c.Out.WriteHeader(http.StatusOK)
} else if req.Method == "DELETE" { } else if c.Req.Method == "DELETE" {
db(req).DeleteFeed(id) s.db.DeleteFeed(id)
rw.WriteHeader(http.StatusNoContent) c.Out.WriteHeader(http.StatusNoContent)
} else { } else {
rw.WriteHeader(http.StatusMethodNotAllowed) c.Out.WriteHeader(http.StatusMethodNotAllowed)
} }
} }
func ItemHandler(rw http.ResponseWriter, req *http.Request) { func (s *Server) handleItem(c *router.Context) {
if req.Method == "PUT" { if c.Req.Method == "PUT" {
id, err := strconv.ParseInt(Vars(req)["id"], 10, 64) id, err := strconv.ParseInt(c.Vars["id"], 10, 64)
if err != nil { if err != nil {
rw.WriteHeader(http.StatusBadRequest) c.Out.WriteHeader(http.StatusBadRequest)
return return
} }
var body ItemUpdateForm var body ItemUpdateForm
if err := json.NewDecoder(req.Body).Decode(&body); err != nil { if err := json.NewDecoder(c.Req.Body).Decode(&body); err != nil {
log.Print(err) log.Print(err)
rw.WriteHeader(http.StatusBadRequest) c.Out.WriteHeader(http.StatusBadRequest)
return return
} }
if body.Status != nil { if body.Status != nil {
db(req).UpdateItemStatus(id, *body.Status) s.db.UpdateItemStatus(id, *body.Status)
} }
rw.WriteHeader(http.StatusOK) c.Out.WriteHeader(http.StatusOK)
} else { } else {
rw.WriteHeader(http.StatusMethodNotAllowed) c.Out.WriteHeader(http.StatusMethodNotAllowed)
} }
} }
func ItemListHandler(rw http.ResponseWriter, req *http.Request) { func (s *Server) handleItemList(c *router.Context) {
if req.Method == "GET" { if c.Req.Method == "GET" {
perPage := 20 perPage := 20
curPage := 1 curPage := 1
query := req.URL.Query() query := c.Req.URL.Query()
if page, err := strconv.ParseInt(query.Get("page"), 10, 64); err == nil { if page, err := strconv.ParseInt(query.Get("page"), 10, 64); err == nil {
curPage = int(page) curPage = int(page)
} }
@ -299,17 +285,17 @@ func ItemListHandler(rw http.ResponseWriter, req *http.Request) {
filter.Search = &search filter.Search = &search
} }
newestFirst := query.Get("oldest_first") != "true" newestFirst := query.Get("oldest_first") != "true"
items := db(req).ListItems(filter, (curPage-1)*perPage, perPage, newestFirst) items := s.db.ListItems(filter, (curPage-1)*perPage, perPage, newestFirst)
count := db(req).CountItems(filter) count := s.db.CountItems(filter)
writeJSON(rw, map[string]interface{}{ writeJSON(c.Out, map[string]interface{}{
"page": map[string]int{ "page": map[string]int{
"cur": curPage, "cur": curPage,
"num": int(math.Ceil(float64(count) / float64(perPage))), "num": int(math.Ceil(float64(count) / float64(perPage))),
}, },
"list": items, "list": items,
}) })
} else if req.Method == "PUT" { } else if c.Req.Method == "PUT" {
query := req.URL.Query() query := c.Req.URL.Query()
filter := storage.MarkFilter{} filter := storage.MarkFilter{}
if folderID, err := strconv.ParseInt(query.Get("folder_id"), 10, 64); err == nil { if folderID, err := strconv.ParseInt(query.Get("folder_id"), 10, 64); err == nil {
filter.FolderID = &folderID filter.FolderID = &folderID
@ -317,36 +303,36 @@ func ItemListHandler(rw http.ResponseWriter, req *http.Request) {
if feedID, err := strconv.ParseInt(query.Get("feed_id"), 10, 64); err == nil { if feedID, err := strconv.ParseInt(query.Get("feed_id"), 10, 64); err == nil {
filter.FeedID = &feedID filter.FeedID = &feedID
} }
db(req).MarkItemsRead(filter) s.db.MarkItemsRead(filter)
rw.WriteHeader(http.StatusOK) c.Out.WriteHeader(http.StatusOK)
} else { } else {
rw.WriteHeader(http.StatusMethodNotAllowed) c.Out.WriteHeader(http.StatusMethodNotAllowed)
} }
} }
func SettingsHandler(rw http.ResponseWriter, req *http.Request) { func (s *Server) handleSettings(c *router.Context) {
if req.Method == "GET" { if c.Req.Method == "GET" {
writeJSON(rw, db(req).GetSettings()) writeJSON(c.Out, s.db.GetSettings())
} else if req.Method == "PUT" { } else if c.Req.Method == "PUT" {
settings := make(map[string]interface{}) settings := make(map[string]interface{})
if err := json.NewDecoder(req.Body).Decode(&settings); err != nil { if err := json.NewDecoder(c.Req.Body).Decode(&settings); err != nil {
rw.WriteHeader(http.StatusBadRequest) c.Out.WriteHeader(http.StatusBadRequest)
return return
} }
if db(req).UpdateSettings(settings) { if s.db.UpdateSettings(settings) {
if _, ok := settings["refresh_rate"]; ok { if _, ok := settings["refresh_rate"]; ok {
handler(req).refreshRate <- db(req).GetSettingsValueInt64("refresh_rate") s.refreshRate <- s.db.GetSettingsValueInt64("refresh_rate")
} }
rw.WriteHeader(http.StatusOK) c.Out.WriteHeader(http.StatusOK)
} else { } else {
rw.WriteHeader(http.StatusBadRequest) c.Out.WriteHeader(http.StatusBadRequest)
} }
} }
} }
func OPMLImportHandler(rw http.ResponseWriter, req *http.Request) { func (s *Server) handleOPMLImport(c *router.Context) {
if req.Method == "POST" { if c.Req.Method == "POST" {
file, _, err := req.FormFile("opml") file, _, err := c.Req.FormFile("opml")
if err != nil { if err != nil {
log.Print(err) log.Print(err)
return return
@ -358,25 +344,25 @@ func OPMLImportHandler(rw http.ResponseWriter, req *http.Request) {
} }
for _, outline := range doc.Outlines { for _, outline := range doc.Outlines {
if outline.Type == "rss" { if outline.Type == "rss" {
db(req).CreateFeed(outline.Title, outline.Description, outline.SiteURL, outline.FeedURL, nil) s.db.CreateFeed(outline.Title, outline.Description, outline.SiteURL, outline.FeedURL, nil)
} else { } else {
folder := db(req).CreateFolder(outline.Title) folder := s.db.CreateFolder(outline.Title)
for _, o := range outline.AllFeeds() { for _, o := range outline.AllFeeds() {
db(req).CreateFeed(o.Title, o.Description, o.SiteURL, o.FeedURL, &folder.Id) s.db.CreateFeed(o.Title, o.Description, o.SiteURL, o.FeedURL, &folder.Id)
} }
} }
} }
handler(req).fetchAllFeeds() s.fetchAllFeeds()
rw.WriteHeader(http.StatusOK) c.Out.WriteHeader(http.StatusOK)
} else { } else {
rw.WriteHeader(http.StatusMethodNotAllowed) c.Out.WriteHeader(http.StatusMethodNotAllowed)
} }
} }
func OPMLExportHandler(rw http.ResponseWriter, req *http.Request) { func (s *Server) handleOPMLExport(c *router.Context) {
if req.Method == "GET" { if c.Req.Method == "GET" {
rw.Header().Set("Content-Type", "application/xml; charset=utf-8") c.Out.Header().Set("Content-Type", "application/xml; charset=utf-8")
rw.Header().Set("Content-Disposition", `attachment; filename="subscriptions.opml"`) c.Out.Header().Set("Content-Disposition", `attachment; filename="subscriptions.opml"`)
builder := strings.Builder{} builder := strings.Builder{}
@ -407,7 +393,7 @@ func OPMLExportHandler(rw http.ResponseWriter, req *http.Request) {
line(`</head>`) line(`</head>`)
line(`<body>`) line(`<body>`)
feedsByFolderID := make(map[int64][]storage.Feed) feedsByFolderID := make(map[int64][]storage.Feed)
for _, feed := range db(req).ListFeeds() { for _, feed := range s.db.ListFeeds() {
var folderId = int64(0) var folderId = int64(0)
if feed.FolderId != nil { if feed.FolderId != nil {
folderId = *feed.FolderId folderId = *feed.FolderId
@ -417,7 +403,7 @@ func OPMLExportHandler(rw http.ResponseWriter, req *http.Request) {
} }
feedsByFolderID[folderId] = append(feedsByFolderID[folderId], feed) feedsByFolderID[folderId] = append(feedsByFolderID[folderId], feed)
} }
for _, folder := range db(req).ListFolders() { for _, folder := range s.db.ListFolders() {
line(` <outline text="%s">`, folder.Title) line(` <outline text="%s">`, folder.Title)
for _, feed := range feedsByFolderID[folder.Id] { for _, feed := range feedsByFolderID[folder.Id] {
feedline(feed, 4) feedline(feed, 4)
@ -429,24 +415,24 @@ func OPMLExportHandler(rw http.ResponseWriter, req *http.Request) {
} }
line(`</body>`) line(`</body>`)
line(`</opml>`) line(`</opml>`)
rw.Write([]byte(builder.String())) c.Out.Write([]byte(builder.String()))
} }
} }
func PageCrawlHandler(rw http.ResponseWriter, req *http.Request) { func (s *Server) handlePageCrawl(c *router.Context) {
query := req.URL.Query() query := c.Req.URL.Query()
if url := query.Get("url"); len(url) > 0 { if url := query.Get("url"); len(url) > 0 {
res, err := http.Get(url) res, err := http.Get(url)
if err == nil { if err == nil {
body, err := ioutil.ReadAll(res.Body) body, err := ioutil.ReadAll(res.Body)
if err == nil { if err == nil {
rw.Write(body) c.Out.Write(body)
} }
} }
} }
} }
func LogoutHandler(rw http.ResponseWriter, req *http.Request) { func (s *Server) handleLogout(c *router.Context) {
userLogout(rw) userLogout(c.Out)
rw.WriteHeader(http.StatusNoContent) c.Out.WriteHeader(http.StatusNoContent)
} }

View File

@ -1,50 +0,0 @@
package server
import (
"net/http"
"regexp"
)
var BasePath string = ""
type Route struct {
url string
urlRegex *regexp.Regexp
handler func(http.ResponseWriter, *http.Request)
manualAuth bool
}
func (r Route) ManualAuth() Route {
r.manualAuth = true
return r
}
func p(path string, handler http.HandlerFunc) Route {
var urlRegexp string
urlRegexp = regexp.MustCompile(`[\*\:]\w+`).ReplaceAllStringFunc(path, func(m string) string {
if m[0:1] == `*` {
return "(?P<" + m[1:] + ">.+)"
}
return "(?P<" + m[1:] + ">[^/]+)"
})
urlRegexp = "^" + urlRegexp + "$"
return Route{
url: path,
urlRegex: regexp.MustCompile(urlRegexp),
handler: handler,
}
}
func getRoute(reqPath string) (*Route, map[string]string) {
vars := make(map[string]string)
for _, route := range routes {
if route.urlRegex.MatchString(reqPath) {
matches := route.urlRegex.FindStringSubmatchIndex(reqPath)
for i, key := range route.urlRegex.SubexpNames()[1:] {
vars[key] = reqPath[matches[i*2+2]:matches[i*2+3]]
}
return &route, vars
}
}
return nil, nil
}

View File

@ -1,17 +1,17 @@
package server package server
import ( import (
"context"
"log" "log"
"net/http" "net/http"
"runtime" "runtime"
"strings"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/nkanaev/yarr/src/storage" "github.com/nkanaev/yarr/src/storage"
) )
var BasePath string = ""
type Server struct { type Server struct {
Addr string Addr string
db *storage.Storage db *storage.Storage
@ -45,16 +45,16 @@ func (h *Server) GetAddr() string {
return proto + "://" + h.Addr + BasePath return proto + "://" + h.Addr + BasePath
} }
func (h *Server) Start() { func (s *Server) Start() {
h.startJobs() s.startJobs()
s := &http.Server{Addr: h.Addr, Handler: h} httpserver := &http.Server{Addr: s.Addr, Handler: s.handler()}
var err error var err error
if h.CertFile != "" && h.KeyFile != "" { if s.CertFile != "" && s.KeyFile != "" {
err = s.ListenAndServeTLS(h.CertFile, h.KeyFile) err = httpserver.ListenAndServeTLS(s.CertFile, s.KeyFile)
} else { } else {
err = s.ListenAndServe() err = httpserver.ListenAndServe()
} }
if err != http.ErrServerClosed { if err != http.ErrServerClosed {
log.Fatal(err) log.Fatal(err)
@ -65,6 +65,7 @@ func unsafeMethod(method string) bool {
return method == "POST" || method == "PUT" || method == "DELETE" return method == "POST" || method == "PUT" || method == "DELETE"
} }
/*
func (h Server) ServeHTTP(rw http.ResponseWriter, req *http.Request) { func (h Server) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
reqPath := req.URL.Path reqPath := req.URL.Path
if BasePath != "" { if BasePath != "" {
@ -99,6 +100,7 @@ func (h Server) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
ctx = context.WithValue(ctx, ctxVars, vars) ctx = context.WithValue(ctx, ctxVars, vars)
route.handler(rw, req.WithContext(ctx)) route.handler(rw, req.WithContext(ctx))
} }
*/
func (h *Server) startJobs() { func (h *Server) startJobs() {
delTicker := time.NewTicker(time.Hour * 24) delTicker := time.NewTicker(time.Hour * 24)
@ -200,26 +202,3 @@ func (h *Server) fetchFeed(feed storage.Feed) {
atomic.AddInt32(h.queueSize, 1) atomic.AddInt32(h.queueSize, 1)
h.feedQueue <- feed h.feedQueue <- feed
} }
func Vars(req *http.Request) map[string]string {
if rv := req.Context().Value(ctxVars); rv != nil {
return rv.(map[string]string)
}
return nil
}
func db(req *http.Request) *storage.Storage {
if h := handler(req); h != nil {
return h.db
}
return nil
}
func handler(req *http.Request) *Server {
return req.Context().Value(ctxHandler).(*Server)
}
const (
ctxVars = 2
ctxHandler = 3
)