Make sure golint pass on the code base
This commit is contained in:
parent
8781648af9
commit
bb8e61c7c5
59 changed files with 322 additions and 171 deletions
|
@ -9,7 +9,9 @@ go:
|
|||
- 1.9
|
||||
before_install:
|
||||
- npm install -g jshint
|
||||
- go get -u github.com/golang/lint/golint
|
||||
script:
|
||||
- jshint server/static/js/app.js
|
||||
- make lint
|
||||
- make test
|
||||
- make integration-test
|
||||
|
|
14
Makefile
14
Makefile
|
@ -1,9 +1,10 @@
|
|||
APP = miniflux
|
||||
VERSION = $(shell git rev-parse --short HEAD)
|
||||
BUILD_DATE = `date +%FT%T%z`
|
||||
DB_URL = postgres://postgres:postgres@localhost/miniflux_test?sslmode=disable
|
||||
APP := miniflux
|
||||
VERSION := $(shell git rev-parse --short HEAD)
|
||||
BUILD_DATE := `date +%FT%T%z`
|
||||
PKG_LIST := $(shell go list ./... | grep -v /vendor/)
|
||||
DB_URL := postgres://postgres:postgres@localhost/miniflux_test?sslmode=disable
|
||||
|
||||
.PHONY: build-linux build-darwin build run clean test integration-test clean-integration-test
|
||||
.PHONY: build-linux build-darwin build run clean test lint integration-test clean-integration-test
|
||||
|
||||
build-linux:
|
||||
@ go generate
|
||||
|
@ -25,6 +26,9 @@ clean:
|
|||
test:
|
||||
go test -cover -race ./...
|
||||
|
||||
lint:
|
||||
@ golint -set_exit_status ${PKG_LIST}
|
||||
|
||||
integration-test:
|
||||
psql -U postgres -c 'drop database if exists miniflux_test;'
|
||||
psql -U postgres -c 'create database miniflux_test;'
|
||||
|
|
|
@ -24,8 +24,8 @@ func Load() *Translator {
|
|||
return translator
|
||||
}
|
||||
|
||||
// GetAvailableLanguages returns the list of available languages.
|
||||
func GetAvailableLanguages() map[string]string {
|
||||
// AvailableLanguages returns the list of available languages.
|
||||
func AvailableLanguages() map[string]string {
|
||||
return map[string]string{
|
||||
"en_US": "English",
|
||||
"fr_FR": "Français",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
// 2017-11-25 15:50:52.572283626 -0800 PST m=+0.030941705
|
||||
// 2017-11-27 21:07:53.23444885 -0800 PST m=+0.028635078
|
||||
|
||||
package locale
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@ package model
|
|||
|
||||
import "github.com/miniflux/miniflux2/errors"
|
||||
|
||||
// GetThemes returns the list of available themes.
|
||||
func GetThemes() map[string]string {
|
||||
// Themes returns the list of available themes.
|
||||
func Themes() map[string]string {
|
||||
return map[string]string{
|
||||
"default": "Default",
|
||||
"black": "Black",
|
||||
|
@ -16,7 +16,7 @@ func GetThemes() map[string]string {
|
|||
|
||||
// ValidateTheme validates theme value.
|
||||
func ValidateTheme(theme string) error {
|
||||
for key := range GetThemes() {
|
||||
for key := range Themes() {
|
||||
if key == theme {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -33,11 +33,7 @@ func (u User) ValidateUserCreation() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err := u.ValidatePassword(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return u.ValidatePassword()
|
||||
}
|
||||
|
||||
// ValidateUserModification validates user for modification.
|
||||
|
@ -54,11 +50,7 @@ func (u User) ValidateUserModification() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err := ValidateTheme(u.Theme); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return ValidateTheme(u.Theme)
|
||||
}
|
||||
|
||||
// ValidateUserLogin validates user credential requirements.
|
||||
|
|
|
@ -92,7 +92,7 @@ func (h *Handler) CreateFeed(userID, categoryID int64, url string) (*model.Feed,
|
|||
func (h *Handler) RefreshFeed(userID, feedID int64) error {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Handler:RefreshFeed] feedID=%d", feedID))
|
||||
|
||||
originalFeed, err := h.store.GetFeedById(userID, feedID)
|
||||
originalFeed, err := h.store.FeedByID(userID, feedID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ type Handler struct {
|
|||
|
||||
// Export exports user feeds to OPML.
|
||||
func (h *Handler) Export(userID int64) (string, error) {
|
||||
feeds, err := h.store.GetFeeds(userID)
|
||||
feeds, err := h.store.Feeds(userID)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return "", errors.New("unable to fetch feeds")
|
||||
|
@ -52,13 +52,13 @@ func (h *Handler) Import(userID int64, data io.Reader) (err error) {
|
|||
var category *model.Category
|
||||
|
||||
if subscription.CategoryName == "" {
|
||||
category, err = h.store.GetFirstCategory(userID)
|
||||
category, err = h.store.FirstCategory(userID)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return errors.New("unable to find first category")
|
||||
}
|
||||
} else {
|
||||
category, err = h.store.GetCategoryByTitle(userID, subscription.CategoryName)
|
||||
category, err = h.store.CategoryByTitle(userID, subscription.CategoryName)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return errors.New("unable to search category by title")
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/miniflux/miniflux2/reader/sanitizer"
|
||||
)
|
||||
|
||||
// ItemContentProcessor executes a set of functions to sanitize and alter item contents.
|
||||
func ItemContentProcessor(url, content string) string {
|
||||
content = sanitizer.Sanitize(url, content)
|
||||
return rewrite.Rewriter(url, content)
|
||||
|
|
|
@ -38,6 +38,7 @@ var rewriteRules = []func(string, string) string{
|
|||
},
|
||||
}
|
||||
|
||||
// Rewriter modify item contents with a set of rewriting rules.
|
||||
func Rewriter(url, content string) string {
|
||||
for _, rewriteRule := range rewriteRules {
|
||||
content = rewriteRule(url, content)
|
||||
|
|
|
@ -6,6 +6,7 @@ package api
|
|||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/miniflux/miniflux2/server/api/payload"
|
||||
"github.com/miniflux/miniflux2/server/core"
|
||||
)
|
||||
|
@ -65,7 +66,7 @@ func (c *Controller) UpdateCategory(ctx *core.Context, request *core.Request, re
|
|||
|
||||
// GetCategories is the API handler to get a list of categories for a given user.
|
||||
func (c *Controller) GetCategories(ctx *core.Context, request *core.Request, response *core.Response) {
|
||||
categories, err := c.store.GetCategories(ctx.UserID())
|
||||
categories, err := c.store.Categories(ctx.UserID())
|
||||
if err != nil {
|
||||
response.JSON().ServerError(errors.New("Unable to fetch categories"))
|
||||
return
|
||||
|
|
|
@ -66,7 +66,7 @@ func (c *Controller) UpdateFeed(ctx *core.Context, request *core.Request, respon
|
|||
return
|
||||
}
|
||||
|
||||
originalFeed, err := c.store.GetFeedById(userID, feedID)
|
||||
originalFeed, err := c.store.FeedByID(userID, feedID)
|
||||
if err != nil {
|
||||
response.JSON().NotFound(errors.New("Unable to find this feed"))
|
||||
return
|
||||
|
@ -88,7 +88,7 @@ func (c *Controller) UpdateFeed(ctx *core.Context, request *core.Request, respon
|
|||
|
||||
// GetFeeds is the API handler that get all feeds that belongs to the given user.
|
||||
func (c *Controller) GetFeeds(ctx *core.Context, request *core.Request, response *core.Response) {
|
||||
feeds, err := c.store.GetFeeds(ctx.UserID())
|
||||
feeds, err := c.store.Feeds(ctx.UserID())
|
||||
if err != nil {
|
||||
response.JSON().ServerError(errors.New("Unable to fetch feeds from the database"))
|
||||
return
|
||||
|
@ -106,7 +106,7 @@ func (c *Controller) GetFeed(ctx *core.Context, request *core.Request, response
|
|||
return
|
||||
}
|
||||
|
||||
feed, err := c.store.GetFeedById(userID, feedID)
|
||||
feed, err := c.store.FeedByID(userID, feedID)
|
||||
if err != nil {
|
||||
response.JSON().ServerError(errors.New("Unable to fetch this feed"))
|
||||
return
|
||||
|
|
|
@ -6,6 +6,7 @@ package api
|
|||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/miniflux/miniflux2/server/api/payload"
|
||||
"github.com/miniflux/miniflux2/server/core"
|
||||
)
|
||||
|
@ -67,7 +68,7 @@ func (c *Controller) UpdateUser(ctx *core.Context, request *core.Request, respon
|
|||
return
|
||||
}
|
||||
|
||||
originalUser, err := c.store.GetUserById(userID)
|
||||
originalUser, err := c.store.UserByID(userID)
|
||||
if err != nil {
|
||||
response.JSON().BadRequest(errors.New("Unable to fetch this user from the database"))
|
||||
return
|
||||
|
@ -94,7 +95,7 @@ func (c *Controller) GetUsers(ctx *core.Context, request *core.Request, response
|
|||
return
|
||||
}
|
||||
|
||||
users, err := c.store.GetUsers()
|
||||
users, err := c.store.Users()
|
||||
if err != nil {
|
||||
response.JSON().ServerError(errors.New("Unable to fetch the list of users"))
|
||||
return
|
||||
|
@ -116,7 +117,7 @@ func (c *Controller) GetUser(ctx *core.Context, request *core.Request, response
|
|||
return
|
||||
}
|
||||
|
||||
user, err := c.store.GetUserById(userID)
|
||||
user, err := c.store.UserByID(userID)
|
||||
if err != nil {
|
||||
response.JSON().BadRequest(errors.New("Unable to fetch this user from the database"))
|
||||
return
|
||||
|
@ -143,7 +144,7 @@ func (c *Controller) RemoveUser(ctx *core.Context, request *core.Request, respon
|
|||
return
|
||||
}
|
||||
|
||||
user, err := c.store.GetUserById(userID)
|
||||
user, err := c.store.UserByID(userID)
|
||||
if err != nil {
|
||||
response.JSON().ServerError(errors.New("Unable to fetch this user from the database"))
|
||||
return
|
||||
|
|
|
@ -12,11 +12,13 @@ import (
|
|||
"github.com/miniflux/miniflux2/model"
|
||||
)
|
||||
|
||||
// EntriesResponse represents the response sent when fetching entries.
|
||||
type EntriesResponse struct {
|
||||
Total int `json:"total"`
|
||||
Entries model.Entries `json:"entries"`
|
||||
}
|
||||
|
||||
// DecodeUserPayload unserialize JSON user object.
|
||||
func DecodeUserPayload(data io.Reader) (*model.User, error) {
|
||||
var user model.User
|
||||
|
||||
|
@ -28,6 +30,7 @@ func DecodeUserPayload(data io.Reader) (*model.User, error) {
|
|||
return &user, nil
|
||||
}
|
||||
|
||||
// DecodeURLPayload unserialize JSON subscription object.
|
||||
func DecodeURLPayload(data io.Reader) (string, error) {
|
||||
type payload struct {
|
||||
URL string `json:"url"`
|
||||
|
@ -42,6 +45,7 @@ func DecodeURLPayload(data io.Reader) (string, error) {
|
|||
return p.URL, nil
|
||||
}
|
||||
|
||||
// DecodeEntryStatusPayload unserialize JSON entry statuses object.
|
||||
func DecodeEntryStatusPayload(data io.Reader) ([]int64, string, error) {
|
||||
type payload struct {
|
||||
EntryIDs []int64 `json:"entry_ids"`
|
||||
|
@ -57,6 +61,7 @@ func DecodeEntryStatusPayload(data io.Reader) ([]int64, string, error) {
|
|||
return p.EntryIDs, p.Status, nil
|
||||
}
|
||||
|
||||
// DecodeFeedCreationPayload unserialize JSON feed creation object.
|
||||
func DecodeFeedCreationPayload(data io.Reader) (string, int64, error) {
|
||||
type payload struct {
|
||||
FeedURL string `json:"feed_url"`
|
||||
|
@ -72,6 +77,7 @@ func DecodeFeedCreationPayload(data io.Reader) (string, int64, error) {
|
|||
return p.FeedURL, p.CategoryID, nil
|
||||
}
|
||||
|
||||
// DecodeFeedModificationPayload unserialize JSON feed object.
|
||||
func DecodeFeedModificationPayload(data io.Reader) (*model.Feed, error) {
|
||||
var feed model.Feed
|
||||
|
||||
|
@ -83,6 +89,7 @@ func DecodeFeedModificationPayload(data io.Reader) (*model.Feed, error) {
|
|||
return &feed, nil
|
||||
}
|
||||
|
||||
// DecodeCategoryPayload unserialize JSON category object.
|
||||
func DecodeCategoryPayload(data io.Reader) (*model.Category, error) {
|
||||
var category model.Category
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/miniflux/miniflux2/model"
|
||||
"github.com/miniflux/miniflux2/server/middleware"
|
||||
"github.com/miniflux/miniflux2/server/route"
|
||||
"github.com/miniflux/miniflux2/storage"
|
||||
|
||||
|
@ -26,7 +27,7 @@ type Context struct {
|
|||
|
||||
// IsAdminUser checks if the logged user is administrator.
|
||||
func (c *Context) IsAdminUser() bool {
|
||||
if v := c.request.Context().Value("IsAdminUser"); v != nil {
|
||||
if v := c.request.Context().Value(middleware.IsAdminUserContextKey); v != nil {
|
||||
return v.(bool)
|
||||
}
|
||||
return false
|
||||
|
@ -34,7 +35,7 @@ func (c *Context) IsAdminUser() bool {
|
|||
|
||||
// UserTimezone returns the timezone used by the logged user.
|
||||
func (c *Context) UserTimezone() string {
|
||||
if v := c.request.Context().Value("UserTimezone"); v != nil {
|
||||
if v := c.request.Context().Value(middleware.UserTimezoneContextKey); v != nil {
|
||||
return v.(string)
|
||||
}
|
||||
return "UTC"
|
||||
|
@ -42,7 +43,7 @@ func (c *Context) UserTimezone() string {
|
|||
|
||||
// IsAuthenticated returns a boolean if the user is authenticated.
|
||||
func (c *Context) IsAuthenticated() bool {
|
||||
if v := c.request.Context().Value("IsAuthenticated"); v != nil {
|
||||
if v := c.request.Context().Value(middleware.IsAuthenticatedContextKey); v != nil {
|
||||
return v.(bool)
|
||||
}
|
||||
return false
|
||||
|
@ -50,7 +51,7 @@ func (c *Context) IsAuthenticated() bool {
|
|||
|
||||
// UserID returns the UserID of the logged user.
|
||||
func (c *Context) UserID() int64 {
|
||||
if v := c.request.Context().Value("UserId"); v != nil {
|
||||
if v := c.request.Context().Value(middleware.UserIDContextKey); v != nil {
|
||||
return v.(int64)
|
||||
}
|
||||
return 0
|
||||
|
@ -60,7 +61,7 @@ func (c *Context) UserID() int64 {
|
|||
func (c *Context) LoggedUser() *model.User {
|
||||
if c.user == nil {
|
||||
var err error
|
||||
c.user, err = c.store.GetUserById(c.UserID())
|
||||
c.user, err = c.store.UserByID(c.UserID())
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
@ -81,7 +82,7 @@ func (c *Context) UserLanguage() string {
|
|||
|
||||
// CsrfToken returns the current CSRF token.
|
||||
func (c *Context) CsrfToken() string {
|
||||
if v := c.request.Context().Value("CsrfToken"); v != nil {
|
||||
if v := c.request.Context().Value(middleware.CsrfContextKey); v != nil {
|
||||
return v.(string)
|
||||
}
|
||||
|
||||
|
@ -91,7 +92,7 @@ func (c *Context) CsrfToken() string {
|
|||
|
||||
// Route returns the path for the given arguments.
|
||||
func (c *Context) Route(name string, args ...interface{}) string {
|
||||
return route.GetRoute(c.router, name, args...)
|
||||
return route.Path(c.router, name, args...)
|
||||
}
|
||||
|
||||
// NewContext creates a new Context.
|
||||
|
|
|
@ -27,7 +27,7 @@ type Handler struct {
|
|||
translator *locale.Translator
|
||||
template *template.Engine
|
||||
router *mux.Router
|
||||
middleware *middleware.MiddlewareChain
|
||||
middleware *middleware.Chain
|
||||
}
|
||||
|
||||
// Use is a wrapper around an HTTP handler.
|
||||
|
@ -51,7 +51,7 @@ func (h *Handler) Use(f HandlerFunc) http.Handler {
|
|||
}
|
||||
|
||||
// NewHandler returns a new Handler.
|
||||
func NewHandler(store *storage.Storage, router *mux.Router, template *template.Engine, translator *locale.Translator, middleware *middleware.MiddlewareChain) *Handler {
|
||||
func NewHandler(store *storage.Storage, router *mux.Router, template *template.Engine, translator *locale.Translator, middleware *middleware.Chain) *Handler {
|
||||
return &Handler{
|
||||
store: store,
|
||||
translator: translator,
|
||||
|
|
|
@ -6,15 +6,18 @@ package middleware
|
|||
|
||||
import (
|
||||
"context"
|
||||
"github.com/miniflux/miniflux2/storage"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/miniflux/miniflux2/storage"
|
||||
)
|
||||
|
||||
// BasicAuthMiddleware is the middleware for HTTP Basic authentication.
|
||||
type BasicAuthMiddleware struct {
|
||||
store *storage.Storage
|
||||
}
|
||||
|
||||
// Handler executes the middleware.
|
||||
func (b *BasicAuthMiddleware) Handler(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
|
||||
|
@ -35,7 +38,7 @@ func (b *BasicAuthMiddleware) Handler(next http.Handler) http.Handler {
|
|||
return
|
||||
}
|
||||
|
||||
user, err := b.store.GetUserByUsername(username)
|
||||
user, err := b.store.UserByUsername(username)
|
||||
if err != nil || user == nil {
|
||||
log.Println("[Middleware:BasicAuth] User not found:", username)
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
|
@ -47,15 +50,16 @@ func (b *BasicAuthMiddleware) Handler(next http.Handler) http.Handler {
|
|||
b.store.SetLastLogin(user.ID)
|
||||
|
||||
ctx := r.Context()
|
||||
ctx = context.WithValue(ctx, "UserId", user.ID)
|
||||
ctx = context.WithValue(ctx, "UserTimezone", user.Timezone)
|
||||
ctx = context.WithValue(ctx, "IsAdminUser", user.IsAdmin)
|
||||
ctx = context.WithValue(ctx, "IsAuthenticated", true)
|
||||
ctx = context.WithValue(ctx, UserIDContextKey, user.ID)
|
||||
ctx = context.WithValue(ctx, UserTimezoneContextKey, user.Timezone)
|
||||
ctx = context.WithValue(ctx, IsAdminUserContextKey, user.IsAdmin)
|
||||
ctx = context.WithValue(ctx, IsAuthenticatedContextKey, true)
|
||||
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
// NewBasicAuthMiddleware returns a new BasicAuthMiddleware.
|
||||
func NewBasicAuthMiddleware(s *storage.Storage) *BasicAuthMiddleware {
|
||||
return &BasicAuthMiddleware{store: s}
|
||||
}
|
||||
|
|
26
server/middleware/context_keys.go
Normal file
26
server/middleware/context_keys.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
// 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 middleware
|
||||
|
||||
type contextKey struct {
|
||||
name string
|
||||
}
|
||||
|
||||
var (
|
||||
// UserIDContextKey is the context key used to store the user ID.
|
||||
UserIDContextKey = &contextKey{"UserID"}
|
||||
|
||||
// UserTimezoneContextKey is the context key used to store the user timezone.
|
||||
UserTimezoneContextKey = &contextKey{"UserTimezone"}
|
||||
|
||||
// IsAdminUserContextKey is the context key used to store the user role.
|
||||
IsAdminUserContextKey = &contextKey{"IsAdminUser"}
|
||||
|
||||
// IsAuthenticatedContextKey is the context key used to store the authentication flag.
|
||||
IsAuthenticatedContextKey = &contextKey{"IsAuthenticated"}
|
||||
|
||||
// CsrfContextKey is the context key used to store CSRF token.
|
||||
CsrfContextKey = &contextKey{"CSRF"}
|
||||
)
|
|
@ -6,11 +6,13 @@ package middleware
|
|||
|
||||
import (
|
||||
"context"
|
||||
"github.com/miniflux/miniflux2/helper"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/miniflux/miniflux2/helper"
|
||||
)
|
||||
|
||||
// Csrf is a middleware that handle CSRF tokens.
|
||||
func Csrf(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var csrfToken string
|
||||
|
@ -32,7 +34,7 @@ func Csrf(next http.Handler) http.Handler {
|
|||
}
|
||||
|
||||
ctx := r.Context()
|
||||
ctx = context.WithValue(ctx, "CsrfToken", csrfToken)
|
||||
ctx = context.WithValue(ctx, CsrfContextKey, csrfToken)
|
||||
|
||||
w.Header().Add("Vary", "Cookie")
|
||||
isTokenValid := csrfToken == r.FormValue("csrf") || csrfToken == r.Header.Get("X-Csrf-Token")
|
||||
|
|
|
@ -8,13 +8,16 @@ import (
|
|||
"net/http"
|
||||
)
|
||||
|
||||
// Middleware represents a HTTP middleware.
|
||||
type Middleware func(http.Handler) http.Handler
|
||||
|
||||
type MiddlewareChain struct {
|
||||
// Chain handles a list of middlewares.
|
||||
type Chain struct {
|
||||
middlewares []Middleware
|
||||
}
|
||||
|
||||
func (m *MiddlewareChain) Wrap(h http.Handler) http.Handler {
|
||||
// Wrap adds a HTTP handler into the chain.
|
||||
func (m *Chain) Wrap(h http.Handler) http.Handler {
|
||||
for i := range m.middlewares {
|
||||
h = m.middlewares[len(m.middlewares)-1-i](h)
|
||||
}
|
||||
|
@ -22,10 +25,12 @@ func (m *MiddlewareChain) Wrap(h http.Handler) http.Handler {
|
|||
return h
|
||||
}
|
||||
|
||||
func (m *MiddlewareChain) WrapFunc(fn http.HandlerFunc) http.Handler {
|
||||
// WrapFunc adds a HTTP handler function into the chain.
|
||||
func (m *Chain) WrapFunc(fn http.HandlerFunc) http.Handler {
|
||||
return m.Wrap(fn)
|
||||
}
|
||||
|
||||
func NewMiddlewareChain(middlewares ...Middleware) *MiddlewareChain {
|
||||
return &MiddlewareChain{append(([]Middleware)(nil), middlewares...)}
|
||||
// NewChain returns a new Chain.
|
||||
func NewChain(middlewares ...Middleware) *Chain {
|
||||
return &Chain{append(([]Middleware)(nil), middlewares...)}
|
||||
}
|
||||
|
|
|
@ -16,11 +16,13 @@ import (
|
|||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// SessionMiddleware represents a session middleware.
|
||||
type SessionMiddleware struct {
|
||||
store *storage.Storage
|
||||
router *mux.Router
|
||||
}
|
||||
|
||||
// Handler execute the middleware.
|
||||
func (s *SessionMiddleware) Handler(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
session := s.getSessionFromCookie(r)
|
||||
|
@ -30,13 +32,13 @@ func (s *SessionMiddleware) Handler(next http.Handler) http.Handler {
|
|||
if s.isPublicRoute(r) {
|
||||
next.ServeHTTP(w, r)
|
||||
} else {
|
||||
http.Redirect(w, r, route.GetRoute(s.router, "login"), http.StatusFound)
|
||||
http.Redirect(w, r, route.Path(s.router, "login"), http.StatusFound)
|
||||
}
|
||||
} else {
|
||||
log.Println("[Middleware:Session]", session)
|
||||
ctx := r.Context()
|
||||
ctx = context.WithValue(ctx, "UserId", session.UserID)
|
||||
ctx = context.WithValue(ctx, "IsAuthenticated", true)
|
||||
ctx = context.WithValue(ctx, UserIDContextKey, session.UserID)
|
||||
ctx = context.WithValue(ctx, IsAuthenticatedContextKey, true)
|
||||
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
}
|
||||
|
@ -59,7 +61,7 @@ func (s *SessionMiddleware) getSessionFromCookie(r *http.Request) *model.Session
|
|||
return nil
|
||||
}
|
||||
|
||||
session, err := s.store.GetSessionByToken(sessionCookie.Value)
|
||||
session, err := s.store.SessionByToken(sessionCookie.Value)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
|
@ -68,6 +70,7 @@ func (s *SessionMiddleware) getSessionFromCookie(r *http.Request) *model.Session
|
|||
return session
|
||||
}
|
||||
|
||||
// NewSessionMiddleware returns a new SessionMiddleware.
|
||||
func NewSessionMiddleware(s *storage.Storage, r *mux.Router) *SessionMiddleware {
|
||||
return &SessionMiddleware{store: s, router: r}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,8 @@ import (
|
|||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func GetRoute(router *mux.Router, name string, args ...interface{}) string {
|
||||
// Path returns the defined route based on given arguments.
|
||||
func Path(router *mux.Router, name string, args ...interface{}) string {
|
||||
route := router.Get(name)
|
||||
if route == nil {
|
||||
log.Fatalln("Route not found:", name)
|
||||
|
|
|
@ -31,11 +31,11 @@ func getRoutes(cfg *config.Config, store *storage.Storage, feedHandler *feed.Han
|
|||
apiController := api_controller.NewController(store, feedHandler)
|
||||
uiController := ui_controller.NewController(cfg, store, pool, feedHandler, opml.NewHandler(store))
|
||||
|
||||
apiHandler := core.NewHandler(store, router, templateEngine, translator, middleware.NewMiddlewareChain(
|
||||
apiHandler := core.NewHandler(store, router, templateEngine, translator, middleware.NewChain(
|
||||
middleware.NewBasicAuthMiddleware(store).Handler,
|
||||
))
|
||||
|
||||
uiHandler := core.NewHandler(store, router, templateEngine, translator, middleware.NewMiddlewareChain(
|
||||
uiHandler := core.NewHandler(store, router, templateEngine, translator, middleware.NewChain(
|
||||
middleware.NewSessionMiddleware(store, router).Handler,
|
||||
middleware.Csrf,
|
||||
))
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
// 2017-11-25 10:47:30.351686806 -0800 PST m=+0.007814912
|
||||
// 2017-11-27 21:07:53.21170439 -0800 PST m=+0.005890618
|
||||
|
||||
package static
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
// 2017-11-25 10:47:30.352812144 -0800 PST m=+0.008940250
|
||||
// 2017-11-27 21:07:53.213299146 -0800 PST m=+0.007485374
|
||||
|
||||
package static
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
// 2017-11-25 10:47:30.357950671 -0800 PST m=+0.014078777
|
||||
// 2017-11-27 21:07:53.215205872 -0800 PST m=+0.009392100
|
||||
|
||||
package static
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
// 2017-11-25 10:47:30.370347794 -0800 PST m=+0.026475900
|
||||
// 2017-11-27 21:07:53.233262137 -0800 PST m=+0.027448365
|
||||
|
||||
package template
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ func (e *Engine) parseAll() {
|
|||
return false
|
||||
},
|
||||
"route": func(name string, args ...interface{}) string {
|
||||
return route.GetRoute(e.router, name, args...)
|
||||
return route.Path(e.router, name, args...)
|
||||
},
|
||||
"noescape": func(str string) template.HTML {
|
||||
return template.HTML(str)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
// 2017-11-25 17:05:58.40092186 -0800 PST m=+0.019242510
|
||||
// 2017-11-27 21:07:53.218349526 -0800 PST m=+0.012535754
|
||||
|
||||
package template
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/miniflux/miniflux2/version"
|
||||
)
|
||||
|
||||
// AboutPage shows the about page.
|
||||
func (c *Controller) AboutPage(ctx *core.Context, request *core.Request, response *core.Response) {
|
||||
args, err := c.getCommonTemplateArgs(ctx)
|
||||
if err != nil {
|
||||
|
|
|
@ -22,7 +22,7 @@ func (c *Controller) ShowCategories(ctx *core.Context, request *core.Request, re
|
|||
}
|
||||
|
||||
user := ctx.LoggedUser()
|
||||
categories, err := c.store.GetCategoriesWithFeedCount(user.ID)
|
||||
categories, err := c.store.CategoriesWithFeedCount(user.ID)
|
||||
if err != nil {
|
||||
response.HTML().ServerError(err)
|
||||
return
|
||||
|
@ -57,7 +57,7 @@ func (c *Controller) ShowCategoryEntries(ctx *core.Context, request *core.Reques
|
|||
builder.WithDirection(model.DefaultSortingDirection)
|
||||
builder.WithoutStatus(model.EntryStatusRemoved)
|
||||
builder.WithOffset(offset)
|
||||
builder.WithLimit(NbItemsPerPage)
|
||||
builder.WithLimit(nbItemsPerPage)
|
||||
|
||||
entries, err := builder.GetEntries()
|
||||
if err != nil {
|
||||
|
@ -110,7 +110,7 @@ func (c *Controller) SaveCategory(ctx *core.Context, request *core.Request, resp
|
|||
return
|
||||
}
|
||||
|
||||
duplicateCategory, err := c.store.GetCategoryByTitle(user.ID, categoryForm.Title)
|
||||
duplicateCategory, err := c.store.CategoryByTitle(user.ID, categoryForm.Title)
|
||||
if err != nil {
|
||||
response.HTML().ServerError(err)
|
||||
return
|
||||
|
@ -223,7 +223,7 @@ func (c *Controller) getCategoryFromURL(ctx *core.Context, request *core.Request
|
|||
}
|
||||
|
||||
user := ctx.LoggedUser()
|
||||
category, err := c.store.GetCategory(user.ID, categoryID)
|
||||
category, err := c.store.Category(user.ID, categoryID)
|
||||
if err != nil {
|
||||
response.HTML().ServerError(err)
|
||||
return nil, err
|
||||
|
|
|
@ -39,7 +39,7 @@ func (c *Controller) ShowFeedsPage(ctx *core.Context, request *core.Request, res
|
|||
return
|
||||
}
|
||||
|
||||
feeds, err := c.store.GetFeeds(user.ID)
|
||||
feeds, err := c.store.Feeds(user.ID)
|
||||
if err != nil {
|
||||
response.HTML().ServerError(err)
|
||||
return
|
||||
|
@ -74,7 +74,7 @@ func (c *Controller) ShowFeedEntries(ctx *core.Context, request *core.Request, r
|
|||
builder.WithOrder(model.DefaultSortingOrder)
|
||||
builder.WithDirection(model.DefaultSortingDirection)
|
||||
builder.WithOffset(offset)
|
||||
builder.WithLimit(NbItemsPerPage)
|
||||
builder.WithLimit(nbItemsPerPage)
|
||||
|
||||
entries, err := builder.GetEntries()
|
||||
if err != nil {
|
||||
|
@ -190,7 +190,7 @@ func (c *Controller) getFeedFromURL(request *core.Request, response *core.Respon
|
|||
return nil, err
|
||||
}
|
||||
|
||||
feed, err := c.store.GetFeedById(user.ID, feedID)
|
||||
feed, err := c.store.FeedByID(user.ID, feedID)
|
||||
if err != nil {
|
||||
response.HTML().ServerError(err)
|
||||
return nil, err
|
||||
|
@ -210,7 +210,7 @@ func (c *Controller) getFeedFormTemplateArgs(ctx *core.Context, user *model.User
|
|||
return nil, err
|
||||
}
|
||||
|
||||
categories, err := c.store.GetCategories(user.ID)
|
||||
categories, err := c.store.Categories(user.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ func (c *Controller) ShowHistoryPage(ctx *core.Context, request *core.Request, r
|
|||
builder.WithOrder(model.DefaultSortingOrder)
|
||||
builder.WithDirection(model.DefaultSortingDirection)
|
||||
builder.WithOffset(offset)
|
||||
builder.WithLimit(NbItemsPerPage)
|
||||
builder.WithLimit(nbItemsPerPage)
|
||||
|
||||
entries, err := builder.GetEntries()
|
||||
if err != nil {
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"github.com/miniflux/miniflux2/server/core"
|
||||
"time"
|
||||
|
||||
"github.com/miniflux/miniflux2/server/core"
|
||||
)
|
||||
|
||||
// ShowIcon shows the feed icon.
|
||||
func (c *Controller) ShowIcon(ctx *core.Context, request *core.Request, response *core.Response) {
|
||||
iconID, err := request.IntegerParam("iconID")
|
||||
if err != nil {
|
||||
|
@ -16,7 +18,7 @@ func (c *Controller) ShowIcon(ctx *core.Context, request *core.Request, response
|
|||
return
|
||||
}
|
||||
|
||||
icon, err := c.store.GetIconByID(iconID)
|
||||
icon, err := c.store.IconByID(iconID)
|
||||
if err != nil {
|
||||
response.HTML().ServerError(err)
|
||||
return
|
||||
|
|
|
@ -82,7 +82,7 @@ func (c *Controller) OAuth2Callback(ctx *core.Context, request *core.Request, re
|
|||
return
|
||||
}
|
||||
|
||||
user, err := c.store.GetUserByExtraField(profile.Key, profile.ID)
|
||||
user, err := c.store.UserByExtraField(profile.Key, profile.ID)
|
||||
if err != nil {
|
||||
response.HTML().ServerError(err)
|
||||
return
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/miniflux/miniflux2/server/core"
|
||||
)
|
||||
|
||||
// Export generates the OPML file.
|
||||
func (c *Controller) Export(ctx *core.Context, request *core.Request, response *core.Response) {
|
||||
user := ctx.LoggedUser()
|
||||
opml, err := c.opmlHandler.Export(user.ID)
|
||||
|
@ -21,6 +22,7 @@ func (c *Controller) Export(ctx *core.Context, request *core.Request, response *
|
|||
response.XML().Download("feeds.opml", opml)
|
||||
}
|
||||
|
||||
// Import shows the import form.
|
||||
func (c *Controller) Import(ctx *core.Context, request *core.Request, response *core.Response) {
|
||||
args, err := c.getCommonTemplateArgs(ctx)
|
||||
if err != nil {
|
||||
|
@ -33,6 +35,7 @@ func (c *Controller) Import(ctx *core.Context, request *core.Request, response *
|
|||
}))
|
||||
}
|
||||
|
||||
// UploadOPML handles OPML file importation.
|
||||
func (c *Controller) UploadOPML(ctx *core.Context, request *core.Request, response *core.Response) {
|
||||
file, fileHeader, err := request.File("file")
|
||||
if err != nil {
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
package controller
|
||||
|
||||
const (
|
||||
NbItemsPerPage = 100
|
||||
nbItemsPerPage = 100
|
||||
)
|
||||
|
||||
type Pagination struct {
|
||||
type pagination struct {
|
||||
Route string
|
||||
Total int
|
||||
Offset int
|
||||
|
@ -19,25 +19,25 @@ type Pagination struct {
|
|||
PrevOffset int
|
||||
}
|
||||
|
||||
func (c *Controller) getPagination(route string, total, offset int) Pagination {
|
||||
func (c *Controller) getPagination(route string, total, offset int) pagination {
|
||||
nextOffset := 0
|
||||
prevOffset := 0
|
||||
showNext := (total - offset) > NbItemsPerPage
|
||||
showNext := (total - offset) > nbItemsPerPage
|
||||
showPrev := offset > 0
|
||||
|
||||
if showNext {
|
||||
nextOffset = offset + NbItemsPerPage
|
||||
nextOffset = offset + nbItemsPerPage
|
||||
}
|
||||
|
||||
if showPrev {
|
||||
prevOffset = offset - NbItemsPerPage
|
||||
prevOffset = offset - nbItemsPerPage
|
||||
}
|
||||
|
||||
return Pagination{
|
||||
return pagination{
|
||||
Route: route,
|
||||
Total: total,
|
||||
Offset: offset,
|
||||
ItemsPerPage: NbItemsPerPage,
|
||||
ItemsPerPage: nbItemsPerPage,
|
||||
ShowNext: showNext,
|
||||
NextOffset: nextOffset,
|
||||
ShowPrev: showPrev,
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"github.com/miniflux/miniflux2/server/core"
|
||||
"log"
|
||||
|
||||
"github.com/miniflux/miniflux2/server/core"
|
||||
)
|
||||
|
||||
// ShowSessions shows the list of active sessions.
|
||||
func (c *Controller) ShowSessions(ctx *core.Context, request *core.Request, response *core.Response) {
|
||||
user := ctx.LoggedUser()
|
||||
args, err := c.getCommonTemplateArgs(ctx)
|
||||
|
@ -17,7 +19,7 @@ func (c *Controller) ShowSessions(ctx *core.Context, request *core.Request, resp
|
|||
return
|
||||
}
|
||||
|
||||
sessions, err := c.store.GetSessions(user.ID)
|
||||
sessions, err := c.store.Sessions(user.ID)
|
||||
if err != nil {
|
||||
response.HTML().ServerError(err)
|
||||
return
|
||||
|
@ -31,6 +33,7 @@ func (c *Controller) ShowSessions(ctx *core.Context, request *core.Request, resp
|
|||
}))
|
||||
}
|
||||
|
||||
// RemoveSession remove a session.
|
||||
func (c *Controller) RemoveSession(ctx *core.Context, request *core.Request, response *core.Response) {
|
||||
user := ctx.LoggedUser()
|
||||
|
||||
|
|
|
@ -5,13 +5,15 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/miniflux/miniflux2/locale"
|
||||
"github.com/miniflux/miniflux2/model"
|
||||
"github.com/miniflux/miniflux2/server/core"
|
||||
"github.com/miniflux/miniflux2/server/ui/form"
|
||||
"log"
|
||||
)
|
||||
|
||||
// ShowSettings shows the settings page.
|
||||
func (c *Controller) ShowSettings(ctx *core.Context, request *core.Request, response *core.Response) {
|
||||
user := ctx.LoggedUser()
|
||||
|
||||
|
@ -24,6 +26,7 @@ func (c *Controller) ShowSettings(ctx *core.Context, request *core.Request, resp
|
|||
response.HTML().Render("settings", args)
|
||||
}
|
||||
|
||||
// UpdateSettings update the settings.
|
||||
func (c *Controller) UpdateSettings(ctx *core.Context, request *core.Request, response *core.Response) {
|
||||
user := ctx.LoggedUser()
|
||||
|
||||
|
@ -81,9 +84,9 @@ func (c *Controller) getSettingsFormTemplateArgs(ctx *core.Context, user *model.
|
|||
}
|
||||
|
||||
args["menu"] = "settings"
|
||||
args["themes"] = model.GetThemes()
|
||||
args["languages"] = locale.GetAvailableLanguages()
|
||||
args["timezones"], err = c.store.GetTimezones()
|
||||
args["themes"] = model.Themes()
|
||||
args["languages"] = locale.AvailableLanguages()
|
||||
args["timezones"], err = c.store.Timezones()
|
||||
if err != nil {
|
||||
return args, err
|
||||
}
|
||||
|
|
|
@ -6,12 +6,14 @@ package controller
|
|||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"github.com/miniflux/miniflux2/server/core"
|
||||
"github.com/miniflux/miniflux2/server/static"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/miniflux/miniflux2/server/core"
|
||||
"github.com/miniflux/miniflux2/server/static"
|
||||
)
|
||||
|
||||
// Stylesheet renders the CSS.
|
||||
func (c *Controller) Stylesheet(ctx *core.Context, request *core.Request, response *core.Response) {
|
||||
stylesheet := request.StringParam("name", "white")
|
||||
body := static.Stylesheets["common"]
|
||||
|
@ -25,10 +27,12 @@ func (c *Controller) Stylesheet(ctx *core.Context, request *core.Request, respon
|
|||
response.Cache("text/css", etag, []byte(body), 48*time.Hour)
|
||||
}
|
||||
|
||||
// Javascript renders application client side code.
|
||||
func (c *Controller) Javascript(ctx *core.Context, request *core.Request, response *core.Response) {
|
||||
response.Cache("text/javascript", static.JavascriptChecksums["app"], []byte(static.Javascript["app"]), 48*time.Hour)
|
||||
}
|
||||
|
||||
// Favicon renders the application favicon.
|
||||
func (c *Controller) Favicon(ctx *core.Context, request *core.Request, response *core.Response) {
|
||||
blob, err := base64.StdEncoding.DecodeString(static.Binaries["favicon.ico"])
|
||||
if err != nil {
|
||||
|
|
|
@ -135,7 +135,7 @@ func (c *Controller) getSubscriptionFormTemplateArgs(ctx *core.Context, user *mo
|
|||
return nil, err
|
||||
}
|
||||
|
||||
categories, err := c.store.GetCategories(user.ID)
|
||||
categories, err := c.store.Categories(user.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ func (c *Controller) ShowUnreadPage(ctx *core.Context, request *core.Request, re
|
|||
builder.WithOrder(model.DefaultSortingOrder)
|
||||
builder.WithDirection(model.DefaultSortingDirection)
|
||||
builder.WithOffset(offset)
|
||||
builder.WithLimit(NbItemsPerPage)
|
||||
builder.WithLimit(nbItemsPerPage)
|
||||
|
||||
entries, err := builder.GetEntries()
|
||||
if err != nil {
|
||||
|
|
|
@ -6,12 +6,14 @@ package controller
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"github.com/miniflux/miniflux2/model"
|
||||
"github.com/miniflux/miniflux2/server/core"
|
||||
"github.com/miniflux/miniflux2/server/ui/form"
|
||||
"log"
|
||||
)
|
||||
|
||||
// ShowUsers shows the list of users.
|
||||
func (c *Controller) ShowUsers(ctx *core.Context, request *core.Request, response *core.Response) {
|
||||
user := ctx.LoggedUser()
|
||||
|
||||
|
@ -26,7 +28,7 @@ func (c *Controller) ShowUsers(ctx *core.Context, request *core.Request, respons
|
|||
return
|
||||
}
|
||||
|
||||
users, err := c.store.GetUsers()
|
||||
users, err := c.store.Users()
|
||||
if err != nil {
|
||||
response.HTML().ServerError(err)
|
||||
return
|
||||
|
@ -38,6 +40,7 @@ func (c *Controller) ShowUsers(ctx *core.Context, request *core.Request, respons
|
|||
}))
|
||||
}
|
||||
|
||||
// CreateUser shows the user creation form.
|
||||
func (c *Controller) CreateUser(ctx *core.Context, request *core.Request, response *core.Response) {
|
||||
user := ctx.LoggedUser()
|
||||
|
||||
|
@ -58,6 +61,7 @@ func (c *Controller) CreateUser(ctx *core.Context, request *core.Request, respon
|
|||
}))
|
||||
}
|
||||
|
||||
// SaveUser validate and save the new user into the database.
|
||||
func (c *Controller) SaveUser(ctx *core.Context, request *core.Request, response *core.Response) {
|
||||
user := ctx.LoggedUser()
|
||||
|
||||
|
@ -105,6 +109,7 @@ func (c *Controller) SaveUser(ctx *core.Context, request *core.Request, response
|
|||
response.Redirect(ctx.Route("users"))
|
||||
}
|
||||
|
||||
// EditUser shows the form to edit a user.
|
||||
func (c *Controller) EditUser(ctx *core.Context, request *core.Request, response *core.Response) {
|
||||
user := ctx.LoggedUser()
|
||||
|
||||
|
@ -134,6 +139,7 @@ func (c *Controller) EditUser(ctx *core.Context, request *core.Request, response
|
|||
}))
|
||||
}
|
||||
|
||||
// UpdateUser validate and update a user.
|
||||
func (c *Controller) UpdateUser(ctx *core.Context, request *core.Request, response *core.Response) {
|
||||
user := ctx.LoggedUser()
|
||||
|
||||
|
@ -189,6 +195,7 @@ func (c *Controller) UpdateUser(ctx *core.Context, request *core.Request, respon
|
|||
response.Redirect(ctx.Route("users"))
|
||||
}
|
||||
|
||||
// RemoveUser deletes a user from the database.
|
||||
func (c *Controller) RemoveUser(ctx *core.Context, request *core.Request, response *core.Response) {
|
||||
user := ctx.LoggedUser()
|
||||
if !user.IsAdmin {
|
||||
|
@ -216,7 +223,7 @@ func (c *Controller) getUserFromURL(ctx *core.Context, request *core.Request, re
|
|||
return nil, err
|
||||
}
|
||||
|
||||
user, err := c.store.GetUserById(userID)
|
||||
user, err := c.store.UserByID(userID)
|
||||
if err != nil {
|
||||
response.HTML().ServerError(err)
|
||||
return nil, err
|
||||
|
|
|
@ -6,9 +6,10 @@ package filter
|
|||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"strings"
|
||||
|
||||
"github.com/miniflux/miniflux2/reader/url"
|
||||
"github.com/miniflux/miniflux2/server/route"
|
||||
"strings"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/gorilla/mux"
|
||||
|
@ -24,7 +25,7 @@ func ImageProxyFilter(r *mux.Router, data string) string {
|
|||
doc.Find("img").Each(func(i int, img *goquery.Selection) {
|
||||
if srcAttr, ok := img.Attr("src"); ok {
|
||||
if !url.IsHTTPS(srcAttr) {
|
||||
path := route.GetRoute(r, "proxy", "encodedURL", base64.StdEncoding.EncodeToString([]byte(srcAttr)))
|
||||
path := route.Path(r, "proxy", "encodedURL", base64.StdEncoding.EncodeToString([]byte(srcAttr)))
|
||||
img.SetAttr("src", path)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,23 +5,27 @@
|
|||
package form
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/miniflux/miniflux2/errors"
|
||||
)
|
||||
|
||||
// AuthForm represents the authentication form.
|
||||
type AuthForm struct {
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
// Validate makes sure the form values are valid.
|
||||
func (a AuthForm) Validate() error {
|
||||
if a.Username == "" || a.Password == "" {
|
||||
return errors.New("All fields are mandatory.")
|
||||
return errors.NewLocalizedError("All fields are mandatory.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewAuthForm returns a new AuthForm.
|
||||
func NewAuthForm(r *http.Request) *AuthForm {
|
||||
return &AuthForm{
|
||||
Username: r.FormValue("username"),
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
package form
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/miniflux/miniflux2/model"
|
||||
"net/http"
|
||||
|
||||
"github.com/miniflux/miniflux2/errors"
|
||||
"github.com/miniflux/miniflux2/model"
|
||||
)
|
||||
|
||||
// CategoryForm represents a feed form in the UI
|
||||
|
@ -15,18 +16,21 @@ type CategoryForm struct {
|
|||
Title string
|
||||
}
|
||||
|
||||
// Validate makes sure the form values are valid.
|
||||
func (c CategoryForm) Validate() error {
|
||||
if c.Title == "" {
|
||||
return errors.New("The title is mandatory.")
|
||||
return errors.NewLocalizedError("The title is mandatory.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Merge update the given category fields.
|
||||
func (c CategoryForm) Merge(category *model.Category) *model.Category {
|
||||
category.Title = c.Title
|
||||
return category
|
||||
}
|
||||
|
||||
// NewCategoryForm returns a new CategoryForm.
|
||||
func NewCategoryForm(r *http.Request) *CategoryForm {
|
||||
return &CategoryForm{
|
||||
Title: r.FormValue("title"),
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
package form
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/miniflux/miniflux2/model"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/miniflux/miniflux2/errors"
|
||||
"github.com/miniflux/miniflux2/model"
|
||||
)
|
||||
|
||||
// FeedForm represents a feed form in the UI
|
||||
|
@ -22,11 +23,12 @@ type FeedForm struct {
|
|||
// ValidateModification validates FeedForm fields
|
||||
func (f FeedForm) ValidateModification() error {
|
||||
if f.FeedURL == "" || f.SiteURL == "" || f.Title == "" || f.CategoryID == 0 {
|
||||
return errors.New("All fields are mandatory.")
|
||||
return errors.NewLocalizedError("All fields are mandatory.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Merge updates the fields of the given feed.
|
||||
func (f FeedForm) Merge(feed *model.Feed) *model.Feed {
|
||||
feed.Category.ID = f.CategoryID
|
||||
feed.Title = f.Title
|
||||
|
|
|
@ -5,11 +5,13 @@
|
|||
package form
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/miniflux/miniflux2/model"
|
||||
"net/http"
|
||||
|
||||
"github.com/miniflux/miniflux2/errors"
|
||||
"github.com/miniflux/miniflux2/model"
|
||||
)
|
||||
|
||||
// SettingsForm represents the settings form.
|
||||
type SettingsForm struct {
|
||||
Username string
|
||||
Password string
|
||||
|
@ -19,6 +21,7 @@ type SettingsForm struct {
|
|||
Timezone string
|
||||
}
|
||||
|
||||
// Merge updates the fields of the given user.
|
||||
func (s *SettingsForm) Merge(user *model.User) *model.User {
|
||||
user.Username = s.Username
|
||||
user.Theme = s.Theme
|
||||
|
@ -32,24 +35,26 @@ func (s *SettingsForm) Merge(user *model.User) *model.User {
|
|||
return user
|
||||
}
|
||||
|
||||
// Validate makes sure the form values are valid.
|
||||
func (s *SettingsForm) Validate() error {
|
||||
if s.Username == "" || s.Theme == "" || s.Language == "" || s.Timezone == "" {
|
||||
return errors.New("The username, theme, language and timezone fields are mandatory.")
|
||||
return errors.NewLocalizedError("The username, theme, language and timezone fields are mandatory.")
|
||||
}
|
||||
|
||||
if s.Password != "" {
|
||||
if s.Password != s.Confirmation {
|
||||
return errors.New("Passwords are not the same.")
|
||||
return errors.NewLocalizedError("Passwords are not the same.")
|
||||
}
|
||||
|
||||
if len(s.Password) < 6 {
|
||||
return errors.New("You must use at least 6 characters")
|
||||
return errors.NewLocalizedError("You must use at least 6 characters")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewSettingsForm returns a new SettingsForm.
|
||||
func NewSettingsForm(r *http.Request) *SettingsForm {
|
||||
return &SettingsForm{
|
||||
Username: r.FormValue("username"),
|
||||
|
|
|
@ -5,24 +5,28 @@
|
|||
package form
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/miniflux/miniflux2/errors"
|
||||
)
|
||||
|
||||
// SubscriptionForm represents the subscription form.
|
||||
type SubscriptionForm struct {
|
||||
URL string
|
||||
CategoryID int64
|
||||
}
|
||||
|
||||
// Validate makes sure the form values are valid.
|
||||
func (s *SubscriptionForm) Validate() error {
|
||||
if s.URL == "" || s.CategoryID == 0 {
|
||||
return errors.New("The URL and the category are mandatory.")
|
||||
return errors.NewLocalizedError("The URL and the category are mandatory.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewSubscriptionForm returns a new SubscriptionForm.
|
||||
func NewSubscriptionForm(r *http.Request) *SubscriptionForm {
|
||||
categoryID, err := strconv.Atoi(r.FormValue("category_id"))
|
||||
if err != nil {
|
||||
|
|
|
@ -5,11 +5,13 @@
|
|||
package form
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/miniflux/miniflux2/model"
|
||||
"net/http"
|
||||
|
||||
"github.com/miniflux/miniflux2/errors"
|
||||
"github.com/miniflux/miniflux2/model"
|
||||
)
|
||||
|
||||
// UserForm represents the user form.
|
||||
type UserForm struct {
|
||||
Username string
|
||||
Password string
|
||||
|
@ -17,40 +19,43 @@ type UserForm struct {
|
|||
IsAdmin bool
|
||||
}
|
||||
|
||||
// ValidateCreation validates user creation.
|
||||
func (u UserForm) ValidateCreation() error {
|
||||
if u.Username == "" || u.Password == "" || u.Confirmation == "" {
|
||||
return errors.New("All fields are mandatory.")
|
||||
return errors.NewLocalizedError("All fields are mandatory.")
|
||||
}
|
||||
|
||||
if u.Password != u.Confirmation {
|
||||
return errors.New("Passwords are not the same.")
|
||||
return errors.NewLocalizedError("Passwords are not the same.")
|
||||
}
|
||||
|
||||
if len(u.Password) < 6 {
|
||||
return errors.New("You must use at least 6 characters.")
|
||||
return errors.NewLocalizedError("You must use at least 6 characters.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateModification validates user modification.
|
||||
func (u UserForm) ValidateModification() error {
|
||||
if u.Username == "" {
|
||||
return errors.New("The username is mandatory.")
|
||||
return errors.NewLocalizedError("The username is mandatory.")
|
||||
}
|
||||
|
||||
if u.Password != "" {
|
||||
if u.Password != u.Confirmation {
|
||||
return errors.New("Passwords are not the same.")
|
||||
return errors.NewLocalizedError("Passwords are not the same.")
|
||||
}
|
||||
|
||||
if len(u.Password) < 6 {
|
||||
return errors.New("You must use at least 6 characters.")
|
||||
return errors.NewLocalizedError("You must use at least 6 characters.")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToUser returns a User from the form values.
|
||||
func (u UserForm) ToUser() *model.User {
|
||||
return &model.User{
|
||||
Username: u.Username,
|
||||
|
@ -59,6 +64,7 @@ func (u UserForm) ToUser() *model.User {
|
|||
}
|
||||
}
|
||||
|
||||
// Merge updates the fields of the given user.
|
||||
func (u UserForm) Merge(user *model.User) *model.User {
|
||||
user.Username = u.Username
|
||||
user.IsAdmin = u.IsAdmin
|
||||
|
@ -70,6 +76,7 @@ func (u UserForm) Merge(user *model.User) *model.User {
|
|||
return user
|
||||
}
|
||||
|
||||
// NewUserForm returns a new UserForm.
|
||||
func NewUserForm(r *http.Request) *UserForm {
|
||||
return &UserForm{
|
||||
Username: r.FormValue("username"),
|
||||
|
|
|
@ -7,10 +7,12 @@ package payload
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/miniflux/miniflux2/model"
|
||||
"io"
|
||||
|
||||
"github.com/miniflux/miniflux2/model"
|
||||
)
|
||||
|
||||
// DecodeEntryStatusPayload unserialize JSON request to update entry statuses.
|
||||
func DecodeEntryStatusPayload(data io.Reader) (entryIDs []int64, status string, err error) {
|
||||
type payload struct {
|
||||
EntryIDs []int64 `json:"entry_ids"`
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
// 2017-11-25 10:47:30.347837046 -0800 PST m=+0.003965152
|
||||
// 2017-11-27 21:07:53.208711992 -0800 PST m=+0.002898220
|
||||
|
||||
package sql
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/miniflux/miniflux2/model"
|
||||
)
|
||||
|
||||
// AnotherCategoryExists checks if another category exists with the same title.
|
||||
func (s *Storage) AnotherCategoryExists(userID, categoryID int64, title string) bool {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:AnotherCategoryExists] userID=%d, categoryID=%d, title=%s", userID, categoryID, title))
|
||||
|
||||
|
@ -23,6 +24,7 @@ func (s *Storage) AnotherCategoryExists(userID, categoryID int64, title string)
|
|||
return result >= 1
|
||||
}
|
||||
|
||||
// CategoryExists checks if the given category exists into the database.
|
||||
func (s *Storage) CategoryExists(userID, categoryID int64) bool {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:CategoryExists] userID=%d, categoryID=%d", userID, categoryID))
|
||||
|
||||
|
@ -32,8 +34,9 @@ func (s *Storage) CategoryExists(userID, categoryID int64) bool {
|
|||
return result >= 1
|
||||
}
|
||||
|
||||
func (s *Storage) GetCategory(userID, categoryID int64) (*model.Category, error) {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:GetCategory] userID=%d, getCategory=%d", userID, categoryID))
|
||||
// Category returns a category from the database.
|
||||
func (s *Storage) Category(userID, categoryID int64) (*model.Category, error) {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:Category] userID=%d, getCategory=%d", userID, categoryID))
|
||||
var category model.Category
|
||||
|
||||
query := `SELECT id, user_id, title FROM categories WHERE user_id=$1 AND id=$2`
|
||||
|
@ -41,29 +44,31 @@ func (s *Storage) GetCategory(userID, categoryID int64) (*model.Category, error)
|
|||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("Unable to fetch category: %v", err)
|
||||
return nil, fmt.Errorf("unable to fetch category: %v", err)
|
||||
}
|
||||
|
||||
return &category, nil
|
||||
}
|
||||
|
||||
func (s *Storage) GetFirstCategory(userID int64) (*model.Category, error) {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:GetFirstCategory] userID=%d", userID))
|
||||
// FirstCategory returns the first category for the given user.
|
||||
func (s *Storage) FirstCategory(userID int64) (*model.Category, error) {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:FirstCategory] userID=%d", userID))
|
||||
var category model.Category
|
||||
|
||||
query := `SELECT id, user_id, title FROM categories WHERE user_id=$1 ORDER BY title ASC`
|
||||
query := `SELECT id, user_id, title FROM categories WHERE user_id=$1 ORDER BY title ASC LIMIT 1`
|
||||
err := s.db.QueryRow(query, userID).Scan(&category.ID, &category.UserID, &category.Title)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("Unable to fetch category: %v", err)
|
||||
return nil, fmt.Errorf("unable to fetch category: %v", err)
|
||||
}
|
||||
|
||||
return &category, nil
|
||||
}
|
||||
|
||||
func (s *Storage) GetCategoryByTitle(userID int64, title string) (*model.Category, error) {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:GetCategoryByTitle] userID=%d, title=%s", userID, title))
|
||||
// CategoryByTitle finds a category by the title.
|
||||
func (s *Storage) CategoryByTitle(userID int64, title string) (*model.Category, error) {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:CategoryByTitle] userID=%d, title=%s", userID, title))
|
||||
var category model.Category
|
||||
|
||||
query := `SELECT id, user_id, title FROM categories WHERE user_id=$1 AND title=$2`
|
||||
|
@ -77,10 +82,11 @@ func (s *Storage) GetCategoryByTitle(userID int64, title string) (*model.Categor
|
|||
return &category, nil
|
||||
}
|
||||
|
||||
func (s *Storage) GetCategories(userID int64) (model.Categories, error) {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:GetCategories] userID=%d", userID))
|
||||
// Categories returns all categories that belongs to the given user.
|
||||
func (s *Storage) Categories(userID int64) (model.Categories, error) {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:Categories] userID=%d", userID))
|
||||
|
||||
query := `SELECT id, user_id, title FROM categories WHERE user_id=$1`
|
||||
query := `SELECT id, user_id, title FROM categories WHERE user_id=$1 ORDER BY title ASC`
|
||||
rows, err := s.db.Query(query, userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to fetch categories: %v", err)
|
||||
|
@ -100,8 +106,9 @@ func (s *Storage) GetCategories(userID int64) (model.Categories, error) {
|
|||
return categories, nil
|
||||
}
|
||||
|
||||
func (s *Storage) GetCategoriesWithFeedCount(userID int64) (model.Categories, error) {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:GetCategoriesWithFeedCount] userID=%d", userID))
|
||||
// CategoriesWithFeedCount returns all categories with the number of feeds.
|
||||
func (s *Storage) CategoriesWithFeedCount(userID int64) (model.Categories, error) {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:CategoriesWithFeedCount] userID=%d", userID))
|
||||
query := `SELECT
|
||||
c.id, c.user_id, c.title,
|
||||
(SELECT count(*) FROM feeds WHERE feeds.category_id=c.id) AS count
|
||||
|
@ -126,6 +133,7 @@ func (s *Storage) GetCategoriesWithFeedCount(userID int64) (model.Categories, er
|
|||
return categories, nil
|
||||
}
|
||||
|
||||
// CreateCategory creates a new category.
|
||||
func (s *Storage) CreateCategory(category *model.Category) error {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:CreateCategory] title=%s", category.Title))
|
||||
|
||||
|
@ -149,6 +157,7 @@ func (s *Storage) CreateCategory(category *model.Category) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// UpdateCategory updates an existing category.
|
||||
func (s *Storage) UpdateCategory(category *model.Category) error {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:UpdateCategory] categoryID=%d", category.ID))
|
||||
|
||||
|
@ -167,6 +176,7 @@ func (s *Storage) UpdateCategory(category *model.Category) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// RemoveCategory deletes a category.
|
||||
func (s *Storage) RemoveCategory(userID, categoryID int64) error {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:RemoveCategory] userID=%d, categoryID=%d", userID, categoryID))
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/miniflux/miniflux2/model"
|
||||
)
|
||||
|
||||
// FeedExists checks if the given feed exists.
|
||||
func (s *Storage) FeedExists(userID, feedID int64) bool {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:FeedExists] userID=%d, feedID=%d", userID, feedID))
|
||||
|
||||
|
@ -23,6 +24,7 @@ func (s *Storage) FeedExists(userID, feedID int64) bool {
|
|||
return result >= 1
|
||||
}
|
||||
|
||||
// FeedURLExists checks if feed URL already exists.
|
||||
func (s *Storage) FeedURLExists(userID int64, feedURL string) bool {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:FeedURLExists] userID=%d, feedURL=%s", userID, feedURL))
|
||||
|
||||
|
@ -43,8 +45,9 @@ func (s *Storage) CountFeeds(userID int64) int {
|
|||
return result
|
||||
}
|
||||
|
||||
func (s *Storage) GetFeeds(userID int64) (model.Feeds, error) {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:GetFeeds] userID=%d", userID))
|
||||
// Feeds returns all feeds of the given user.
|
||||
func (s *Storage) Feeds(userID int64) (model.Feeds, error) {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:Feeds] userID=%d", userID))
|
||||
|
||||
feeds := make(model.Feeds, 0)
|
||||
query := `SELECT
|
||||
|
@ -109,8 +112,9 @@ func (s *Storage) GetFeeds(userID int64) (model.Feeds, error) {
|
|||
return feeds, nil
|
||||
}
|
||||
|
||||
func (s *Storage) GetFeedById(userID, feedID int64) (*model.Feed, error) {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:GetFeedById] feedID=%d", feedID))
|
||||
// FeedByID returns a feed by the ID.
|
||||
func (s *Storage) FeedByID(userID, feedID int64) (*model.Feed, error) {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:FeedByID] feedID=%d", feedID))
|
||||
|
||||
var feed model.Feed
|
||||
feed.Category = &model.Category{UserID: userID}
|
||||
|
@ -149,6 +153,7 @@ func (s *Storage) GetFeedById(userID, feedID int64) (*model.Feed, error) {
|
|||
return &feed, nil
|
||||
}
|
||||
|
||||
// CreateFeed creates a new feed.
|
||||
func (s *Storage) CreateFeed(feed *model.Feed) error {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:CreateFeed] feedURL=%s", feed.FeedURL))
|
||||
sql := `
|
||||
|
@ -184,6 +189,7 @@ func (s *Storage) CreateFeed(feed *model.Feed) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// UpdateFeed updates an existing feed.
|
||||
func (s *Storage) UpdateFeed(feed *model.Feed) (err error) {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:UpdateFeed] feedURL=%s", feed.FeedURL))
|
||||
|
||||
|
@ -213,6 +219,7 @@ func (s *Storage) UpdateFeed(feed *model.Feed) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
// RemoveFeed removes a feed.
|
||||
func (s *Storage) RemoveFeed(userID, feedID int64) error {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:RemoveFeed] userID=%d, feedID=%d", userID, feedID))
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/miniflux/miniflux2/model"
|
||||
)
|
||||
|
||||
// HasIcon checks if the given feed has an icon.
|
||||
func (s *Storage) HasIcon(feedID int64) bool {
|
||||
var result int
|
||||
query := `SELECT count(*) as c FROM feed_icons WHERE feed_id=$1`
|
||||
|
@ -21,8 +22,9 @@ func (s *Storage) HasIcon(feedID int64) bool {
|
|||
return result == 1
|
||||
}
|
||||
|
||||
func (s *Storage) GetIconByID(iconID int64) (*model.Icon, error) {
|
||||
defer helper.ExecutionTime(time.Now(), "[Storage:GetIconByID]")
|
||||
// IconByID returns an icon by the ID.
|
||||
func (s *Storage) IconByID(iconID int64) (*model.Icon, error) {
|
||||
defer helper.ExecutionTime(time.Now(), "[Storage:IconByID]")
|
||||
|
||||
var icon model.Icon
|
||||
query := `SELECT id, hash, mime_type, content FROM icons WHERE id=$1`
|
||||
|
@ -36,8 +38,9 @@ func (s *Storage) GetIconByID(iconID int64) (*model.Icon, error) {
|
|||
return &icon, nil
|
||||
}
|
||||
|
||||
func (s *Storage) GetIconByHash(icon *model.Icon) error {
|
||||
defer helper.ExecutionTime(time.Now(), "[Storage:GetIconByHash]")
|
||||
// IconByHash returns an icon by the hash (checksum).
|
||||
func (s *Storage) IconByHash(icon *model.Icon) error {
|
||||
defer helper.ExecutionTime(time.Now(), "[Storage:IconByHash]")
|
||||
|
||||
err := s.db.QueryRow(`SELECT id FROM icons WHERE hash=$1`, icon.Hash).Scan(&icon.ID)
|
||||
if err == sql.ErrNoRows {
|
||||
|
@ -49,6 +52,7 @@ func (s *Storage) GetIconByHash(icon *model.Icon) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// CreateIcon creates a new icon.
|
||||
func (s *Storage) CreateIcon(icon *model.Icon) error {
|
||||
defer helper.ExecutionTime(time.Now(), "[Storage:CreateIcon]")
|
||||
|
||||
|
@ -73,10 +77,11 @@ func (s *Storage) CreateIcon(icon *model.Icon) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// CreateFeedIcon creates an icon and associate the icon to the given feed.
|
||||
func (s *Storage) CreateFeedIcon(feed *model.Feed, icon *model.Icon) error {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:CreateFeedIcon] feedID=%d", feed.ID))
|
||||
|
||||
err := s.GetIconByHash(icon)
|
||||
err := s.IconByHash(icon)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -7,11 +7,13 @@ package storage
|
|||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"github.com/miniflux/miniflux2/helper"
|
||||
"github.com/miniflux/miniflux2/model"
|
||||
)
|
||||
|
||||
func (s *Storage) GetSessions(userID int64) (model.Sessions, error) {
|
||||
// Sessions returns the list of sessions for the given user.
|
||||
func (s *Storage) Sessions(userID int64) (model.Sessions, error) {
|
||||
query := `SELECT id, user_id, token, created_at, user_agent, ip FROM sessions WHERE user_id=$1 ORDER BY id DESC`
|
||||
rows, err := s.db.Query(query, userID)
|
||||
if err != nil {
|
||||
|
@ -41,6 +43,7 @@ func (s *Storage) GetSessions(userID int64) (model.Sessions, error) {
|
|||
return sessions, nil
|
||||
}
|
||||
|
||||
// CreateSession creates a new sessions.
|
||||
func (s *Storage) CreateSession(username, userAgent, ip string) (sessionID string, err error) {
|
||||
var userID int64
|
||||
|
||||
|
@ -61,7 +64,8 @@ func (s *Storage) CreateSession(username, userAgent, ip string) (sessionID strin
|
|||
return token, nil
|
||||
}
|
||||
|
||||
func (s *Storage) GetSessionByToken(token string) (*model.Session, error) {
|
||||
// SessionByToken finds a session by the token.
|
||||
func (s *Storage) SessionByToken(token string) (*model.Session, error) {
|
||||
var session model.Session
|
||||
|
||||
query := "SELECT id, user_id, token, created_at, user_agent, ip FROM sessions WHERE token = $1"
|
||||
|
@ -83,6 +87,7 @@ func (s *Storage) GetSessionByToken(token string) (*model.Session, error) {
|
|||
return &session, nil
|
||||
}
|
||||
|
||||
// RemoveSessionByToken remove a session by using the token.
|
||||
func (s *Storage) RemoveSessionByToken(userID int64, token string) error {
|
||||
result, err := s.db.Exec(`DELETE FROM sessions WHERE user_id=$1 AND token=$2`, userID, token)
|
||||
if err != nil {
|
||||
|
@ -101,6 +106,7 @@ func (s *Storage) RemoveSessionByToken(userID int64, token string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// RemoveSessionByID remove a session by using the ID.
|
||||
func (s *Storage) RemoveSessionByID(userID, sessionID int64) error {
|
||||
result, err := s.db.Exec(`DELETE FROM sessions WHERE user_id=$1 AND id=$2`, userID, sessionID)
|
||||
if err != nil {
|
||||
|
@ -119,7 +125,8 @@ func (s *Storage) RemoveSessionByID(userID, sessionID int64) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// FlushAllSessions removes all sessions from the database.
|
||||
func (s *Storage) FlushAllSessions() (err error) {
|
||||
_, err = s.db.Exec(`delete from sessions`)
|
||||
_, err = s.db.Exec(`DELETE FROM sessions`)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -8,19 +8,23 @@ import (
|
|||
"database/sql"
|
||||
"log"
|
||||
|
||||
// Postgresql driver import
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
// Storage handles all operations related to the database.
|
||||
type Storage struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
// Close closes all database connections.
|
||||
func (s *Storage) Close() {
|
||||
s.db.Close()
|
||||
}
|
||||
|
||||
func NewStorage(databaseUrl string, maxOpenConns int) *Storage {
|
||||
db, err := sql.Open("postgres", databaseUrl)
|
||||
// NewStorage returns a new Storage.
|
||||
func NewStorage(databaseURL string, maxOpenConns int) *Storage {
|
||||
db, err := sql.Open("postgres", databaseURL)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to connect to the database: %v", err)
|
||||
}
|
||||
|
|
|
@ -6,12 +6,14 @@ package storage
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/miniflux/miniflux2/helper"
|
||||
"time"
|
||||
|
||||
"github.com/miniflux/miniflux2/helper"
|
||||
)
|
||||
|
||||
func (s *Storage) GetTimezones() (map[string]string, error) {
|
||||
defer helper.ExecutionTime(time.Now(), "[Storage:GetTimezones]")
|
||||
// Timezones returns all timezones supported by the database.
|
||||
func (s *Storage) Timezones() (map[string]string, error) {
|
||||
defer helper.ExecutionTime(time.Now(), "[Storage:Timezones]")
|
||||
|
||||
timezones := make(map[string]string)
|
||||
query := `select name from pg_timezone_names() order by name asc`
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// SetLastLogin updates the last login date of a user.
|
||||
func (s *Storage) SetLastLogin(userID int64) error {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:SetLastLogin] userID=%d", userID))
|
||||
query := "UPDATE users SET last_login_at=now() WHERE id=$1"
|
||||
|
@ -30,6 +31,7 @@ func (s *Storage) SetLastLogin(userID int64) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// UserExists checks if a user exists by using the given username.
|
||||
func (s *Storage) UserExists(username string) bool {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:UserExists] username=%s", username))
|
||||
|
||||
|
@ -38,6 +40,7 @@ func (s *Storage) UserExists(username string) bool {
|
|||
return result >= 1
|
||||
}
|
||||
|
||||
// AnotherUserExists checks if another user exists with the given username.
|
||||
func (s *Storage) AnotherUserExists(userID int64, username string) bool {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:AnotherUserExists] userID=%d, username=%s", userID, username))
|
||||
|
||||
|
@ -46,6 +49,7 @@ func (s *Storage) AnotherUserExists(userID int64, username string) bool {
|
|||
return result >= 1
|
||||
}
|
||||
|
||||
// CreateUser creates a new user.
|
||||
func (s *Storage) CreateUser(user *model.User) (err error) {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:CreateUser] username=%s", user.Username))
|
||||
password := ""
|
||||
|
@ -84,6 +88,7 @@ func (s *Storage) CreateUser(user *model.User) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
// UpdateExtraField updates an extra field of the given user.
|
||||
func (s *Storage) UpdateExtraField(userID int64, field, value string) error {
|
||||
query := fmt.Sprintf(`UPDATE users SET extra = hstore('%s', $1) WHERE id=$2`, field)
|
||||
_, err := s.db.Exec(query, value, userID)
|
||||
|
@ -93,6 +98,7 @@ func (s *Storage) UpdateExtraField(userID int64, field, value string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// RemoveExtraField deletes an extra field for the given user.
|
||||
func (s *Storage) RemoveExtraField(userID int64, field string) error {
|
||||
query := `UPDATE users SET extra = delete(extra, $1) WHERE id=$2`
|
||||
_, err := s.db.Exec(query, field, userID)
|
||||
|
@ -102,6 +108,7 @@ func (s *Storage) RemoveExtraField(userID int64, field string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// UpdateUser updates a user.
|
||||
func (s *Storage) UpdateUser(user *model.User) error {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:UpdateUser] username=%s", user.Username))
|
||||
user.Username = strings.ToLower(user.Username)
|
||||
|
@ -128,8 +135,9 @@ func (s *Storage) UpdateUser(user *model.User) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Storage) GetUserById(userID int64) (*model.User, error) {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:GetUserById] userID=%d", userID))
|
||||
// UserByID finds a user by the ID.
|
||||
func (s *Storage) UserByID(userID int64) (*model.User, error) {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:UserByID] userID=%d", userID))
|
||||
|
||||
var user model.User
|
||||
var extra hstore.Hstore
|
||||
|
@ -151,8 +159,9 @@ func (s *Storage) GetUserById(userID int64) (*model.User, error) {
|
|||
return &user, nil
|
||||
}
|
||||
|
||||
func (s *Storage) GetUserByUsername(username string) (*model.User, error) {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:GetUserByUsername] username=%s", username))
|
||||
// UserByUsername finds a user by the username.
|
||||
func (s *Storage) UserByUsername(username string) (*model.User, error) {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:UserByUsername] username=%s", username))
|
||||
|
||||
var user model.User
|
||||
row := s.db.QueryRow("SELECT id, username, is_admin, theme, language, timezone FROM users WHERE username=$1", username)
|
||||
|
@ -166,8 +175,9 @@ func (s *Storage) GetUserByUsername(username string) (*model.User, error) {
|
|||
return &user, nil
|
||||
}
|
||||
|
||||
func (s *Storage) GetUserByExtraField(field, value string) (*model.User, error) {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:GetUserByExtraField] field=%s", field))
|
||||
// UserByExtraField finds a user by an extra field value.
|
||||
func (s *Storage) UserByExtraField(field, value string) (*model.User, error) {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:UserByExtraField] field=%s", field))
|
||||
var user model.User
|
||||
query := `SELECT id, username, is_admin, theme, language, timezone FROM users WHERE extra->$1=$2`
|
||||
row := s.db.QueryRow(query, field, value)
|
||||
|
@ -181,6 +191,7 @@ func (s *Storage) GetUserByExtraField(field, value string) (*model.User, error)
|
|||
return &user, nil
|
||||
}
|
||||
|
||||
// RemoveUser deletes a user.
|
||||
func (s *Storage) RemoveUser(userID int64) error {
|
||||
defer helper.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:RemoveUser] userID=%d", userID))
|
||||
|
||||
|
@ -195,14 +206,15 @@ func (s *Storage) RemoveUser(userID int64) error {
|
|||
}
|
||||
|
||||
if count == 0 {
|
||||
return errors.New("nothing has been removed.")
|
||||
return errors.New("nothing has been removed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Storage) GetUsers() (model.Users, error) {
|
||||
defer helper.ExecutionTime(time.Now(), "[Storage:GetUsers]")
|
||||
// Users returns all users.
|
||||
func (s *Storage) Users() (model.Users, error) {
|
||||
defer helper.ExecutionTime(time.Now(), "[Storage:Users]")
|
||||
|
||||
var users model.Users
|
||||
rows, err := s.db.Query("SELECT id, username, is_admin, theme, language, timezone, last_login_at FROM users ORDER BY username ASC")
|
||||
|
@ -233,6 +245,7 @@ func (s *Storage) GetUsers() (model.Users, error) {
|
|||
return users, nil
|
||||
}
|
||||
|
||||
// CheckPassword validate the hashed password.
|
||||
func (s *Storage) CheckPassword(username, password string) error {
|
||||
defer helper.ExecutionTime(time.Now(), "[Storage:CheckPassword]")
|
||||
|
||||
|
@ -241,13 +254,13 @@ func (s *Storage) CheckPassword(username, password string) error {
|
|||
|
||||
err := s.db.QueryRow("SELECT password FROM users WHERE username=$1", username).Scan(&hash)
|
||||
if err == sql.ErrNoRows {
|
||||
return fmt.Errorf("Unable to find this user: %s\n", username)
|
||||
return fmt.Errorf("unable to find this user: %s", username)
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("Unable to fetch user: %v\n", err)
|
||||
return fmt.Errorf("unable to fetch user: %v", err)
|
||||
}
|
||||
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)); err != nil {
|
||||
return fmt.Errorf("Invalid password for %s\n", username)
|
||||
return fmt.Errorf("invalid password for %s", username)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
Loading…
Reference in a new issue