From 5a69a61d4841a35d7ddcb761a688db8c688314d6 Mon Sep 17 00:00:00 2001 From: Frédéric Guillot Date: Sun, 11 Nov 2018 11:28:29 -0800 Subject: Move UI middlewares and routes to ui package --- ui/about.go | 13 ++-- ui/bookmark_entries.go | 19 +++--- ui/category_create.go | 13 ++-- ui/category_edit.go | 15 ++--- ui/category_entries.go | 21 +++--- ui/category_list.go | 15 ++--- ui/category_remove.go | 11 ++- ui/category_save.go | 19 +++--- ui/category_update.go | 21 +++--- ui/controller.go | 37 ---------- ui/entry_bookmark.go | 25 ++++--- ui/entry_category.go | 25 ++++--- ui/entry_feed.go | 25 ++++--- ui/entry_read.go | 23 +++---- ui/entry_save.go | 9 ++- ui/entry_scraper.go | 7 +- ui/entry_search.go | 25 ++++--- ui/entry_toggle_bookmark.go | 5 +- ui/entry_unread.go | 27 ++++---- ui/entry_update_status.go | 5 +- ui/feed_edit.go | 17 +++-- ui/feed_entries.go | 21 +++--- ui/feed_icon.go | 5 +- ui/feed_list.go | 15 ++--- ui/feed_refresh.go | 16 ++--- ui/feed_remove.go | 7 +- ui/feed_update.go | 21 +++--- ui/handler.go | 24 +++++++ ui/history_entries.go | 19 +++--- ui/history_flush.go | 7 +- ui/integration_pocket.go | 32 +++++---- ui/integration_show.go | 17 +++-- ui/integration_update.go | 17 +++-- ui/login_check.go | 21 +++--- ui/login_show.go | 9 ++- ui/logout.go | 15 ++--- ui/middleware.go | 149 +++++++++++++++++++++++++++++++++++++++++ ui/oauth2_callback.go | 41 ++++++------ ui/oauth2_redirect.go | 11 ++- ui/oauth2_unlink.go | 19 +++--- ui/opml_export.go | 5 +- ui/opml_import.go | 13 ++-- ui/opml_upload.go | 19 +++--- ui/pagination.go | 4 +- ui/proxy.go | 3 +- ui/search_entries.go | 19 +++--- ui/session_list.go | 15 ++--- ui/session_remove.go | 7 +- ui/settings_show.go | 15 ++--- ui/settings_update.go | 21 +++--- ui/static_app_icon.go | 3 +- ui/static_favicon.go | 3 +- ui/static_javascript.go | 3 +- ui/static_manifest.go | 11 ++- ui/static_stylesheet.go | 3 +- ui/subscription_add.go | 15 ++--- ui/subscription_bookmarklet.go | 15 ++--- ui/subscription_choose.go | 19 +++--- ui/subscription_submit.go | 25 ++++--- ui/ui.go | 133 ++++++++++++++++++++++++++++++++++++ ui/unread_entries.go | 19 +++--- ui/unread_mark_all_read.go | 7 +- ui/user_create.go | 13 ++-- ui/user_edit.go | 14 ++-- ui/user_list.go | 15 ++--- ui/user_remove.go | 11 ++- ui/user_save.go | 19 +++--- ui/user_update.go | 21 +++--- 68 files changed, 759 insertions(+), 554 deletions(-) delete mode 100644 ui/controller.go create mode 100644 ui/handler.go create mode 100644 ui/middleware.go create mode 100644 ui/ui.go (limited to 'ui') diff --git a/ui/about.go b/ui/about.go index abc39a0..5bb9f6b 100644 --- a/ui/about.go +++ b/ui/about.go @@ -14,22 +14,21 @@ import ( "miniflux.app/version" ) -// About shows the about page. -func (c *Controller) About(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) showAboutPage(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) view.Set("version", version.Version) view.Set("build_date", version.BuildDate) view.Set("menu", "settings") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) html.OK(w, r, view.Render("about")) } diff --git a/ui/bookmark_entries.go b/ui/bookmark_entries.go index 5e6bb45..f2c36b2 100644 --- a/ui/bookmark_entries.go +++ b/ui/bookmark_entries.go @@ -15,16 +15,15 @@ import ( "miniflux.app/ui/view" ) -// ShowStarredPage renders the page with all starred entries. -func (c *Controller) ShowStarredPage(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) showStarredPage(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } offset := request.QueryIntParam(r, "offset", 0) - builder := c.store.NewEntryQueryBuilder(user.ID) + builder := h.store.NewEntryQueryBuilder(user.ID) builder.WithoutStatus(model.EntryStatusRemoved) builder.WithStarred() builder.WithOrder(model.DefaultSortingOrder) @@ -44,17 +43,17 @@ func (c *Controller) ShowStarredPage(w http.ResponseWriter, r *http.Request) { return } - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) view.Set("total", count) view.Set("entries", entries) - view.Set("pagination", c.getPagination(route.Path(c.router, "starred"), count, offset)) + view.Set("pagination", getPagination(route.Path(h.router, "starred"), count, offset)) view.Set("menu", "starred") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) - view.Set("hasSaveEntry", c.store.HasSaveEntry(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) + view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID)) html.OK(w, r, view.Render("bookmark_entries")) } diff --git a/ui/category_create.go b/ui/category_create.go index bd5039f..d8fe2cf 100644 --- a/ui/category_create.go +++ b/ui/category_create.go @@ -13,20 +13,19 @@ import ( "miniflux.app/ui/view" ) -// CreateCategory shows the form to create a new category. -func (c *Controller) CreateCategory(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) showCreateCategoryPage(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) view.Set("menu", "categories") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) html.OK(w, r, view.Render("create_category")) } diff --git a/ui/category_edit.go b/ui/category_edit.go index e0375be..344cfe4 100644 --- a/ui/category_edit.go +++ b/ui/category_edit.go @@ -14,19 +14,18 @@ import ( "miniflux.app/ui/view" ) -// EditCategory shows the form to modify a category. -func (c *Controller) EditCategory(w http.ResponseWriter, r *http.Request) { - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) +func (h *handler) showEditCategoryPage(w http.ResponseWriter, r *http.Request) { + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) - user, err := c.store.UserByID(request.UserID(r)) + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } categoryID := request.RouteInt64Param(r, "categoryID") - category, err := c.store.Category(request.UserID(r), categoryID) + category, err := h.store.Category(request.UserID(r), categoryID) if err != nil { html.ServerError(w, r, err) return @@ -45,8 +44,8 @@ func (c *Controller) EditCategory(w http.ResponseWriter, r *http.Request) { view.Set("category", category) view.Set("menu", "categories") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) html.OK(w, r, view.Render("edit_category")) } diff --git a/ui/category_entries.go b/ui/category_entries.go index fe5f638..19b17b9 100644 --- a/ui/category_entries.go +++ b/ui/category_entries.go @@ -15,16 +15,15 @@ import ( "miniflux.app/ui/view" ) -// CategoryEntries shows all entries for the given category. -func (c *Controller) CategoryEntries(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) showCategoryEntriesPage(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } categoryID := request.RouteInt64Param(r, "categoryID") - category, err := c.store.Category(request.UserID(r), categoryID) + category, err := h.store.Category(request.UserID(r), categoryID) if err != nil { html.ServerError(w, r, err) return @@ -36,7 +35,7 @@ func (c *Controller) CategoryEntries(w http.ResponseWriter, r *http.Request) { } offset := request.QueryIntParam(r, "offset", 0) - builder := c.store.NewEntryQueryBuilder(user.ID) + builder := h.store.NewEntryQueryBuilder(user.ID) builder.WithCategoryID(category.ID) builder.WithOrder(model.DefaultSortingOrder) builder.WithDirection(user.EntryDirection) @@ -56,17 +55,17 @@ func (c *Controller) CategoryEntries(w http.ResponseWriter, r *http.Request) { return } - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) view.Set("category", category) view.Set("total", count) view.Set("entries", entries) - view.Set("pagination", c.getPagination(route.Path(c.router, "categoryEntries", "categoryID", category.ID), count, offset)) + view.Set("pagination", getPagination(route.Path(h.router, "categoryEntries", "categoryID", category.ID), count, offset)) view.Set("menu", "categories") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) - view.Set("hasSaveEntry", c.store.HasSaveEntry(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) + view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID)) html.OK(w, r, view.Render("category_entries")) } diff --git a/ui/category_list.go b/ui/category_list.go index f4a3651..3137b95 100644 --- a/ui/category_list.go +++ b/ui/category_list.go @@ -13,28 +13,27 @@ import ( "miniflux.app/ui/view" ) -// CategoryList shows the page with all categories. -func (c *Controller) CategoryList(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) showCategoryListPage(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } - categories, err := c.store.CategoriesWithFeedCount(user.ID) + categories, err := h.store.CategoriesWithFeedCount(user.ID) if err != nil { html.ServerError(w, r, err) return } - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) view.Set("categories", categories) view.Set("total", len(categories)) view.Set("menu", "categories") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) html.OK(w, r, view.Render("categories")) } diff --git a/ui/category_remove.go b/ui/category_remove.go index f8a38dc..8e616fc 100644 --- a/ui/category_remove.go +++ b/ui/category_remove.go @@ -12,16 +12,15 @@ import ( "miniflux.app/http/route" ) -// RemoveCategory deletes a category from the database. -func (c *Controller) RemoveCategory(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) removeCategory(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } categoryID := request.RouteInt64Param(r, "categoryID") - category, err := c.store.Category(request.UserID(r), categoryID) + category, err := h.store.Category(request.UserID(r), categoryID) if err != nil { html.ServerError(w, r, err) return @@ -32,10 +31,10 @@ func (c *Controller) RemoveCategory(w http.ResponseWriter, r *http.Request) { return } - if err := c.store.RemoveCategory(user.ID, category.ID); err != nil { + if err := h.store.RemoveCategory(user.ID, category.ID); err != nil { html.ServerError(w, r, err) return } - html.Redirect(w, r, route.Path(c.router, "categories")) + html.Redirect(w, r, route.Path(h.router, "categories")) } diff --git a/ui/category_save.go b/ui/category_save.go index d115d0a..dd71528 100644 --- a/ui/category_save.go +++ b/ui/category_save.go @@ -17,9 +17,8 @@ import ( "miniflux.app/ui/view" ) -// SaveCategory validate and save the new category into the database. -func (c *Controller) SaveCategory(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) saveCategory(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return @@ -27,13 +26,13 @@ func (c *Controller) SaveCategory(w http.ResponseWriter, r *http.Request) { categoryForm := form.NewCategoryForm(r) - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) view.Set("form", categoryForm) view.Set("menu", "categories") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) if err := categoryForm.Validate(); err != nil { view.Set("errorMessage", err.Error()) @@ -41,7 +40,7 @@ func (c *Controller) SaveCategory(w http.ResponseWriter, r *http.Request) { return } - duplicateCategory, err := c.store.CategoryByTitle(user.ID, categoryForm.Title) + duplicateCategory, err := h.store.CategoryByTitle(user.ID, categoryForm.Title) if err != nil { html.ServerError(w, r, err) return @@ -58,12 +57,12 @@ func (c *Controller) SaveCategory(w http.ResponseWriter, r *http.Request) { UserID: user.ID, } - if err = c.store.CreateCategory(&category); err != nil { + if err = h.store.CreateCategory(&category); err != nil { logger.Error("[Controller:CreateCategory] %v", err) view.Set("errorMessage", "error.unable_to_create_category") html.OK(w, r, view.Render("create_category")) return } - html.Redirect(w, r, route.Path(c.router, "categories")) + html.Redirect(w, r, route.Path(h.router, "categories")) } diff --git a/ui/category_update.go b/ui/category_update.go index 6ef5648..1d20648 100644 --- a/ui/category_update.go +++ b/ui/category_update.go @@ -16,16 +16,15 @@ import ( "miniflux.app/ui/view" ) -// UpdateCategory validates and updates a category. -func (c *Controller) UpdateCategory(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) updateCategory(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } categoryID := request.RouteInt64Param(r, "categoryID") - category, err := c.store.Category(request.UserID(r), categoryID) + category, err := h.store.Category(request.UserID(r), categoryID) if err != nil { html.ServerError(w, r, err) return @@ -38,14 +37,14 @@ func (c *Controller) UpdateCategory(w http.ResponseWriter, r *http.Request) { categoryForm := form.NewCategoryForm(r) - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) view.Set("form", categoryForm) view.Set("category", category) view.Set("menu", "categories") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) if err := categoryForm.Validate(); err != nil { view.Set("errorMessage", err.Error()) @@ -53,13 +52,13 @@ func (c *Controller) UpdateCategory(w http.ResponseWriter, r *http.Request) { return } - if c.store.AnotherCategoryExists(user.ID, category.ID, categoryForm.Title) { + if h.store.AnotherCategoryExists(user.ID, category.ID, categoryForm.Title) { view.Set("errorMessage", "error.category_already_exists") html.OK(w, r, view.Render("edit_category")) return } - err = c.store.UpdateCategory(categoryForm.Merge(category)) + err = h.store.UpdateCategory(categoryForm.Merge(category)) if err != nil { logger.Error("[Controller:UpdateCategory] %v", err) view.Set("errorMessage", "error.unable_to_update_category") @@ -67,5 +66,5 @@ func (c *Controller) UpdateCategory(w http.ResponseWriter, r *http.Request) { return } - html.Redirect(w, r, route.Path(c.router, "categories")) + html.Redirect(w, r, route.Path(h.router, "categories")) } diff --git a/ui/controller.go b/ui/controller.go deleted file mode 100644 index b08c253..0000000 --- a/ui/controller.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2017 Frédéric Guillot. All rights reserved. -// Use of this source code is governed by the Apache 2.0 -// license that can be found in the LICENSE file. - -package ui // import "miniflux.app/ui" - -import ( - "miniflux.app/config" - "miniflux.app/reader/feed" - "miniflux.app/scheduler" - "miniflux.app/storage" - "miniflux.app/template" - - "github.com/gorilla/mux" -) - -// Controller contains all HTTP handlers for the user interface. -type Controller struct { - cfg *config.Config - store *storage.Storage - pool *scheduler.WorkerPool - feedHandler *feed.Handler - tpl *template.Engine - router *mux.Router -} - -// NewController returns a new Controller. -func NewController(cfg *config.Config, store *storage.Storage, pool *scheduler.WorkerPool, feedHandler *feed.Handler, tpl *template.Engine, router *mux.Router) *Controller { - return &Controller{ - cfg: cfg, - store: store, - pool: pool, - feedHandler: feedHandler, - tpl: tpl, - router: router, - } -} diff --git a/ui/entry_bookmark.go b/ui/entry_bookmark.go index 895c2bc..a07859d 100644 --- a/ui/entry_bookmark.go +++ b/ui/entry_bookmark.go @@ -16,16 +16,15 @@ import ( "miniflux.app/ui/view" ) -// ShowStarredEntry shows a single feed entry in "starred" mode. -func (c *Controller) ShowStarredEntry(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) showStarredEntryPage(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } entryID := request.RouteInt64Param(r, "entryID") - builder := c.store.NewEntryQueryBuilder(user.ID) + builder := h.store.NewEntryQueryBuilder(user.ID) builder.WithEntryID(entryID) builder.WithoutStatus(model.EntryStatusRemoved) @@ -41,7 +40,7 @@ func (c *Controller) ShowStarredEntry(w http.ResponseWriter, r *http.Request) { } if entry.Status == model.EntryStatusUnread { - err = c.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead) + err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead) if err != nil { html.ServerError(w, r, err) return @@ -50,7 +49,7 @@ func (c *Controller) ShowStarredEntry(w http.ResponseWriter, r *http.Request) { entry.Status = model.EntryStatusRead } - entryPaginationBuilder := storage.NewEntryPaginationBuilder(c.store, user.ID, entry.ID, user.EntryDirection) + entryPaginationBuilder := storage.NewEntryPaginationBuilder(h.store, user.ID, entry.ID, user.EntryDirection) entryPaginationBuilder.WithStarred() prevEntry, nextEntry, err := entryPaginationBuilder.Entries() if err != nil { @@ -60,16 +59,16 @@ func (c *Controller) ShowStarredEntry(w http.ResponseWriter, r *http.Request) { nextEntryRoute := "" if nextEntry != nil { - nextEntryRoute = route.Path(c.router, "starredEntry", "entryID", nextEntry.ID) + nextEntryRoute = route.Path(h.router, "starredEntry", "entryID", nextEntry.ID) } prevEntryRoute := "" if prevEntry != nil { - prevEntryRoute = route.Path(c.router, "starredEntry", "entryID", prevEntry.ID) + prevEntryRoute = route.Path(h.router, "starredEntry", "entryID", prevEntry.ID) } - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) view.Set("entry", entry) view.Set("prevEntry", prevEntry) view.Set("nextEntry", nextEntry) @@ -77,9 +76,9 @@ func (c *Controller) ShowStarredEntry(w http.ResponseWriter, r *http.Request) { view.Set("prevEntryRoute", prevEntryRoute) view.Set("menu", "starred") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) - view.Set("hasSaveEntry", c.store.HasSaveEntry(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) + view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID)) html.OK(w, r, view.Render("entry")) } diff --git a/ui/entry_category.go b/ui/entry_category.go index d11248b..c260257 100644 --- a/ui/entry_category.go +++ b/ui/entry_category.go @@ -16,9 +16,8 @@ import ( "miniflux.app/ui/view" ) -// ShowCategoryEntry shows a single feed entry in "category" mode. -func (c *Controller) ShowCategoryEntry(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) showCategoryEntryPage(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return @@ -27,7 +26,7 @@ func (c *Controller) ShowCategoryEntry(w http.ResponseWriter, r *http.Request) { categoryID := request.RouteInt64Param(r, "categoryID") entryID := request.RouteInt64Param(r, "entryID") - builder := c.store.NewEntryQueryBuilder(user.ID) + builder := h.store.NewEntryQueryBuilder(user.ID) builder.WithCategoryID(categoryID) builder.WithEntryID(entryID) builder.WithoutStatus(model.EntryStatusRemoved) @@ -44,7 +43,7 @@ func (c *Controller) ShowCategoryEntry(w http.ResponseWriter, r *http.Request) { } if entry.Status == model.EntryStatusUnread { - err = c.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead) + err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead) if err != nil { html.ServerError(w, r, err) return @@ -53,7 +52,7 @@ func (c *Controller) ShowCategoryEntry(w http.ResponseWriter, r *http.Request) { entry.Status = model.EntryStatusRead } - entryPaginationBuilder := storage.NewEntryPaginationBuilder(c.store, user.ID, entry.ID, user.EntryDirection) + entryPaginationBuilder := storage.NewEntryPaginationBuilder(h.store, user.ID, entry.ID, user.EntryDirection) entryPaginationBuilder.WithCategoryID(categoryID) prevEntry, nextEntry, err := entryPaginationBuilder.Entries() if err != nil { @@ -63,16 +62,16 @@ func (c *Controller) ShowCategoryEntry(w http.ResponseWriter, r *http.Request) { nextEntryRoute := "" if nextEntry != nil { - nextEntryRoute = route.Path(c.router, "categoryEntry", "categoryID", categoryID, "entryID", nextEntry.ID) + nextEntryRoute = route.Path(h.router, "categoryEntry", "categoryID", categoryID, "entryID", nextEntry.ID) } prevEntryRoute := "" if prevEntry != nil { - prevEntryRoute = route.Path(c.router, "categoryEntry", "categoryID", categoryID, "entryID", prevEntry.ID) + prevEntryRoute = route.Path(h.router, "categoryEntry", "categoryID", categoryID, "entryID", prevEntry.ID) } - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) view.Set("entry", entry) view.Set("prevEntry", prevEntry) view.Set("nextEntry", nextEntry) @@ -80,9 +79,9 @@ func (c *Controller) ShowCategoryEntry(w http.ResponseWriter, r *http.Request) { view.Set("prevEntryRoute", prevEntryRoute) view.Set("menu", "categories") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) - view.Set("hasSaveEntry", c.store.HasSaveEntry(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) + view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID)) html.OK(w, r, view.Render("entry")) } diff --git a/ui/entry_feed.go b/ui/entry_feed.go index 9c28c67..e4ea585 100644 --- a/ui/entry_feed.go +++ b/ui/entry_feed.go @@ -16,9 +16,8 @@ import ( "miniflux.app/ui/view" ) -// ShowFeedEntry shows a single feed entry in "feed" mode. -func (c *Controller) ShowFeedEntry(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) showFeedEntryPage(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return @@ -27,7 +26,7 @@ func (c *Controller) ShowFeedEntry(w http.ResponseWriter, r *http.Request) { entryID := request.RouteInt64Param(r, "entryID") feedID := request.RouteInt64Param(r, "feedID") - builder := c.store.NewEntryQueryBuilder(user.ID) + builder := h.store.NewEntryQueryBuilder(user.ID) builder.WithFeedID(feedID) builder.WithEntryID(entryID) builder.WithoutStatus(model.EntryStatusRemoved) @@ -44,7 +43,7 @@ func (c *Controller) ShowFeedEntry(w http.ResponseWriter, r *http.Request) { } if entry.Status == model.EntryStatusUnread { - err = c.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead) + err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead) if err != nil { html.ServerError(w, r, err) return @@ -53,7 +52,7 @@ func (c *Controller) ShowFeedEntry(w http.ResponseWriter, r *http.Request) { entry.Status = model.EntryStatusRead } - entryPaginationBuilder := storage.NewEntryPaginationBuilder(c.store, user.ID, entry.ID, user.EntryDirection) + entryPaginationBuilder := storage.NewEntryPaginationBuilder(h.store, user.ID, entry.ID, user.EntryDirection) entryPaginationBuilder.WithFeedID(feedID) prevEntry, nextEntry, err := entryPaginationBuilder.Entries() if err != nil { @@ -63,16 +62,16 @@ func (c *Controller) ShowFeedEntry(w http.ResponseWriter, r *http.Request) { nextEntryRoute := "" if nextEntry != nil { - nextEntryRoute = route.Path(c.router, "feedEntry", "feedID", feedID, "entryID", nextEntry.ID) + nextEntryRoute = route.Path(h.router, "feedEntry", "feedID", feedID, "entryID", nextEntry.ID) } prevEntryRoute := "" if prevEntry != nil { - prevEntryRoute = route.Path(c.router, "feedEntry", "feedID", feedID, "entryID", prevEntry.ID) + prevEntryRoute = route.Path(h.router, "feedEntry", "feedID", feedID, "entryID", prevEntry.ID) } - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) view.Set("entry", entry) view.Set("prevEntry", prevEntry) view.Set("nextEntry", nextEntry) @@ -80,9 +79,9 @@ func (c *Controller) ShowFeedEntry(w http.ResponseWriter, r *http.Request) { view.Set("prevEntryRoute", prevEntryRoute) view.Set("menu", "feeds") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) - view.Set("hasSaveEntry", c.store.HasSaveEntry(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) + view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID)) html.OK(w, r, view.Render("entry")) } diff --git a/ui/entry_read.go b/ui/entry_read.go index 208ae3b..df79d44 100644 --- a/ui/entry_read.go +++ b/ui/entry_read.go @@ -16,16 +16,15 @@ import ( "miniflux.app/ui/view" ) -// ShowReadEntry shows a single feed entry in "history" mode. -func (c *Controller) ShowReadEntry(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) showReadEntryPage(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } entryID := request.RouteInt64Param(r, "entryID") - builder := c.store.NewEntryQueryBuilder(user.ID) + builder := h.store.NewEntryQueryBuilder(user.ID) builder.WithEntryID(entryID) builder.WithoutStatus(model.EntryStatusRemoved) @@ -40,7 +39,7 @@ func (c *Controller) ShowReadEntry(w http.ResponseWriter, r *http.Request) { return } - entryPaginationBuilder := storage.NewEntryPaginationBuilder(c.store, user.ID, entry.ID, user.EntryDirection) + entryPaginationBuilder := storage.NewEntryPaginationBuilder(h.store, user.ID, entry.ID, user.EntryDirection) entryPaginationBuilder.WithStatus(model.EntryStatusRead) prevEntry, nextEntry, err := entryPaginationBuilder.Entries() if err != nil { @@ -50,16 +49,16 @@ func (c *Controller) ShowReadEntry(w http.ResponseWriter, r *http.Request) { nextEntryRoute := "" if nextEntry != nil { - nextEntryRoute = route.Path(c.router, "readEntry", "entryID", nextEntry.ID) + nextEntryRoute = route.Path(h.router, "readEntry", "entryID", nextEntry.ID) } prevEntryRoute := "" if prevEntry != nil { - prevEntryRoute = route.Path(c.router, "readEntry", "entryID", prevEntry.ID) + prevEntryRoute = route.Path(h.router, "readEntry", "entryID", prevEntry.ID) } - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) view.Set("entry", entry) view.Set("prevEntry", prevEntry) view.Set("nextEntry", nextEntry) @@ -67,9 +66,9 @@ func (c *Controller) ShowReadEntry(w http.ResponseWriter, r *http.Request) { view.Set("prevEntryRoute", prevEntryRoute) view.Set("menu", "history") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) - view.Set("hasSaveEntry", c.store.HasSaveEntry(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) + view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID)) html.OK(w, r, view.Render("entry")) } diff --git a/ui/entry_save.go b/ui/entry_save.go index 93910c0..86d5b4d 100644 --- a/ui/entry_save.go +++ b/ui/entry_save.go @@ -13,10 +13,9 @@ import ( "miniflux.app/model" ) -// SaveEntry send the link to external services. -func (c *Controller) SaveEntry(w http.ResponseWriter, r *http.Request) { +func (h *handler) saveEntry(w http.ResponseWriter, r *http.Request) { entryID := request.RouteInt64Param(r, "entryID") - builder := c.store.NewEntryQueryBuilder(request.UserID(r)) + builder := h.store.NewEntryQueryBuilder(request.UserID(r)) builder.WithEntryID(entryID) builder.WithoutStatus(model.EntryStatusRemoved) @@ -31,14 +30,14 @@ func (c *Controller) SaveEntry(w http.ResponseWriter, r *http.Request) { return } - settings, err := c.store.Integration(request.UserID(r)) + settings, err := h.store.Integration(request.UserID(r)) if err != nil { json.ServerError(w, r, err) return } go func() { - integration.SendEntry(c.cfg, entry, settings) + integration.SendEntry(h.cfg, entry, settings) }() json.Created(w, r, map[string]string{"message": "saved"}) diff --git a/ui/entry_scraper.go b/ui/entry_scraper.go index c35d949..519bfd2 100644 --- a/ui/entry_scraper.go +++ b/ui/entry_scraper.go @@ -14,10 +14,9 @@ import ( "miniflux.app/reader/scraper" ) -// FetchContent downloads the original HTML page and returns relevant contents. -func (c *Controller) FetchContent(w http.ResponseWriter, r *http.Request) { +func (h *handler) fetchContent(w http.ResponseWriter, r *http.Request) { entryID := request.RouteInt64Param(r, "entryID") - builder := c.store.NewEntryQueryBuilder(request.UserID(r)) + builder := h.store.NewEntryQueryBuilder(request.UserID(r)) builder.WithEntryID(entryID) builder.WithoutStatus(model.EntryStatusRemoved) @@ -39,7 +38,7 @@ func (c *Controller) FetchContent(w http.ResponseWriter, r *http.Request) { } entry.Content = sanitizer.Sanitize(entry.URL, content) - c.store.UpdateEntryContent(entry) + h.store.UpdateEntryContent(entry) json.OK(w, r, map[string]string{"content": entry.Content}) } diff --git a/ui/entry_search.go b/ui/entry_search.go index d083b35..252b306 100644 --- a/ui/entry_search.go +++ b/ui/entry_search.go @@ -17,9 +17,8 @@ import ( "miniflux.app/ui/view" ) -// ShowSearchEntry shows a single entry in "search" mode. -func (c *Controller) ShowSearchEntry(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) showSearchEntryPage(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return @@ -27,7 +26,7 @@ func (c *Controller) ShowSearchEntry(w http.ResponseWriter, r *http.Request) { entryID := request.RouteInt64Param(r, "entryID") searchQuery := request.QueryStringParam(r, "q", "") - builder := c.store.NewEntryQueryBuilder(user.ID) + builder := h.store.NewEntryQueryBuilder(user.ID) builder.WithSearchQuery(searchQuery) builder.WithEntryID(entryID) builder.WithoutStatus(model.EntryStatusRemoved) @@ -44,7 +43,7 @@ func (c *Controller) ShowSearchEntry(w http.ResponseWriter, r *http.Request) { } if entry.Status == model.EntryStatusUnread { - err = c.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead) + err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead) if err != nil { logger.Error("[Controller:ShowSearchEntry] %v", err) html.ServerError(w, r, err) @@ -54,7 +53,7 @@ func (c *Controller) ShowSearchEntry(w http.ResponseWriter, r *http.Request) { entry.Status = model.EntryStatusRead } - entryPaginationBuilder := storage.NewEntryPaginationBuilder(c.store, user.ID, entry.ID, user.EntryDirection) + entryPaginationBuilder := storage.NewEntryPaginationBuilder(h.store, user.ID, entry.ID, user.EntryDirection) entryPaginationBuilder.WithSearchQuery(searchQuery) prevEntry, nextEntry, err := entryPaginationBuilder.Entries() if err != nil { @@ -64,16 +63,16 @@ func (c *Controller) ShowSearchEntry(w http.ResponseWriter, r *http.Request) { nextEntryRoute := "" if nextEntry != nil { - nextEntryRoute = route.Path(c.router, "searchEntry", "entryID", nextEntry.ID) + nextEntryRoute = route.Path(h.router, "searchEntry", "entryID", nextEntry.ID) } prevEntryRoute := "" if prevEntry != nil { - prevEntryRoute = route.Path(c.router, "searchEntry", "entryID", prevEntry.ID) + prevEntryRoute = route.Path(h.router, "searchEntry", "entryID", prevEntry.ID) } - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) view.Set("searchQuery", searchQuery) view.Set("entry", entry) view.Set("prevEntry", prevEntry) @@ -82,9 +81,9 @@ func (c *Controller) ShowSearchEntry(w http.ResponseWriter, r *http.Request) { view.Set("prevEntryRoute", prevEntryRoute) view.Set("menu", "search") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) - view.Set("hasSaveEntry", c.store.HasSaveEntry(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) + view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID)) html.OK(w, r, view.Render("entry")) } diff --git a/ui/entry_toggle_bookmark.go b/ui/entry_toggle_bookmark.go index 9f7a3e7..3e638a1 100644 --- a/ui/entry_toggle_bookmark.go +++ b/ui/entry_toggle_bookmark.go @@ -11,10 +11,9 @@ import ( "miniflux.app/http/response/json" ) -// ToggleBookmark handles Ajax request to toggle bookmark value. -func (c *Controller) ToggleBookmark(w http.ResponseWriter, r *http.Request) { +func (h *handler) toggleBookmark(w http.ResponseWriter, r *http.Request) { entryID := request.RouteInt64Param(r, "entryID") - if err := c.store.ToggleBookmark(request.UserID(r), entryID); err != nil { + if err := h.store.ToggleBookmark(request.UserID(r), entryID); err != nil { json.ServerError(w, r, err) return } diff --git a/ui/entry_unread.go b/ui/entry_unread.go index 37e9592..fb8d45c 100644 --- a/ui/entry_unread.go +++ b/ui/entry_unread.go @@ -16,16 +16,15 @@ import ( "miniflux.app/ui/view" ) -// ShowUnreadEntry shows a single feed entry in "unread" mode. -func (c *Controller) ShowUnreadEntry(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) showUnreadEntryPage(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } entryID := request.RouteInt64Param(r, "entryID") - builder := c.store.NewEntryQueryBuilder(user.ID) + builder := h.store.NewEntryQueryBuilder(user.ID) builder.WithEntryID(entryID) builder.WithoutStatus(model.EntryStatusRemoved) @@ -42,14 +41,14 @@ func (c *Controller) ShowUnreadEntry(w http.ResponseWriter, r *http.Request) { // Make sure we always get the pagination in unread mode even if the page is refreshed. if entry.Status == model.EntryStatusRead { - err = c.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusUnread) + err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusUnread) if err != nil { html.ServerError(w, r, err) return } } - entryPaginationBuilder := storage.NewEntryPaginationBuilder(c.store, user.ID, entry.ID, user.EntryDirection) + entryPaginationBuilder := storage.NewEntryPaginationBuilder(h.store, user.ID, entry.ID, user.EntryDirection) entryPaginationBuilder.WithStatus(model.EntryStatusUnread) prevEntry, nextEntry, err := entryPaginationBuilder.Entries() if err != nil { @@ -59,24 +58,24 @@ func (c *Controller) ShowUnreadEntry(w http.ResponseWriter, r *http.Request) { nextEntryRoute := "" if nextEntry != nil { - nextEntryRoute = route.Path(c.router, "unreadEntry", "entryID", nextEntry.ID) + nextEntryRoute = route.Path(h.router, "unreadEntry", "entryID", nextEntry.ID) } prevEntryRoute := "" if prevEntry != nil { - prevEntryRoute = route.Path(c.router, "unreadEntry", "entryID", prevEntry.ID) + prevEntryRoute = route.Path(h.router, "unreadEntry", "entryID", prevEntry.ID) } // Always mark the entry as read after fetching the pagination. - err = c.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead) + err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead) if err != nil { html.ServerError(w, r, err) return } entry.Status = model.EntryStatusRead - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) view.Set("entry", entry) view.Set("prevEntry", prevEntry) view.Set("nextEntry", nextEntry) @@ -84,11 +83,11 @@ func (c *Controller) ShowUnreadEntry(w http.ResponseWriter, r *http.Request) { view.Set("prevEntryRoute", prevEntryRoute) view.Set("menu", "unread") view.Set("user", user) - view.Set("hasSaveEntry", c.store.HasSaveEntry(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) // Fetching the counter here avoid to be off by one. - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) html.OK(w, r, view.Render("entry")) } diff --git a/ui/entry_update_status.go b/ui/entry_update_status.go index 6c5cb12..ac1c284 100644 --- a/ui/entry_update_status.go +++ b/ui/entry_update_status.go @@ -12,8 +12,7 @@ import ( "miniflux.app/http/response/json" ) -// UpdateEntriesStatus updates the status for a list of entries. -func (c *Controller) UpdateEntriesStatus(w http.ResponseWriter, r *http.Request) { +func (h *handler) updateEntriesStatus(w http.ResponseWriter, r *http.Request) { entryIDs, status, err := decodeEntryStatusPayload(r.Body) if err != nil { json.BadRequest(w, r, err) @@ -25,7 +24,7 @@ func (c *Controller) UpdateEntriesStatus(w http.ResponseWriter, r *http.Request) return } - err = c.store.SetEntriesStatus(request.UserID(r), entryIDs, status) + err = h.store.SetEntriesStatus(request.UserID(r), entryIDs, status) if err != nil { json.ServerError(w, r, err) return diff --git a/ui/feed_edit.go b/ui/feed_edit.go index 330817b..a195613 100644 --- a/ui/feed_edit.go +++ b/ui/feed_edit.go @@ -15,16 +15,15 @@ import ( "miniflux.app/ui/view" ) -// EditFeed shows the form to modify a subscription. -func (c *Controller) EditFeed(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) showEditFeedPage(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } feedID := request.RouteInt64Param(r, "feedID") - feed, err := c.store.FeedByID(user.ID, feedID) + feed, err := h.store.FeedByID(user.ID, feedID) if err != nil { html.ServerError(w, r, err) return @@ -35,7 +34,7 @@ func (c *Controller) EditFeed(w http.ResponseWriter, r *http.Request) { return } - categories, err := c.store.Categories(user.ID) + categories, err := h.store.Categories(user.ID) if err != nil { html.ServerError(w, r, err) return @@ -54,15 +53,15 @@ func (c *Controller) EditFeed(w http.ResponseWriter, r *http.Request) { Password: feed.Password, } - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) view.Set("form", feedForm) view.Set("categories", categories) view.Set("feed", feed) view.Set("menu", "feeds") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) view.Set("defaultUserAgent", client.DefaultUserAgent) html.OK(w, r, view.Render("edit_feed")) diff --git a/ui/feed_entries.go b/ui/feed_entries.go index 685b5c1..46fec9a 100644 --- a/ui/feed_entries.go +++ b/ui/feed_entries.go @@ -15,16 +15,15 @@ import ( "miniflux.app/ui/view" ) -// ShowFeedEntries shows all entries for the given feed. -func (c *Controller) ShowFeedEntries(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) showFeedEntriesPage(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } feedID := request.RouteInt64Param(r, "feedID") - feed, err := c.store.FeedByID(user.ID, feedID) + feed, err := h.store.FeedByID(user.ID, feedID) if err != nil { html.ServerError(w, r, err) return @@ -36,7 +35,7 @@ func (c *Controller) ShowFeedEntries(w http.ResponseWriter, r *http.Request) { } offset := request.QueryIntParam(r, "offset", 0) - builder := c.store.NewEntryQueryBuilder(user.ID) + builder := h.store.NewEntryQueryBuilder(user.ID) builder.WithFeedID(feed.ID) builder.WithoutStatus(model.EntryStatusRemoved) builder.WithOrder(model.DefaultSortingOrder) @@ -56,17 +55,17 @@ func (c *Controller) ShowFeedEntries(w http.ResponseWriter, r *http.Request) { return } - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) view.Set("feed", feed) view.Set("entries", entries) view.Set("total", count) - view.Set("pagination", c.getPagination(route.Path(c.router, "feedEntries", "feedID", feed.ID), count, offset)) + view.Set("pagination", getPagination(route.Path(h.router, "feedEntries", "feedID", feed.ID), count, offset)) view.Set("menu", "feeds") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) - view.Set("hasSaveEntry", c.store.HasSaveEntry(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) + view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID)) html.OK(w, r, view.Render("feed_entries")) } diff --git a/ui/feed_icon.go b/ui/feed_icon.go index 63aa050..4b5f30f 100644 --- a/ui/feed_icon.go +++ b/ui/feed_icon.go @@ -13,10 +13,9 @@ import ( "miniflux.app/http/response/html" ) -// ShowIcon shows the feed icon. -func (c *Controller) ShowIcon(w http.ResponseWriter, r *http.Request) { +func (h *handler) showIcon(w http.ResponseWriter, r *http.Request) { iconID := request.RouteInt64Param(r, "iconID") - icon, err := c.store.IconByID(iconID) + icon, err := h.store.IconByID(iconID) if err != nil { html.ServerError(w, r, err) return diff --git a/ui/feed_list.go b/ui/feed_list.go index ac5a97c..0c2c546 100644 --- a/ui/feed_list.go +++ b/ui/feed_list.go @@ -13,28 +13,27 @@ import ( "miniflux.app/ui/view" ) -// ShowFeedsPage shows the page with all subscriptions. -func (c *Controller) ShowFeedsPage(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) showFeedsPage(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } - feeds, err := c.store.Feeds(user.ID) + feeds, err := h.store.Feeds(user.ID) if err != nil { html.ServerError(w, r, err) return } - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) view.Set("feeds", feeds) view.Set("total", len(feeds)) view.Set("menu", "feeds") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) html.OK(w, r, view.Render("feeds")) } diff --git a/ui/feed_refresh.go b/ui/feed_refresh.go index 0da4eb8..1b9dc3c 100644 --- a/ui/feed_refresh.go +++ b/ui/feed_refresh.go @@ -13,28 +13,26 @@ import ( "miniflux.app/logger" ) -// RefreshFeed refresh a subscription and redirect to the feed entries page. -func (c *Controller) RefreshFeed(w http.ResponseWriter, r *http.Request) { +func (h *handler) refreshFeed(w http.ResponseWriter, r *http.Request) { feedID := request.RouteInt64Param(r, "feedID") - if err := c.feedHandler.RefreshFeed(request.UserID(r), feedID); err != nil { + if err := h.feedHandler.RefreshFeed(request.UserID(r), feedID); err != nil { logger.Error("[Controller:RefreshFeed] %v", err) } - html.Redirect(w, r, route.Path(c.router, "feedEntries", "feedID", feedID)) + html.Redirect(w, r, route.Path(h.router, "feedEntries", "feedID", feedID)) } -// RefreshAllFeeds refresh all feeds in the background for the current user. -func (c *Controller) RefreshAllFeeds(w http.ResponseWriter, r *http.Request) { +func (h *handler) refreshAllFeeds(w http.ResponseWriter, r *http.Request) { userID := request.UserID(r) - jobs, err := c.store.NewUserBatch(userID, c.store.CountFeeds(userID)) + jobs, err := h.store.NewUserBatch(userID, h.store.CountFeeds(userID)) if err != nil { html.ServerError(w, r, err) return } go func() { - c.pool.Push(jobs) + h.pool.Push(jobs) }() - html.Redirect(w, r, route.Path(c.router, "feeds")) + html.Redirect(w, r, route.Path(h.router, "feeds")) } diff --git a/ui/feed_remove.go b/ui/feed_remove.go index 41fc5b0..c70d77a 100644 --- a/ui/feed_remove.go +++ b/ui/feed_remove.go @@ -12,13 +12,12 @@ import ( "miniflux.app/http/route" ) -// RemoveFeed deletes a subscription from the database and redirect to the list of feeds page. -func (c *Controller) RemoveFeed(w http.ResponseWriter, r *http.Request) { +func (h *handler) removeFeed(w http.ResponseWriter, r *http.Request) { feedID := request.RouteInt64Param(r, "feedID") - if err := c.store.RemoveFeed(request.UserID(r), feedID); err != nil { + if err := h.store.RemoveFeed(request.UserID(r), feedID); err != nil { html.ServerError(w, r, err) return } - html.Redirect(w, r, route.Path(c.router, "feeds")) + html.Redirect(w, r, route.Path(h.router, "feeds")) } diff --git a/ui/feed_update.go b/ui/feed_update.go index 66b6d40..936fd1f 100644 --- a/ui/feed_update.go +++ b/ui/feed_update.go @@ -17,16 +17,15 @@ import ( "miniflux.app/ui/view" ) -// UpdateFeed update a subscription and redirect to the feed entries page. -func (c *Controller) UpdateFeed(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) updateFeed(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } feedID := request.RouteInt64Param(r, "feedID") - feed, err := c.store.FeedByID(user.ID, feedID) + feed, err := h.store.FeedByID(user.ID, feedID) if err != nil { html.ServerError(w, r, err) return @@ -37,7 +36,7 @@ func (c *Controller) UpdateFeed(w http.ResponseWriter, r *http.Request) { return } - categories, err := c.store.Categories(user.ID) + categories, err := h.store.Categories(user.ID) if err != nil { html.ServerError(w, r, err) return @@ -45,15 +44,15 @@ func (c *Controller) UpdateFeed(w http.ResponseWriter, r *http.Request) { feedForm := form.NewFeedForm(r) - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) view.Set("form", feedForm) view.Set("categories", categories) view.Set("feed", feed) view.Set("menu", "feeds") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) view.Set("defaultUserAgent", client.DefaultUserAgent) if err := feedForm.ValidateModification(); err != nil { @@ -62,7 +61,7 @@ func (c *Controller) UpdateFeed(w http.ResponseWriter, r *http.Request) { return } - err = c.store.UpdateFeed(feedForm.Merge(feed)) + err = h.store.UpdateFeed(feedForm.Merge(feed)) if err != nil { logger.Error("[Controller:EditFeed] %v", err) view.Set("errorMessage", "error.unable_to_update_feed") @@ -70,5 +69,5 @@ func (c *Controller) UpdateFeed(w http.ResponseWriter, r *http.Request) { return } - html.Redirect(w, r, route.Path(c.router, "feedEntries", "feedID", feed.ID)) + html.Redirect(w, r, route.Path(h.router, "feedEntries", "feedID", feed.ID)) } diff --git a/ui/handler.go b/ui/handler.go new file mode 100644 index 0000000..39fe560 --- /dev/null +++ b/ui/handler.go @@ -0,0 +1,24 @@ +// Copyright 2017 Frédéric Guillot. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package ui // import "miniflux.app/ui" + +import ( + "miniflux.app/config" + "miniflux.app/reader/feed" + "miniflux.app/scheduler" + "miniflux.app/storage" + "miniflux.app/template" + + "github.com/gorilla/mux" +) + +type handler struct { + router *mux.Router + cfg *config.Config + store *storage.Storage + tpl *template.Engine + pool *scheduler.WorkerPool + feedHandler *feed.Handler +} diff --git a/ui/history_entries.go b/ui/history_entries.go index 8449240..131879c 100644 --- a/ui/history_entries.go +++ b/ui/history_entries.go @@ -15,16 +15,15 @@ import ( "miniflux.app/ui/view" ) -// ShowHistoryPage renders the page with all read entries. -func (c *Controller) ShowHistoryPage(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) showHistoryPage(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } offset := request.QueryIntParam(r, "offset", 0) - builder := c.store.NewEntryQueryBuilder(user.ID) + builder := h.store.NewEntryQueryBuilder(user.ID) builder.WithStatus(model.EntryStatusRead) builder.WithOrder(model.DefaultSortingOrder) builder.WithDirection(user.EntryDirection) @@ -43,16 +42,16 @@ func (c *Controller) ShowHistoryPage(w http.ResponseWriter, r *http.Request) { return } - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) view.Set("entries", entries) view.Set("total", count) - view.Set("pagination", c.getPagination(route.Path(c.router, "history"), count, offset)) + view.Set("pagination", getPagination(route.Path(h.router, "history"), count, offset)) view.Set("menu", "history") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) - view.Set("hasSaveEntry", c.store.HasSaveEntry(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) + view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID)) html.OK(w, r, view.Render("history_entries")) } diff --git a/ui/history_flush.go b/ui/history_flush.go index 6c6907b..d0b8f62 100644 --- a/ui/history_flush.go +++ b/ui/history_flush.go @@ -12,13 +12,12 @@ import ( "miniflux.app/http/route" ) -// FlushHistory changes all "read" items to "removed". -func (c *Controller) FlushHistory(w http.ResponseWriter, r *http.Request) { - err := c.store.FlushHistory(request.UserID(r)) +func (h *handler) flushHistory(w http.ResponseWriter, r *http.Request) { + err := h.store.FlushHistory(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } - html.Redirect(w, r, route.Path(c.router, "history")) + html.Redirect(w, r, route.Path(h.router, "history")) } diff --git a/ui/integration_pocket.go b/ui/integration_pocket.go index 47a975c..8f8c680 100644 --- a/ui/integration_pocket.go +++ b/ui/integration_pocket.go @@ -16,29 +16,28 @@ import ( "miniflux.app/ui/session" ) -// PocketAuthorize redirects the end-user to Pocket website to authorize the application. -func (c *Controller) PocketAuthorize(w http.ResponseWriter, r *http.Request) { +func (h *handler) pocketAuthorize(w http.ResponseWriter, r *http.Request) { printer := locale.NewPrinter(request.UserLanguage(r)) - user, err := c.store.UserByID(request.UserID(r)) + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } - integration, err := c.store.Integration(user.ID) + integration, err := h.store.Integration(user.ID) if err != nil { html.ServerError(w, r, err) return } - sess := session.New(c.store, request.SessionID(r)) - connector := pocket.NewConnector(c.cfg.PocketConsumerKey(integration.PocketConsumerKey)) - redirectURL := c.cfg.BaseURL() + route.Path(c.router, "pocketCallback") + sess := session.New(h.store, request.SessionID(r)) + connector := pocket.NewConnector(h.cfg.PocketConsumerKey(integration.PocketConsumerKey)) + redirectURL := h.cfg.BaseURL() + route.Path(h.router, "pocketCallback") requestToken, err := connector.RequestToken(redirectURL) if err != nil { logger.Error("[Pocket:Authorize] %v", err) sess.NewFlashErrorMessage(printer.Printf("error.pocket_request_token")) - html.Redirect(w, r, route.Path(c.router, "integrations")) + html.Redirect(w, r, route.Path(h.router, "integrations")) return } @@ -46,41 +45,40 @@ func (c *Controller) PocketAuthorize(w http.ResponseWriter, r *http.Request) { html.Redirect(w, r, connector.AuthorizationURL(requestToken, redirectURL)) } -// PocketCallback saves the personal access token after the authorization step. -func (c *Controller) PocketCallback(w http.ResponseWriter, r *http.Request) { +func (h *handler) pocketCallback(w http.ResponseWriter, r *http.Request) { printer := locale.NewPrinter(request.UserLanguage(r)) - sess := session.New(c.store, request.SessionID(r)) + sess := session.New(h.store, request.SessionID(r)) - user, err := c.store.UserByID(request.UserID(r)) + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } - integration, err := c.store.Integration(user.ID) + integration, err := h.store.Integration(user.ID) if err != nil { html.ServerError(w, r, err) return } - connector := pocket.NewConnector(c.cfg.PocketConsumerKey(integration.PocketConsumerKey)) + connector := pocket.NewConnector(h.cfg.PocketConsumerKey(integration.PocketConsumerKey)) accessToken, err := connector.AccessToken(request.PocketRequestToken(r)) if err != nil { logger.Error("[Pocket:Callback] %v", err) sess.NewFlashErrorMessage(printer.Printf("error.pocket_access_token")) - html.Redirect(w, r, route.Path(c.router, "integrations")) + html.Redirect(w, r, route.Path(h.router, "integrations")) return } sess.SetPocketRequestToken("") integration.PocketAccessToken = accessToken - err = c.store.UpdateIntegration(integration) + err = h.store.UpdateIntegration(integration) if err != nil { html.ServerError(w, r, err) return } sess.NewFlashMessage(printer.Printf("alert.pocket_linked")) - html.Redirect(w, r, route.Path(c.router, "integrations")) + html.Redirect(w, r, route.Path(h.router, "integrations")) } diff --git a/ui/integration_show.go b/ui/integration_show.go index 3cea830..c7f2740 100644 --- a/ui/integration_show.go +++ b/ui/integration_show.go @@ -14,15 +14,14 @@ import ( "miniflux.app/ui/view" ) -// ShowIntegrations renders the page with all external integrations. -func (c *Controller) ShowIntegrations(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) showIntegrationPage(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } - integration, err := c.store.Integration(user.ID) + integration, err := h.store.Integration(user.ID) if err != nil { html.ServerError(w, r, err) return @@ -53,14 +52,14 @@ func (c *Controller) ShowIntegrations(w http.ResponseWriter, r *http.Request) { PocketConsumerKey: integration.PocketConsumerKey, } - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) view.Set("form", integrationForm) view.Set("menu", "settings") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) - view.Set("hasPocketConsumerKeyConfigured", c.cfg.PocketConsumerKey("") != "") + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) + view.Set("hasPocketConsumerKeyConfigured", h.cfg.PocketConsumerKey("") != "") html.OK(w, r, view.Render("integrations")) } diff --git a/ui/integration_update.go b/ui/integration_update.go index 5661daf..16fdb0f 100644 --- a/ui/integration_update.go +++ b/ui/integration_update.go @@ -17,17 +17,16 @@ import ( "miniflux.app/ui/session" ) -// UpdateIntegration updates integration settings. -func (c *Controller) UpdateIntegration(w http.ResponseWriter, r *http.Request) { +func (h *handler) updateIntegration(w http.ResponseWriter, r *http.Request) { printer := locale.NewPrinter(request.UserLanguage(r)) - sess := session.New(c.store, request.SessionID(r)) - user, err := c.store.UserByID(request.UserID(r)) + sess := session.New(h.store, request.SessionID(r)) + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } - integration, err := c.store.Integration(user.ID) + integration, err := h.store.Integration(user.ID) if err != nil { html.ServerError(w, r, err) return @@ -36,9 +35,9 @@ func (c *Controller) UpdateIntegration(w http.ResponseWriter, r *http.Request) { integrationForm := form.NewIntegrationForm(r) integrationForm.Merge(integration) - if integration.FeverUsername != "" && c.store.HasDuplicateFeverUsername(user.ID, integration.FeverUsername) { + if integration.FeverUsername != "" && h.store.HasDuplicateFeverUsername(user.ID, integration.FeverUsername) { sess.NewFlashErrorMessage(printer.Printf("error.duplicate_fever_username")) - html.Redirect(w, r, route.Path(c.router, "integrations")) + html.Redirect(w, r, route.Path(h.router, "integrations")) return } @@ -48,12 +47,12 @@ func (c *Controller) UpdateIntegration(w http.ResponseWriter, r *http.Request) { integration.FeverToken = "" } - err = c.store.UpdateIntegration(integration) + err = h.store.UpdateIntegration(integration) if err != nil { html.ServerError(w, r, err) return } sess.NewFlashMessage(printer.Printf("alert.prefs_saved")) - html.Redirect(w, r, route.Path(c.router, "integrations")) + html.Redirect(w, r, route.Path(h.router, "integrations")) } diff --git a/ui/login_check.go b/ui/login_check.go index 73a1736..acc762a 100644 --- a/ui/login_check.go +++ b/ui/login_check.go @@ -13,13 +13,12 @@ import ( "miniflux.app/ui/view" ) -// CheckLogin validates the username/password and redirects the user to the unread page. -func (c *Controller) CheckLogin(w http.ResponseWriter, r *http.Request) { +func (h *handler) checkLogin(w http.ResponseWriter, r *http.Request) { clientIP := request.ClientIP(r) - sess := session.New(c.store, request.SessionID(r)) + sess := session.New(h.store, request.SessionID(r)) authForm := form.NewAuthForm(r) - view := view.New(c.tpl, r, sess) + view := view.New(h.tpl, r, sess) view.Set("errorMessage", "error.bad_credentials") view.Set("form", authForm) @@ -29,22 +28,22 @@ func (c *Controller) CheckLogin(w http.ResponseWriter, r *http.Request) { return } - if err := c.store.CheckPassword(authForm.Username, authForm.Password); err != nil { + if err := h.store.CheckPassword(authForm.Username, authForm.Password); err != nil { logger.Error("[Controller:CheckLogin] [ClientIP=%s] %v", clientIP, err) html.OK(w, r, view.Render("login")) return } - sessionToken, userID, err := c.store.CreateUserSession(authForm.Username, r.UserAgent(), clientIP) + sessionToken, userID, err := h.store.CreateUserSession(authForm.Username, r.UserAgent(), clientIP) if err != nil { html.ServerError(w, r, err) return } logger.Info("[Controller:CheckLogin] username=%s just logged in", authForm.Username) - c.store.SetLastLogin(userID) + h.store.SetLastLogin(userID) - user, err := c.store.UserByID(userID) + user, err := h.store.UserByID(userID) if err != nil { html.ServerError(w, r, err) return @@ -56,9 +55,9 @@ func (c *Controller) CheckLogin(w http.ResponseWriter, r *http.Request) { http.SetCookie(w, cookie.New( cookie.CookieUserSessionID, sessionToken, - c.cfg.IsHTTPS, - c.cfg.BasePath(), + h.cfg.IsHTTPS, + h.cfg.BasePath(), )) - html.Redirect(w, r, route.Path(c.router, "unread")) + html.Redirect(w, r, route.Path(h.router, "unread")) } diff --git a/ui/login_show.go b/ui/login_show.go index 890f9db..ea458f4 100644 --- a/ui/login_show.go +++ b/ui/login_show.go @@ -14,14 +14,13 @@ import ( "miniflux.app/ui/view" ) -// ShowLoginPage shows the login form. -func (c *Controller) ShowLoginPage(w http.ResponseWriter, r *http.Request) { +func (h *handler) showLoginPage(w http.ResponseWriter, r *http.Request) { if request.IsAuthenticated(r) { - html.Redirect(w, r, route.Path(c.router, "unread")) + html.Redirect(w, r, route.Path(h.router, "unread")) return } - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) html.OK(w, r, view.Render("login")) } diff --git a/ui/logout.go b/ui/logout.go index 59f8af2..ecb08b9 100644 --- a/ui/logout.go +++ b/ui/logout.go @@ -15,10 +15,9 @@ import ( "miniflux.app/ui/session" ) -// Logout destroy the session and redirects the user to the login page. -func (c *Controller) Logout(w http.ResponseWriter, r *http.Request) { - sess := session.New(c.store, request.SessionID(r)) - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) logout(w http.ResponseWriter, r *http.Request) { + sess := session.New(h.store, request.SessionID(r)) + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return @@ -27,15 +26,15 @@ func (c *Controller) Logout(w http.ResponseWriter, r *http.Request) { sess.SetLanguage(user.Language) sess.SetTheme(user.Theme) - if err := c.store.RemoveUserSessionByToken(user.ID, request.UserSessionToken(r)); err != nil { + if err := h.store.RemoveUserSessionByToken(user.ID, request.UserSessionToken(r)); err != nil { logger.Error("[Controller:Logout] %v", err) } http.SetCookie(w, cookie.Expired( cookie.CookieUserSessionID, - c.cfg.IsHTTPS, - c.cfg.BasePath(), + h.cfg.IsHTTPS, + h.cfg.BasePath(), )) - html.Redirect(w, r, route.Path(c.router, "login")) + html.Redirect(w, r, route.Path(h.router, "login")) } diff --git a/ui/middleware.go b/ui/middleware.go new file mode 100644 index 0000000..6ec68ee --- /dev/null +++ b/ui/middleware.go @@ -0,0 +1,149 @@ +// Copyright 2018 Frédéric Guillot. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package ui // import "miniflux.app/ui" + +import ( + "context" + "errors" + "net/http" + + "miniflux.app/config" + "miniflux.app/http/cookie" + "miniflux.app/http/request" + "miniflux.app/http/response/html" + "miniflux.app/http/route" + "miniflux.app/storage" + "miniflux.app/logger" + "miniflux.app/model" + + "github.com/gorilla/mux" +) + +type middleware struct { + router *mux.Router + cfg *config.Config + store *storage.Storage +} + +func newMiddleware(router *mux.Router, cfg *config.Config, store *storage.Storage) *middleware { + return &middleware{router, cfg, store} +} + +func (m *middleware) handleUserSession(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + session := m.getUserSessionFromCookie(r) + + if session == nil { + logger.Debug("[UserSession] Session not found") + if m.isPublicRoute(r) { + next.ServeHTTP(w, r) + } else { + html.Redirect(w, r, route.Path(m.router, "login")) + } + } else { + logger.Debug("[UserSession] %s", session) + + ctx := r.Context() + ctx = context.WithValue(ctx, request.UserIDContextKey, session.UserID) + ctx = context.WithValue(ctx, request.IsAuthenticatedContextKey, true) + ctx = context.WithValue(ctx, request.UserSessionTokenContextKey, session.Token) + + next.ServeHTTP(w, r.WithContext(ctx)) + } + }) +} + +func (m *middleware) handleAppSession(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var err error + session := m.getAppSessionValueFromCookie(r) + + if session == nil { + logger.Debug("[AppSession] Session not found") + + session, err = m.store.CreateSession() + if err != nil { + html.ServerError(w, r, err) + return + } + + http.SetCookie(w, cookie.New(cookie.CookieSessionID, session.ID, m.cfg.IsHTTPS, m.cfg.BasePath())) + } else { + logger.Debug("[AppSession] %s", session) + } + + if r.Method == "POST" { + formValue := r.FormValue("csrf") + headerValue := r.Header.Get("X-Csrf-Token") + + if session.Data.CSRF != formValue && session.Data.CSRF != headerValue { + logger.Error(`[AppSession] Invalid or missing CSRF token: Form="%s", Header="%s"`, formValue, headerValue) + html.BadRequest(w, r, errors.New("Invalid or missing CSRF")) + return + } + } + + ctx := r.Context() + ctx = context.WithValue(ctx, request.SessionIDContextKey, session.ID) + ctx = context.WithValue(ctx, request.CSRFContextKey, session.Data.CSRF) + ctx = context.WithValue(ctx, request.OAuth2StateContextKey, session.Data.OAuth2State) + ctx = context.WithValue(ctx, request.FlashMessageContextKey, session.Data.FlashMessage) + ctx = context.WithValue(ctx, request.FlashErrorMessageContextKey, session.Data.FlashErrorMessage) + ctx = context.WithValue(ctx, request.UserLanguageContextKey, session.Data.Language) + ctx = context.WithValue(ctx, request.UserThemeContextKey, session.Data.Theme) + ctx = context.WithValue(ctx, request.PocketRequestTokenContextKey, session.Data.PocketRequestToken) + next.ServeHTTP(w, r.WithContext(ctx)) + }) +} + +func (m *middleware) getAppSessionValueFromCookie(r *http.Request) *model.Session { + cookieValue := request.CookieValue(r, cookie.CookieSessionID) + if cookieValue == "" { + return nil + } + + session, err := m.store.Session(cookieValue) + if err != nil { + logger.Error("[AppSession] %v", err) + return nil + } + + return session +} + +func (m *middleware) isPublicRoute(r *http.Request) bool { + route := mux.CurrentRoute(r) + switch route.GetName() { + case "login", + "checkLogin", + "stylesheet", + "javascript", + "oauth2Redirect", + "oauth2Callback", + "appIcon", + "favicon", + "webManifest", + "robots", + "healthcheck": + return true + default: + return false + } +} + +func (m *middleware) getUserSessionFromCookie(r *http.Request) *model.UserSession { + cookieValue := request.CookieValue(r, cookie.CookieUserSessionID) + if cookieValue == "" { + return nil + } + + session, err := m.store.UserSessionByToken(cookieValue) + if err != nil { + logger.Error("[UserSession] %v", err) + return nil + } + + return session +} diff --git a/ui/oauth2_callback.go b/ui/oauth2_callback.go index 0aecd1c..bd7c999 100644 --- a/ui/oauth2_callback.go +++ b/ui/oauth2_callback.go @@ -17,51 +17,50 @@ import ( "miniflux.app/ui/session" ) -// OAuth2Callback receives the authorization code and create a new session. -func (c *Controller) OAuth2Callback(w http.ResponseWriter, r *http.Request) { +func (h *handler) oauth2Callback(w http.ResponseWriter, r *http.Request) { clientIP := request.ClientIP(r) printer := locale.NewPrinter(request.UserLanguage(r)) - sess := session.New(c.store, request.SessionID(r)) + sess := session.New(h.store, request.SessionID(r)) provider := request.RouteStringParam(r, "provider") if provider == "" { logger.Error("[OAuth2] Invalid or missing provider") - html.Redirect(w, r, route.Path(c.router, "login")) + html.Redirect(w, r, route.Path(h.router, "login")) return } code := request.QueryStringParam(r, "code", "") if code == "" { logger.Error("[OAuth2] No code received on callback") - html.Redirect(w, r, route.Path(c.router, "login")) + html.Redirect(w, r, route.Path(h.router, "login")) return } state := request.QueryStringParam(r, "state", "") if state == "" || state != request.OAuth2State(r) { logger.Error(`[OAuth2] Invalid state value: got "%s" instead of "%s"`, state, request.OAuth2State(r)) - html.Redirect(w, r, route.Path(c.router, "login")) + html.Redirect(w, r, route.Path(h.router, "login")) return } - authProvider, err := getOAuth2Manager(c.cfg).Provider(provider) + authProvider, err := getOAuth2Manager(h.cfg).Provider(provider) if err != nil { logger.Error("[OAuth2] %v", err) - html.Redirect(w, r, route.Path(c.router, "login")) + html.Redirect(w, r, route.Path(h.router, "login")) return } profile, err := authProvider.GetProfile(code) if err != nil { logger.Error("[OAuth2] %v", err) - html.Redirect(w, r, route.Path(c.router, "login")) + html.Redirect(w, r, route.Path(h.router, "login")) return } logger.Info("[OAuth2] [ClientIP=%s] Successful auth for %s", clientIP, profile) if request.IsAuthenticated(r) { - user, err := c.store.UserByExtraField(profile.Key, profile.ID) + user, err := h.store.UserByExtraField(profile.Key, profile.ID) if err != nil { html.ServerError(w, r, err) return @@ -70,28 +69,28 @@ func (c *Controller) OAuth2Callback(w http.ResponseWriter, r *http.Request) { if user != nil { logger.Error("[OAuth2] User #%d cannot be associated because %s is already associated", request.UserID(r), user.Username) sess.NewFlashErrorMessage(printer.Printf("error.duplicate_linked_account")) - html.Redirect(w, r, route.Path(c.router, "settings")) + html.Redirect(w, r, route.Path(h.router, "settings")) return } - if err := c.store.UpdateExtraField(request.UserID(r), profile.Key, profile.ID); err != nil { + if err := h.store.UpdateExtraField(request.UserID(r), profile.Key, profile.ID); err != nil { html.ServerError(w, r, err) return } sess.NewFlashMessage(printer.Printf("alert.account_linked")) - html.Redirect(w, r, route.Path(c.router, "settings")) + html.Redirect(w, r, route.Path(h.router, "settings")) return } - user, err := c.store.UserByExtraField(profile.Key, profile.ID) + user, err := h.store.UserByExtraField(profile.Key, profile.ID) if err != nil { html.ServerError(w, r, err) return } if user == nil { - if !c.cfg.IsOAuth2UserCreationAllowed() { + if !h.cfg.IsOAuth2UserCreationAllowed() { html.Forbidden(w, r) return } @@ -101,13 +100,13 @@ func (c *Controller) OAuth2Callback(w http.ResponseWriter, r *http.Request) { user.IsAdmin = false user.Extra[profile.Key] = profile.ID - if err := c.store.CreateUser(user); err != nil { + if err := h.store.CreateUser(user); err != nil { html.ServerError(w, r, err) return } } - sessionToken, _, err := c.store.CreateUserSession(user.Username, r.UserAgent(), clientIP) + sessionToken, _, err := h.store.CreateUserSession(user.Username, r.UserAgent(), clientIP) if err != nil { html.ServerError(w, r, err) return @@ -115,16 +114,16 @@ func (c *Controller) OAuth2Callback(w http.ResponseWriter, r *http.Request) { logger.Info("[OAuth2] [ClientIP=%s] username=%s (%s) just logged in", clientIP, user.Username, profile) - c.store.SetLastLogin(user.ID) + h.store.SetLastLogin(user.ID) sess.SetLanguage(user.Language) sess.SetTheme(user.Theme) http.SetCookie(w, cookie.New( cookie.CookieUserSessionID, sessionToken, - c.cfg.IsHTTPS, - c.cfg.BasePath(), + h.cfg.IsHTTPS, + h.cfg.BasePath(), )) - html.Redirect(w, r, route.Path(c.router, "unread")) + html.Redirect(w, r, route.Path(h.router, "unread")) } diff --git a/ui/oauth2_redirect.go b/ui/oauth2_redirect.go index e54309a..85116cf 100644 --- a/ui/oauth2_redirect.go +++ b/ui/oauth2_redirect.go @@ -14,21 +14,20 @@ import ( "miniflux.app/ui/session" ) -// OAuth2Redirect redirects the user to the consent page to ask for permission. -func (c *Controller) OAuth2Redirect(w http.ResponseWriter, r *http.Request) { - sess := session.New(c.store, request.SessionID(r)) +func (h *handler) oauth2Redirect(w http.ResponseWriter, r *http.Request) { + sess := session.New(h.store, request.SessionID(r)) provider := request.RouteStringParam(r, "provider") if provider == "" { logger.Error("[OAuth2] Invalid or missing provider: %s", provider) - html.Redirect(w, r, route.Path(c.router, "login")) + html.Redirect(w, r, route.Path(h.router, "login")) return } - authProvider, err := getOAuth2Manager(c.cfg).Provider(provider) + authProvider, err := getOAuth2Manager(h.cfg).Provider(provider) if err != nil { logger.Error("[OAuth2] %v", err) - html.Redirect(w, r, route.Path(c.router, "login")) + html.Redirect(w, r, route.Path(h.router, "login")) return } diff --git a/ui/oauth2_unlink.go b/ui/oauth2_unlink.go index 8e38ddc..3283f89 100644 --- a/ui/oauth2_unlink.go +++ b/ui/oauth2_unlink.go @@ -15,26 +15,25 @@ import ( "miniflux.app/ui/session" ) -// OAuth2Unlink unlink an account from the external provider. -func (c *Controller) OAuth2Unlink(w http.ResponseWriter, r *http.Request) { +func (h *handler) oauth2Unlink(w http.ResponseWriter, r *http.Request) { printer := locale.NewPrinter(request.UserLanguage(r)) provider := request.RouteStringParam(r, "provider") if provider == "" { logger.Info("[OAuth2] Invalid or missing provider") - html.Redirect(w, r, route.Path(c.router, "login")) + html.Redirect(w, r, route.Path(h.router, "login")) return } - authProvider, err := getOAuth2Manager(c.cfg).Provider(provider) + authProvider, err := getOAuth2Manager(h.cfg).Provider(provider) if err != nil { logger.Error("[OAuth2] %v", err) - html.Redirect(w, r, route.Path(c.router, "settings")) + html.Redirect(w, r, route.Path(h.router, "settings")) return } - sess := session.New(c.store, request.SessionID(r)) + sess := session.New(h.store, request.SessionID(r)) - hasPassword, err := c.store.HasPassword(request.UserID(r)) + hasPassword, err := h.store.HasPassword(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return @@ -42,15 +41,15 @@ func (c *Controller) OAuth2Unlink(w http.ResponseWriter, r *http.Request) { if !hasPassword { sess.NewFlashErrorMessage(printer.Printf("error.unlink_account_without_password")) - html.Redirect(w, r, route.Path(c.router, "settings")) + html.Redirect(w, r, route.Path(h.router, "settings")) return } - if err := c.store.RemoveExtraField(request.UserID(r), authProvider.GetUserExtraKey()); err != nil { + if err := h.store.RemoveExtraField(request.UserID(r), authProvider.GetUserExtraKey()); err != nil { html.ServerError(w, r, err) return } sess.NewFlashMessage(printer.Printf("alert.account_unlinked")) - html.Redirect(w, r, route.Path(c.router, "settings")) + html.Redirect(w, r, route.Path(h.router, "settings")) } diff --git a/ui/opml_export.go b/ui/opml_export.go index 55ceb56..4eb6d2c 100644 --- a/ui/opml_export.go +++ b/ui/opml_export.go @@ -13,9 +13,8 @@ import ( "miniflux.app/reader/opml" ) -// Export generates the OPML file. -func (c *Controller) Export(w http.ResponseWriter, r *http.Request) { - opml, err := opml.NewHandler(c.store).Export(request.UserID(r)) +func (h *handler) exportFeeds(w http.ResponseWriter, r *http.Request) { + opml, err := opml.NewHandler(h.store).Export(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return diff --git a/ui/opml_import.go b/ui/opml_import.go index 9a763ca..06f54bb 100644 --- a/ui/opml_import.go +++ b/ui/opml_import.go @@ -13,20 +13,19 @@ import ( "miniflux.app/ui/view" ) -// Import shows the import form. -func (c *Controller) Import(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) showImportPage(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) view.Set("menu", "feeds") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) html.OK(w, r, view.Render("import")) } diff --git a/ui/opml_upload.go b/ui/opml_upload.go index 538f822..82e2ac8 100644 --- a/ui/opml_upload.go +++ b/ui/opml_upload.go @@ -16,9 +16,8 @@ import ( "miniflux.app/ui/view" ) -// UploadOPML handles OPML file importation. -func (c *Controller) UploadOPML(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) uploadOPML(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return @@ -27,7 +26,7 @@ func (c *Controller) UploadOPML(w http.ResponseWriter, r *http.Request) { file, fileHeader, err := r.FormFile("file") if err != nil { logger.Error("[Controller:UploadOPML] %v", err) - html.Redirect(w, r, route.Path(c.router, "import")) + html.Redirect(w, r, route.Path(h.router, "import")) return } defer file.Close() @@ -39,12 +38,12 @@ func (c *Controller) UploadOPML(w http.ResponseWriter, r *http.Request) { fileHeader.Size, ) - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) view.Set("menu", "feeds") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) if fileHeader.Size == 0 { view.Set("errorMessage", "error.empty_file") @@ -52,11 +51,11 @@ func (c *Controller) UploadOPML(w http.ResponseWriter, r *http.Request) { return } - if impErr := opml.NewHandler(c.store).Import(user.ID, file); impErr != nil { + if impErr := opml.NewHandler(h.store).Import(user.ID, file); impErr != nil { view.Set("errorMessage", impErr) html.OK(w, r, view.Render("import")) return } - html.Redirect(w, r, route.Path(c.router, "feeds")) + html.Redirect(w, r, route.Path(h.router, "feeds")) } diff --git a/ui/pagination.go b/ui/pagination.go index 5fdf715..7534431 100644 --- a/ui/pagination.go +++ b/ui/pagination.go @@ -2,7 +2,7 @@ // Use of this source code is governed by the Apache 2.0 // license that can be found in the LICENSE file. -package ui // import "miniflux.app/ui" +package ui // import "miniflux.app/ui" const ( nbItemsPerPage = 100 @@ -20,7 +20,7 @@ type pagination struct { SearchQuery string } -func (c *Controller) getPagination(route string, total, offset int) pagination { +func getPagination(route string, total, offset int) pagination { nextOffset := 0 prevOffset := 0 showNext := (total - offset) > nbItemsPerPage diff --git a/ui/proxy.go b/ui/proxy.go index 050c070..e9f8ec0 100644 --- a/ui/proxy.go +++ b/ui/proxy.go @@ -18,8 +18,7 @@ import ( "miniflux.app/http/response/html" ) -// ImageProxy fetch an image from a remote server and sent it back to the browser. -func (c *Controller) ImageProxy(w http.ResponseWriter, r *http.Request) { +func (h *handler) imageProxy(w http.ResponseWriter, r *http.Request) { // If we receive a "If-None-Match" header, we assume the image is already stored in browser cache. if r.Header.Get("If-None-Match") != "" { w.WriteHeader(http.StatusNotModified) diff --git a/ui/search_entries.go b/ui/search_entries.go index 1304ef2..be97da0 100644 --- a/ui/search_entries.go +++ b/ui/search_entries.go @@ -15,9 +15,8 @@ import ( "miniflux.app/ui/view" ) -// ShowSearchEntries shows all entries for the given feed. -func (c *Controller) ShowSearchEntries(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) showSearchEntriesPage(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return @@ -25,7 +24,7 @@ func (c *Controller) ShowSearchEntries(w http.ResponseWriter, r *http.Request) { searchQuery := request.QueryStringParam(r, "q", "") offset := request.QueryIntParam(r, "offset", 0) - builder := c.store.NewEntryQueryBuilder(user.ID) + builder := h.store.NewEntryQueryBuilder(user.ID) builder.WithSearchQuery(searchQuery) builder.WithoutStatus(model.EntryStatusRemoved) builder.WithOrder(model.DefaultSortingOrder) @@ -45,9 +44,9 @@ func (c *Controller) ShowSearchEntries(w http.ResponseWriter, r *http.Request) { return } - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) - pagination := c.getPagination(route.Path(c.router, "searchEntries"), count, offset) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) + pagination := getPagination(route.Path(h.router, "searchEntries"), count, offset) pagination.SearchQuery = searchQuery view.Set("searchQuery", searchQuery) @@ -56,9 +55,9 @@ func (c *Controller) ShowSearchEntries(w http.ResponseWriter, r *http.Request) { view.Set("pagination", pagination) view.Set("menu", "search") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) - view.Set("hasSaveEntry", c.store.HasSaveEntry(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) + view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID)) html.OK(w, r, view.Render("search_entries")) } diff --git a/ui/session_list.go b/ui/session_list.go index 76e693a..28da202 100644 --- a/ui/session_list.go +++ b/ui/session_list.go @@ -13,18 +13,17 @@ import ( "miniflux.app/ui/view" ) -// ShowSessions shows the list of active user sessions. -func (c *Controller) ShowSessions(w http.ResponseWriter, r *http.Request) { - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) +func (h *handler) showSessionsPage(w http.ResponseWriter, r *http.Request) { + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) - user, err := c.store.UserByID(request.UserID(r)) + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } - sessions, err := c.store.UserSessions(user.ID) + sessions, err := h.store.UserSessions(user.ID) if err != nil { html.ServerError(w, r, err) return @@ -36,8 +35,8 @@ func (c *Controller) ShowSessions(w http.ResponseWriter, r *http.Request) { view.Set("sessions", sessions) view.Set("menu", "settings") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) html.OK(w, r, view.Render("sessions")) } diff --git a/ui/session_remove.go b/ui/session_remove.go index cc62612..e84a7a2 100644 --- a/ui/session_remove.go +++ b/ui/session_remove.go @@ -13,13 +13,12 @@ import ( "miniflux.app/logger" ) -// RemoveSession remove a user session. -func (c *Controller) RemoveSession(w http.ResponseWriter, r *http.Request) { +func (h *handler) removeSession(w http.ResponseWriter, r *http.Request) { sessionID := request.RouteInt64Param(r, "sessionID") - err := c.store.RemoveUserSessionByID(request.UserID(r), sessionID) + err := h.store.RemoveUserSessionByID(request.UserID(r), sessionID) if err != nil { logger.Error("[Controller:RemoveSession] %v", err) } - html.Redirect(w, r, route.Path(c.router, "sessions")) + html.Redirect(w, r, route.Path(h.router, "sessions")) } diff --git a/ui/settings_show.go b/ui/settings_show.go index 4f4756d..39d85ca 100644 --- a/ui/settings_show.go +++ b/ui/settings_show.go @@ -16,12 +16,11 @@ import ( "miniflux.app/ui/view" ) -// ShowSettings shows the settings page. -func (c *Controller) ShowSettings(w http.ResponseWriter, r *http.Request) { - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) +func (h *handler) showSettingsPage(w http.ResponseWriter, r *http.Request) { + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) - user, err := c.store.UserByID(request.UserID(r)) + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return @@ -35,7 +34,7 @@ func (c *Controller) ShowSettings(w http.ResponseWriter, r *http.Request) { EntryDirection: user.EntryDirection, } - timezones, err := c.store.Timezones() + timezones, err := h.store.Timezones() if err != nil { html.ServerError(w, r, err) return @@ -47,8 +46,8 @@ func (c *Controller) ShowSettings(w http.ResponseWriter, r *http.Request) { view.Set("timezones", timezones) view.Set("menu", "settings") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) html.OK(w, r, view.Render("settings")) } diff --git a/ui/settings_update.go b/ui/settings_update.go index 466bb78..3497dd9 100644 --- a/ui/settings_update.go +++ b/ui/settings_update.go @@ -18,18 +18,17 @@ import ( "miniflux.app/ui/view" ) -// UpdateSettings update the settings. -func (c *Controller) UpdateSettings(w http.ResponseWriter, r *http.Request) { - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) +func (h *handler) updateSettings(w http.ResponseWriter, r *http.Request) { + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) - user, err := c.store.UserByID(request.UserID(r)) + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } - timezones, err := c.store.Timezones() + timezones, err := h.store.Timezones() if err != nil { html.ServerError(w, r, err) return @@ -43,8 +42,8 @@ func (c *Controller) UpdateSettings(w http.ResponseWriter, r *http.Request) { view.Set("timezones", timezones) view.Set("menu", "settings") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) if err := settingsForm.Validate(); err != nil { view.Set("errorMessage", err.Error()) @@ -52,13 +51,13 @@ func (c *Controller) UpdateSettings(w http.ResponseWriter, r *http.Request) { return } - if c.store.AnotherUserExists(user.ID, settingsForm.Username) { + if h.store.AnotherUserExists(user.ID, settingsForm.Username) { view.Set("errorMessage", "error.user_already_exists") html.OK(w, r, view.Render("settings")) return } - err = c.store.UpdateUser(settingsForm.Merge(user)) + err = h.store.UpdateUser(settingsForm.Merge(user)) if err != nil { logger.Error("[Controller:UpdateSettings] %v", err) view.Set("errorMessage", "error.unable_to_update_user") @@ -69,5 +68,5 @@ func (c *Controller) UpdateSettings(w http.ResponseWriter, r *http.Request) { sess.SetLanguage(user.Language) sess.SetTheme(user.Theme) sess.NewFlashMessage(locale.NewPrinter(request.UserLanguage(r)).Printf("alert.prefs_saved")) - html.Redirect(w, r, route.Path(c.router, "settings")) + html.Redirect(w, r, route.Path(h.router, "settings")) } diff --git a/ui/static_app_icon.go b/ui/static_app_icon.go index 9e2a448..ea0290b 100644 --- a/ui/static_app_icon.go +++ b/ui/static_app_icon.go @@ -15,8 +15,7 @@ import ( "miniflux.app/ui/static" ) -// AppIcon shows application icons. -func (c *Controller) AppIcon(w http.ResponseWriter, r *http.Request) { +func (h *handler) showAppIcon(w http.ResponseWriter, r *http.Request) { filename := request.RouteStringParam(r, "filename") etag, found := static.BinariesChecksums[filename] if !found { diff --git a/ui/static_favicon.go b/ui/static_favicon.go index 1266a19..80060a5 100644 --- a/ui/static_favicon.go +++ b/ui/static_favicon.go @@ -14,8 +14,7 @@ import ( "miniflux.app/ui/static" ) -// Favicon shows the application favicon. -func (c *Controller) Favicon(w http.ResponseWriter, r *http.Request) { +func (h *handler) showFavicon(w http.ResponseWriter, r *http.Request) { etag, found := static.BinariesChecksums["favicon.ico"] if !found { html.NotFound(w, r) diff --git a/ui/static_javascript.go b/ui/static_javascript.go index ff7fd16..de6b342 100644 --- a/ui/static_javascript.go +++ b/ui/static_javascript.go @@ -14,8 +14,7 @@ import ( "miniflux.app/ui/static" ) -// Javascript renders application client side code. -func (c *Controller) Javascript(w http.ResponseWriter, r *http.Request) { +func (h *handler) showJavascript(w http.ResponseWriter, r *http.Request) { filename := request.RouteStringParam(r, "name") etag, found := static.JavascriptsChecksums[filename] if !found { diff --git a/ui/static_manifest.go b/ui/static_manifest.go index 51369a2..50e7e98 100644 --- a/ui/static_manifest.go +++ b/ui/static_manifest.go @@ -13,8 +13,7 @@ import ( "miniflux.app/model" ) -// WebManifest renders web manifest file. -func (c *Controller) WebManifest(w http.ResponseWriter, r *http.Request) { +func (h *handler) showWebManifest(w http.ResponseWriter, r *http.Request) { type webManifestIcon struct { Source string `json:"src"` Sizes string `json:"sizes"` @@ -38,13 +37,13 @@ func (c *Controller) WebManifest(w http.ResponseWriter, r *http.Request) { ShortName: "Miniflux", Description: "Minimalist Feed Reader", Display: "minimal-ui", - StartURL: route.Path(c.router, "unread"), + StartURL: route.Path(h.router, "unread"), ThemeColor: themeColor, BackgroundColor: themeColor, Icons: []webManifestIcon{ - webManifestIcon{Source: route.Path(c.router, "appIcon", "filename", "icon-120.png"), Sizes: "120x120", Type: "image/png"}, - webManifestIcon{Source: route.Path(c.router, "appIcon", "filename", "icon-192.png"), Sizes: "192x192", Type: "image/png"}, - webManifestIcon{Source: route.Path(c.router, "appIcon", "filename", "icon-512.png"), Sizes: "512x512", Type: "image/png"}, + webManifestIcon{Source: route.Path(h.router, "appIcon", "filename", "icon-120.png"), Sizes: "120x120", Type: "image/png"}, + webManifestIcon{Source: route.Path(h.router, "appIcon", "filename", "icon-192.png"), Sizes: "192x192", Type: "image/png"}, + webManifestIcon{Source: route.Path(h.router, "appIcon", "filename", "icon-512.png"), Sizes: "512x512", Type: "image/png"}, }, } diff --git a/ui/static_stylesheet.go b/ui/static_stylesheet.go index 9a21bc6..506caf4 100644 --- a/ui/static_stylesheet.go +++ b/ui/static_stylesheet.go @@ -14,8 +14,7 @@ import ( "miniflux.app/ui/static" ) -// Stylesheet renders the CSS. -func (c *Controller) Stylesheet(w http.ResponseWriter, r *http.Request) { +func (h *handler) showStylesheet(w http.ResponseWriter, r *http.Request) { filename := request.RouteStringParam(r, "name") etag, found := static.StylesheetsChecksums[filename] if !found { diff --git a/ui/subscription_add.go b/ui/subscription_add.go index 3056eec..db539c8 100644 --- a/ui/subscription_add.go +++ b/ui/subscription_add.go @@ -14,18 +14,17 @@ import ( "miniflux.app/ui/view" ) -// AddSubscription shows the form to add a new feed. -func (c *Controller) AddSubscription(w http.ResponseWriter, r *http.Request) { - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) +func (h *handler) showAddSubscriptionPage(w http.ResponseWriter, r *http.Request) { + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) - user, err := c.store.UserByID(request.UserID(r)) + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } - categories, err := c.store.Categories(user.ID) + categories, err := h.store.Categories(user.ID) if err != nil { html.ServerError(w, r, err) return @@ -34,8 +33,8 @@ func (c *Controller) AddSubscription(w http.ResponseWriter, r *http.Request) { view.Set("categories", categories) view.Set("menu", "feeds") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) view.Set("defaultUserAgent", client.DefaultUserAgent) html.OK(w, r, view.Render("add_subscription")) diff --git a/ui/subscription_bookmarklet.go b/ui/subscription_bookmarklet.go index 5c0e08c..060aa48 100644 --- a/ui/subscription_bookmarklet.go +++ b/ui/subscription_bookmarklet.go @@ -15,18 +15,17 @@ import ( "miniflux.app/ui/view" ) -// Bookmarklet prefill the form to add a subscription from the URL provided by the bookmarklet. -func (c *Controller) Bookmarklet(w http.ResponseWriter, r *http.Request) { - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) +func (h *handler) bookmarklet(w http.ResponseWriter, r *http.Request) { + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) - user, err := c.store.UserByID(request.UserID(r)) + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } - categories, err := c.store.Categories(user.ID) + categories, err := h.store.Categories(user.ID) if err != nil { html.ServerError(w, r, err) return @@ -38,8 +37,8 @@ func (c *Controller) Bookmarklet(w http.ResponseWriter, r *http.Request) { view.Set("categories", categories) view.Set("menu", "feeds") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) view.Set("defaultUserAgent", client.DefaultUserAgent) html.OK(w, r, view.Render("add_subscription")) diff --git a/ui/subscription_choose.go b/ui/subscription_choose.go index 4f701c8..b554b03 100644 --- a/ui/subscription_choose.go +++ b/ui/subscription_choose.go @@ -16,18 +16,17 @@ import ( "miniflux.app/ui/view" ) -// ChooseSubscription shows a page to choose a subscription. -func (c *Controller) ChooseSubscription(w http.ResponseWriter, r *http.Request) { - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) +func (h *handler) showChooseSubscriptionPage(w http.ResponseWriter, r *http.Request) { + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) - user, err := c.store.UserByID(request.UserID(r)) + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } - categories, err := c.store.Categories(user.ID) + categories, err := h.store.Categories(user.ID) if err != nil { html.ServerError(w, r, err) return @@ -36,8 +35,8 @@ func (c *Controller) ChooseSubscription(w http.ResponseWriter, r *http.Request) view.Set("categories", categories) view.Set("menu", "feeds") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) view.Set("defaultUserAgent", client.DefaultUserAgent) subscriptionForm := form.NewSubscriptionForm(r) @@ -48,7 +47,7 @@ func (c *Controller) ChooseSubscription(w http.ResponseWriter, r *http.Request) return } - feed, err := c.feedHandler.CreateFeed( + feed, err := h.feedHandler.CreateFeed( user.ID, subscriptionForm.CategoryID, subscriptionForm.URL, @@ -64,5 +63,5 @@ func (c *Controller) ChooseSubscription(w http.ResponseWriter, r *http.Request) return } - html.Redirect(w, r, route.Path(c.router, "feedEntries", "feedID", feed.ID)) + html.Redirect(w, r, route.Path(h.router, "feedEntries", "feedID", feed.ID)) } diff --git a/ui/subscription_submit.go b/ui/subscription_submit.go index bdf6a59..8d3c7ed 100644 --- a/ui/subscription_submit.go +++ b/ui/subscription_submit.go @@ -18,18 +18,17 @@ import ( "miniflux.app/ui/view" ) -// SubmitSubscription try to find a feed from the URL provided by the user. -func (c *Controller) SubmitSubscription(w http.ResponseWriter, r *http.Request) { - sess := session.New(c.store, request.SessionID(r)) - v := view.New(c.tpl, r, sess) +func (h *handler) submitSubscription(w http.ResponseWriter, r *http.Request) { + sess := session.New(h.store, request.SessionID(r)) + v := view.New(h.tpl, r, sess) - user, err := c.store.UserByID(request.UserID(r)) + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } - categories, err := c.store.Categories(user.ID) + categories, err := h.store.Categories(user.ID) if err != nil { html.ServerError(w, r, err) return @@ -38,8 +37,8 @@ func (c *Controller) SubmitSubscription(w http.ResponseWriter, r *http.Request) v.Set("categories", categories) v.Set("menu", "feeds") v.Set("user", user) - v.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - v.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + v.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + v.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) v.Set("defaultUserAgent", client.DefaultUserAgent) subscriptionForm := form.NewSubscriptionForm(r) @@ -73,7 +72,7 @@ func (c *Controller) SubmitSubscription(w http.ResponseWriter, r *http.Request) v.Set("errorMessage", "error.subscription_not_found") html.OK(w, r, v.Render("add_subscription")) case n == 1: - feed, err := c.feedHandler.CreateFeed( + feed, err := h.feedHandler.CreateFeed( user.ID, subscriptionForm.CategoryID, subscriptions[0].URL, @@ -89,15 +88,15 @@ func (c *Controller) SubmitSubscription(w http.ResponseWriter, r *http.Request) return } - html.Redirect(w, r, route.Path(c.router, "feedEntries", "feedID", feed.ID)) + html.Redirect(w, r, route.Path(h.router, "feedEntries", "feedID", feed.ID)) case n > 1: - v := view.New(c.tpl, r, sess) + v := view.New(h.tpl, r, sess) v.Set("subscriptions", subscriptions) v.Set("form", subscriptionForm) v.Set("menu", "feeds") v.Set("user", user) - v.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - v.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + v.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + v.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) html.OK(w, r, v.Render("choose_subscription")) } diff --git a/ui/ui.go b/ui/ui.go new file mode 100644 index 0000000..3577636 --- /dev/null +++ b/ui/ui.go @@ -0,0 +1,133 @@ +// Copyright 2018 Frédéric Guillot. All rights reserved. +// Use of this source code is governed by the Apache 2.0 +// license that can be found in the LICENSE file. + +package ui // import "miniflux.app/ui" + +import ( + "net/http" + + "miniflux.app/config" + "miniflux.app/reader/feed" + "miniflux.app/scheduler" + "miniflux.app/storage" + "miniflux.app/template" + + "github.com/gorilla/mux" +) + +// Serve declares all routes for the user interface. +func Serve(router *mux.Router, cfg *config.Config, store *storage.Storage, pool *scheduler.WorkerPool, feedHandler *feed.Handler) { + middleware := newMiddleware(router, cfg, store) + handler := &handler{router, cfg, store, template.NewEngine(cfg, router), pool, feedHandler} + + uiRouter := router.NewRoute().Subrouter() + uiRouter.Use(middleware.handleAppSession) + uiRouter.Use(middleware.handleUserSession) + + // Static assets. + uiRouter.HandleFunc("/stylesheets/{name}.css", handler.showStylesheet).Name("stylesheet").Methods("GET") + uiRouter.HandleFunc("/{name}.js", handler.showJavascript).Name("javascript").Methods("GET") + uiRouter.HandleFunc("/favicon.ico", handler.showFavicon).Name("favicon").Methods("GET") + uiRouter.HandleFunc("/icon/{filename}", handler.showAppIcon).Name("appIcon").Methods("GET") + uiRouter.HandleFunc("/manifest.json", handler.showWebManifest).Name("webManifest").Methods("GET") + + // New subscription pages. + uiRouter.HandleFunc("/subscribe", handler.showAddSubscriptionPage).Name("addSubscription").Methods("GET") + uiRouter.HandleFunc("/subscribe", handler.submitSubscription).Name("submitSubscription").Methods("POST") + uiRouter.HandleFunc("/subscriptions", handler.showChooseSubscriptionPage).Name("chooseSubscription").Methods("POST") + uiRouter.HandleFunc("/bookmarklet", handler.bookmarklet).Name("bookmarklet").Methods("GET") + + // Unread page. + uiRouter.HandleFunc("/mark-all-as-read", handler.markAllAsRead).Name("markAllAsRead").Methods("GET") + uiRouter.HandleFunc("/unread", handler.showUnreadPage).Name("unread").Methods("GET") + uiRouter.HandleFunc("/unread/entry/{entryID}", handler.showUnreadEntryPage).Name("unreadEntry").Methods("GET") + + // History pages. + uiRouter.HandleFunc("/history", handler.showHistoryPage).Name("history").Methods("GET") + uiRouter.HandleFunc("/history/entry/{entryID}", handler.showReadEntryPage).Name("readEntry").Methods("GET") + uiRouter.HandleFunc("/history/flush", handler.flushHistory).Name("flushHistory").Methods("GET") + + // Bookmark pages. + uiRouter.HandleFunc("/starred", handler.showStarredPage).Name("starred").Methods("GET") + uiRouter.HandleFunc("/starred/entry/{entryID}", handler.showStarredEntryPage).Name("starredEntry").Methods("GET") + + // Search pages. + uiRouter.HandleFunc("/search", handler.showSearchEntriesPage).Name("searchEntries").Methods("GET") + uiRouter.HandleFunc("/search/entry/{entryID}", handler.showSearchEntryPage).Name("searchEntry").Methods("GET") + + // Feed listing pages. + uiRouter.HandleFunc("/feeds", handler.showFeedsPage).Name("feeds").Methods("GET") + uiRouter.HandleFunc("/feeds/refresh", handler.refreshAllFeeds).Name("refreshAllFeeds").Methods("GET") + + // Individual feed pages. + uiRouter.HandleFunc("/feed/{feedID}/refresh", handler.refreshFeed).Name("refreshFeed").Methods("GET") + uiRouter.HandleFunc("/feed/{feedID}/edit", handler.showEditFeedPage).Name("editFeed").Methods("GET") + uiRouter.HandleFunc("/feed/{feedID}/remove", handler.removeFeed).Name("removeFeed").Methods("POST") + uiRouter.HandleFunc("/feed/{feedID}/update", handler.updateFeed).Name("updateFeed").Methods("POST") + uiRouter.HandleFunc("/feed/{feedID}/entries", handler.showFeedEntriesPage).Name("feedEntries").Methods("GET") + uiRouter.HandleFunc("/feed/{feedID}/entry/{entryID}", handler.showFeedEntryPage).Name("feedEntry").Methods("GET") + uiRouter.HandleFunc("/feed/icon/{iconID}", handler.showIcon).Name("icon").Methods("GET") + + // Category pages. + uiRouter.HandleFunc("/category/{categoryID}/entry/{entryID}", handler.showCategoryEntryPage).Name("categoryEntry").Methods("GET") + uiRouter.HandleFunc("/categories", handler.showCategoryListPage).Name("categories").Methods("GET") + uiRouter.HandleFunc("/category/create", handler.showCreateCategoryPage).Name("createCategory").Methods("GET") + uiRouter.HandleFunc("/category/save", handler.saveCategory).Name("saveCategory").Methods("POST") + uiRouter.HandleFunc("/category/{categoryID}/entries", handler.showCategoryEntriesPage).Name("categoryEntries").Methods("GET") + uiRouter.HandleFunc("/category/{categoryID}/edit", handler.showEditCategoryPage).Name("editCategory").Methods("GET") + uiRouter.HandleFunc("/category/{categoryID}/update", handler.updateCategory).Name("updateCategory").Methods("POST") + uiRouter.HandleFunc("/category/{categoryID}/remove", handler.removeCategory).Name("removeCategory").Methods("POST") + + // Entry pages. + uiRouter.HandleFunc("/entry/status", handler.updateEntriesStatus).Name("updateEntriesStatus").Methods("POST") + uiRouter.HandleFunc("/entry/save/{entryID}", handler.saveEntry).Name("saveEntry").Methods("POST") + uiRouter.HandleFunc("/entry/download/{entryID}", handler.fetchContent).Name("fetchContent").Methods("POST") + uiRouter.HandleFunc("/proxy/{encodedURL}", handler.imageProxy).Name("proxy").Methods("GET") + uiRouter.HandleFunc("/entry/bookmark/{entryID}", handler.toggleBookmark).Name("toggleBookmark").Methods("POST") + + // User pages. + uiRouter.HandleFunc("/users", handler.showUsersPage).Name("users").Methods("GET") + uiRouter.HandleFunc("/user/create", handler.showCreateUserPage).Name("createUser").Methods("GET") + uiRouter.HandleFunc("/user/save", handler.saveUser).Name("saveUser").Methods("POST") + uiRouter.HandleFunc("/users/{userID}/edit", handler.showEditUserPage).Name("editUser").Methods("GET") + uiRouter.HandleFunc("/users/{userID}/update", handler.updateUser).Name("updateUser").Methods("POST") + uiRouter.HandleFunc("/users/{userID}/remove", handler.removeUser).Name("removeUser").Methods("POST") + + // Settings pages. + uiRouter.HandleFunc("/settings", handler.showSettingsPage).Name("settings").Methods("GET") + uiRouter.HandleFunc("/settings", handler.updateSettings).Name("updateSettings").Methods("POST") + uiRouter.HandleFunc("/integrations", handler.showIntegrationPage).Name("integrations").Methods("GET") + uiRouter.HandleFunc("/integration", handler.updateIntegration).Name("updateIntegration").Methods("POST") + uiRouter.HandleFunc("/integration/pocket/authorize", handler.pocketAuthorize).Name("pocketAuthorize").Methods("GET") + uiRouter.HandleFunc("/integration/pocket/callback", handler.pocketCallback).Name("pocketCallback").Methods("GET") + uiRouter.HandleFunc("/about", handler.showAboutPage).Name("about").Methods("GET") + + // Session pages. + uiRouter.HandleFunc("/sessions", handler.showSessionsPage).Name("sessions").Methods("GET") + uiRouter.HandleFunc("/sessions/{sessionID}/remove", handler.removeSession).Name("removeSession").Methods("POST") + + // OPML pages. + uiRouter.HandleFunc("/export", handler.exportFeeds).Name("export").Methods("GET") + uiRouter.HandleFunc("/import", handler.showImportPage).Name("import").Methods("GET") + uiRouter.HandleFunc("/upload", handler.uploadOPML).Name("uploadOPML").Methods("POST") + + // OAuth2 flow. + uiRouter.HandleFunc("/oauth2/{provider}/unlink", handler.oauth2Unlink).Name("oauth2Unlink").Methods("GET") + uiRouter.HandleFunc("/oauth2/{provider}/redirect", handler.oauth2Redirect).Name("oauth2Redirect").Methods("GET") + uiRouter.HandleFunc("/oauth2/{provider}/callback", handler.oauth2Callback).Name("oauth2Callback").Methods("GET") + + // Authentication pages. + uiRouter.HandleFunc("/login", handler.checkLogin).Name("checkLogin").Methods("POST") + uiRouter.HandleFunc("/logout", handler.logout).Name("logout").Methods("GET") + uiRouter.HandleFunc("/", handler.showLoginPage).Name("login").Methods("GET") + + router.HandleFunc("/healthcheck", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("OK")) + }).Name("healthcheck") + + router.HandleFunc("/robots.txt", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain") + w.Write([]byte("User-agent: *\nDisallow: /")) + }).Name("robots") +} diff --git a/ui/unread_entries.go b/ui/unread_entries.go index cb02428..c49fc30 100644 --- a/ui/unread_entries.go +++ b/ui/unread_entries.go @@ -15,19 +15,18 @@ import ( "miniflux.app/ui/view" ) -// ShowUnreadPage render the page with all unread entries. -func (c *Controller) ShowUnreadPage(w http.ResponseWriter, r *http.Request) { - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) +func (h *handler) showUnreadPage(w http.ResponseWriter, r *http.Request) { + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) - user, err := c.store.UserByID(request.UserID(r)) + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return } offset := request.QueryIntParam(r, "offset", 0) - builder := c.store.NewEntryQueryBuilder(user.ID) + builder := h.store.NewEntryQueryBuilder(user.ID) builder.WithStatus(model.EntryStatusUnread) countUnread, err := builder.CountEntries() if err != nil { @@ -39,7 +38,7 @@ func (c *Controller) ShowUnreadPage(w http.ResponseWriter, r *http.Request) { offset = 0 } - builder = c.store.NewEntryQueryBuilder(user.ID) + builder = h.store.NewEntryQueryBuilder(user.ID) builder.WithStatus(model.EntryStatusUnread) builder.WithOrder(model.DefaultSortingOrder) builder.WithDirection(user.EntryDirection) @@ -52,12 +51,12 @@ func (c *Controller) ShowUnreadPage(w http.ResponseWriter, r *http.Request) { } view.Set("entries", entries) - view.Set("pagination", c.getPagination(route.Path(c.router, "unread"), countUnread, offset)) + view.Set("pagination", getPagination(route.Path(h.router, "unread"), countUnread, offset)) view.Set("menu", "unread") view.Set("user", user) view.Set("countUnread", countUnread) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) - view.Set("hasSaveEntry", c.store.HasSaveEntry(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) + view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID)) html.OK(w, r, view.Render("unread_entries")) } diff --git a/ui/unread_mark_all_read.go b/ui/unread_mark_all_read.go index 724da67..c7b9fa0 100644 --- a/ui/unread_mark_all_read.go +++ b/ui/unread_mark_all_read.go @@ -13,11 +13,10 @@ import ( "miniflux.app/logger" ) -// MarkAllAsRead marks all unread entries as read. -func (c *Controller) MarkAllAsRead(w http.ResponseWriter, r *http.Request) { - if err := c.store.MarkAllAsRead(request.UserID(r)); err != nil { +func (h *handler) markAllAsRead(w http.ResponseWriter, r *http.Request) { + if err := h.store.MarkAllAsRead(request.UserID(r)); err != nil { logger.Error("[MarkAllAsRead] %v", err) } - html.Redirect(w, r, route.Path(c.router, "unread")) + html.Redirect(w, r, route.Path(h.router, "unread")) } diff --git a/ui/user_create.go b/ui/user_create.go index a94d286..c3269b2 100644 --- a/ui/user_create.go +++ b/ui/user_create.go @@ -14,12 +14,11 @@ import ( "miniflux.app/ui/view" ) -// CreateUser shows the user creation form. -func (c *Controller) CreateUser(w http.ResponseWriter, r *http.Request) { - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) +func (h *handler) showCreateUserPage(w http.ResponseWriter, r *http.Request) { + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) - user, err := c.store.UserByID(request.UserID(r)) + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return @@ -33,8 +32,8 @@ func (c *Controller) CreateUser(w http.ResponseWriter, r *http.Request) { view.Set("form", &form.UserForm{}) view.Set("menu", "settings") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) html.OK(w, r, view.Render("create_user")) } diff --git a/ui/user_edit.go b/ui/user_edit.go index 2348a7d..3d0289c 100644 --- a/ui/user_edit.go +++ b/ui/user_edit.go @@ -15,11 +15,11 @@ import ( ) // EditUser shows the form to edit a user. -func (c *Controller) EditUser(w http.ResponseWriter, r *http.Request) { - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) +func (h *handler) showEditUserPage(w http.ResponseWriter, r *http.Request) { + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) - user, err := c.store.UserByID(request.UserID(r)) + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return @@ -31,7 +31,7 @@ func (c *Controller) EditUser(w http.ResponseWriter, r *http.Request) { } userID := request.RouteInt64Param(r, "userID") - selectedUser, err := c.store.UserByID(userID) + selectedUser, err := h.store.UserByID(userID) if err != nil { html.ServerError(w, r, err) return @@ -51,8 +51,8 @@ func (c *Controller) EditUser(w http.ResponseWriter, r *http.Request) { view.Set("selected_user", selectedUser) view.Set("menu", "settings") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) html.OK(w, r, view.Render("edit_user")) } diff --git a/ui/user_list.go b/ui/user_list.go index 3c9e3ed..37850c7 100644 --- a/ui/user_list.go +++ b/ui/user_list.go @@ -13,12 +13,11 @@ import ( "miniflux.app/ui/view" ) -// ShowUsers renders the list of users. -func (c *Controller) ShowUsers(w http.ResponseWriter, r *http.Request) { - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) +func (h *handler) showUsersPage(w http.ResponseWriter, r *http.Request) { + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) - user, err := c.store.UserByID(request.UserID(r)) + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return @@ -29,7 +28,7 @@ func (c *Controller) ShowUsers(w http.ResponseWriter, r *http.Request) { return } - users, err := c.store.Users() + users, err := h.store.Users() if err != nil { html.ServerError(w, r, err) return @@ -40,8 +39,8 @@ func (c *Controller) ShowUsers(w http.ResponseWriter, r *http.Request) { view.Set("users", users) view.Set("menu", "settings") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) html.OK(w, r, view.Render("users")) } diff --git a/ui/user_remove.go b/ui/user_remove.go index beda7be..6564ca3 100644 --- a/ui/user_remove.go +++ b/ui/user_remove.go @@ -12,9 +12,8 @@ import ( "miniflux.app/http/route" ) -// RemoveUser deletes a user from the database. -func (c *Controller) RemoveUser(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) removeUser(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return @@ -26,7 +25,7 @@ func (c *Controller) RemoveUser(w http.ResponseWriter, r *http.Request) { } userID := request.RouteInt64Param(r, "userID") - selectedUser, err := c.store.UserByID(userID) + selectedUser, err := h.store.UserByID(userID) if err != nil { html.ServerError(w, r, err) return @@ -37,10 +36,10 @@ func (c *Controller) RemoveUser(w http.ResponseWriter, r *http.Request) { return } - if err := c.store.RemoveUser(selectedUser.ID); err != nil { + if err := h.store.RemoveUser(selectedUser.ID); err != nil { html.ServerError(w, r, err) return } - html.Redirect(w, r, route.Path(c.router, "users")) + html.Redirect(w, r, route.Path(h.router, "users")) } diff --git a/ui/user_save.go b/ui/user_save.go index a4be346..f13ac0a 100644 --- a/ui/user_save.go +++ b/ui/user_save.go @@ -16,9 +16,8 @@ import ( "miniflux.app/ui/view" ) -// SaveUser validate and save the new user into the database. -func (c *Controller) SaveUser(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) saveUser(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return @@ -31,12 +30,12 @@ func (c *Controller) SaveUser(w http.ResponseWriter, r *http.Request) { userForm := form.NewUserForm(r) - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) view.Set("menu", "settings") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) view.Set("form", userForm) if err := userForm.ValidateCreation(); err != nil { @@ -45,19 +44,19 @@ func (c *Controller) SaveUser(w http.ResponseWriter, r *http.Request) { return } - if c.store.UserExists(userForm.Username) { + if h.store.UserExists(userForm.Username) { view.Set("errorMessage", "error.user_already_exists") html.OK(w, r, view.Render("create_user")) return } newUser := userForm.ToUser() - if err := c.store.CreateUser(newUser); err != nil { + if err := h.store.CreateUser(newUser); err != nil { logger.Error("[Controller:SaveUser] %v", err) view.Set("errorMessage", "error.unable_to_create_user") html.OK(w, r, view.Render("create_user")) return } - html.Redirect(w, r, route.Path(c.router, "users")) + html.Redirect(w, r, route.Path(h.router, "users")) } diff --git a/ui/user_update.go b/ui/user_update.go index 34567e0..85f1501 100644 --- a/ui/user_update.go +++ b/ui/user_update.go @@ -16,9 +16,8 @@ import ( "miniflux.app/ui/view" ) -// UpdateUser validate and update a user. -func (c *Controller) UpdateUser(w http.ResponseWriter, r *http.Request) { - user, err := c.store.UserByID(request.UserID(r)) +func (h *handler) updateUser(w http.ResponseWriter, r *http.Request) { + user, err := h.store.UserByID(request.UserID(r)) if err != nil { html.ServerError(w, r, err) return @@ -30,7 +29,7 @@ func (c *Controller) UpdateUser(w http.ResponseWriter, r *http.Request) { } userID := request.RouteInt64Param(r, "userID") - selectedUser, err := c.store.UserByID(userID) + selectedUser, err := h.store.UserByID(userID) if err != nil { html.ServerError(w, r, err) return @@ -43,12 +42,12 @@ func (c *Controller) UpdateUser(w http.ResponseWriter, r *http.Request) { userForm := form.NewUserForm(r) - sess := session.New(c.store, request.SessionID(r)) - view := view.New(c.tpl, r, sess) + sess := session.New(h.store, request.SessionID(r)) + view := view.New(h.tpl, r, sess) view.Set("menu", "settings") view.Set("user", user) - view.Set("countUnread", c.store.CountUnreadEntries(user.ID)) - view.Set("countErrorFeeds", c.store.CountErrorFeeds(user.ID)) + view.Set("countUnread", h.store.CountUnreadEntries(user.ID)) + view.Set("countErrorFeeds", h.store.CountErrorFeeds(user.ID)) view.Set("selected_user", selectedUser) view.Set("form", userForm) @@ -58,19 +57,19 @@ func (c *Controller) UpdateUser(w http.ResponseWriter, r *http.Request) { return } - if c.store.AnotherUserExists(selectedUser.ID, userForm.Username) { + if h.store.AnotherUserExists(selectedUser.ID, userForm.Username) { view.Set("errorMessage", "error.user_already_exists") html.OK(w, r, view.Render("edit_user")) return } userForm.Merge(selectedUser) - if err := c.store.UpdateUser(selectedUser); err != nil { + if err := h.store.UpdateUser(selectedUser); err != nil { logger.Error("[Controller:UpdateUser] %v", err) view.Set("errorMessage", "error.unable_to_update_user") html.OK(w, r, view.Render("edit_user")) return } - html.Redirect(w, r, route.Path(c.router, "users")) + html.Redirect(w, r, route.Path(h.router, "users")) } -- cgit v1.2.3