mirror of
https://github.com/miniflux/v2.git
synced 2025-06-27 16:36:00 +00:00
Support localized feed errors generated by background workers
This commit is contained in:
parent
9694861cb6
commit
953d0a2dc0
18 changed files with 59 additions and 65 deletions
|
@ -12,6 +12,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/miniflux/miniflux/config"
|
"github.com/miniflux/miniflux/config"
|
||||||
|
"github.com/miniflux/miniflux/locale"
|
||||||
"github.com/miniflux/miniflux/logger"
|
"github.com/miniflux/miniflux/logger"
|
||||||
"github.com/miniflux/miniflux/reader/feed"
|
"github.com/miniflux/miniflux/reader/feed"
|
||||||
"github.com/miniflux/miniflux/scheduler"
|
"github.com/miniflux/miniflux/scheduler"
|
||||||
|
@ -26,9 +27,10 @@ func Run(cfg *config.Config, store *storage.Storage) {
|
||||||
signal.Notify(stop, os.Interrupt)
|
signal.Notify(stop, os.Interrupt)
|
||||||
signal.Notify(stop, syscall.SIGTERM)
|
signal.Notify(stop, syscall.SIGTERM)
|
||||||
|
|
||||||
feedHandler := feed.NewFeedHandler(store)
|
translator := locale.Load()
|
||||||
|
feedHandler := feed.NewFeedHandler(store, translator)
|
||||||
pool := scheduler.NewWorkerPool(feedHandler, cfg.WorkerPoolSize())
|
pool := scheduler.NewWorkerPool(feedHandler, cfg.WorkerPoolSize())
|
||||||
server := newServer(cfg, store, pool, feedHandler)
|
server := newServer(cfg, store, pool, feedHandler, translator)
|
||||||
|
|
||||||
scheduler.NewFeedScheduler(
|
scheduler.NewFeedScheduler(
|
||||||
store,
|
store,
|
||||||
|
|
|
@ -23,9 +23,8 @@ import (
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
func routes(cfg *config.Config, store *storage.Storage, feedHandler *feed.Handler, pool *scheduler.WorkerPool) *mux.Router {
|
func routes(cfg *config.Config, store *storage.Storage, feedHandler *feed.Handler, pool *scheduler.WorkerPool, translator *locale.Translator) *mux.Router {
|
||||||
router := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
translator := locale.Load()
|
|
||||||
templateEngine := template.NewEngine(cfg, router, translator)
|
templateEngine := template.NewEngine(cfg, router, translator)
|
||||||
|
|
||||||
apiController := api.NewController(store, feedHandler)
|
apiController := api.NewController(store, feedHandler)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/miniflux/miniflux/config"
|
"github.com/miniflux/miniflux/config"
|
||||||
|
"github.com/miniflux/miniflux/locale"
|
||||||
"github.com/miniflux/miniflux/logger"
|
"github.com/miniflux/miniflux/logger"
|
||||||
"github.com/miniflux/miniflux/reader/feed"
|
"github.com/miniflux/miniflux/reader/feed"
|
||||||
"github.com/miniflux/miniflux/scheduler"
|
"github.com/miniflux/miniflux/scheduler"
|
||||||
|
@ -18,7 +19,7 @@ import (
|
||||||
"golang.org/x/crypto/acme/autocert"
|
"golang.org/x/crypto/acme/autocert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newServer(cfg *config.Config, store *storage.Storage, pool *scheduler.WorkerPool, feedHandler *feed.Handler) *http.Server {
|
func newServer(cfg *config.Config, store *storage.Storage, pool *scheduler.WorkerPool, feedHandler *feed.Handler, translator *locale.Translator) *http.Server {
|
||||||
certFile := cfg.CertFile()
|
certFile := cfg.CertFile()
|
||||||
keyFile := cfg.KeyFile()
|
keyFile := cfg.KeyFile()
|
||||||
certDomain := cfg.CertDomain()
|
certDomain := cfg.CertDomain()
|
||||||
|
@ -28,7 +29,7 @@ func newServer(cfg *config.Config, store *storage.Storage, pool *scheduler.Worke
|
||||||
WriteTimeout: 10 * time.Second,
|
WriteTimeout: 10 * time.Second,
|
||||||
IdleTimeout: 60 * time.Second,
|
IdleTimeout: 60 * time.Second,
|
||||||
Addr: cfg.ListenAddr(),
|
Addr: cfg.ListenAddr(),
|
||||||
Handler: routes(cfg, store, feedHandler, pool),
|
Handler: routes(cfg, store, feedHandler, pool, translator),
|
||||||
}
|
}
|
||||||
|
|
||||||
if certDomain != "" && certCache != "" {
|
if certDomain != "" && certCache != "" {
|
||||||
|
|
|
@ -27,6 +27,6 @@ func (l LocalizedError) Localize(translation *locale.Language) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLocalizedError returns a new LocalizedError.
|
// NewLocalizedError returns a new LocalizedError.
|
||||||
func NewLocalizedError(message string, args ...interface{}) LocalizedError {
|
func NewLocalizedError(message string, args ...interface{}) *LocalizedError {
|
||||||
return LocalizedError{message: message, args: args}
|
return &LocalizedError{message: message, args: args}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Parse returns a normalized feed struct from a Atom feed.
|
// Parse returns a normalized feed struct from a Atom feed.
|
||||||
func Parse(data io.Reader) (*model.Feed, error) {
|
func Parse(data io.Reader) (*model.Feed, *errors.LocalizedError) {
|
||||||
atomFeed := new(atomFeed)
|
atomFeed := new(atomFeed)
|
||||||
decoder := xml.NewDecoder(data)
|
decoder := xml.NewDecoder(data)
|
||||||
decoder.CharsetReader = encoding.CharsetReader
|
decoder.CharsetReader = encoding.CharsetReader
|
||||||
|
|
|
@ -8,8 +8,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/miniflux/miniflux/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParseAtomSample(t *testing.T) {
|
func TestParseAtomSample(t *testing.T) {
|
||||||
|
@ -430,8 +428,4 @@ func TestParseInvalidXml(t *testing.T) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("Parse should returns an error")
|
t.Error("Parse should returns an error")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := err.(errors.LocalizedError); !ok {
|
|
||||||
t.Error("The error returned must be a LocalizedError")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
"github.com/miniflux/miniflux/errors"
|
"github.com/miniflux/miniflux/errors"
|
||||||
"github.com/miniflux/miniflux/http"
|
"github.com/miniflux/miniflux/http"
|
||||||
|
"github.com/miniflux/miniflux/locale"
|
||||||
"github.com/miniflux/miniflux/logger"
|
"github.com/miniflux/miniflux/logger"
|
||||||
"github.com/miniflux/miniflux/model"
|
"github.com/miniflux/miniflux/model"
|
||||||
"github.com/miniflux/miniflux/reader/icon"
|
"github.com/miniflux/miniflux/reader/icon"
|
||||||
|
@ -30,7 +31,8 @@ var (
|
||||||
|
|
||||||
// Handler contains all the logic to create and refresh feeds.
|
// Handler contains all the logic to create and refresh feeds.
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
store *storage.Storage
|
store *storage.Storage
|
||||||
|
translator *locale.Translator
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateFeed fetch, parse and store a new feed.
|
// CreateFeed fetch, parse and store a new feed.
|
||||||
|
@ -44,7 +46,7 @@ func (h *Handler) CreateFeed(userID, categoryID int64, url string, crawler bool)
|
||||||
client := http.NewClient(url)
|
client := http.NewClient(url)
|
||||||
response, err := client.Get()
|
response, err := client.Get()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(errors.LocalizedError); ok {
|
if _, ok := err.(*errors.LocalizedError); ok {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return nil, errors.NewLocalizedError(errRequestFailed, err)
|
return nil, errors.NewLocalizedError(errRequestFailed, err)
|
||||||
|
@ -68,9 +70,9 @@ func (h *Handler) CreateFeed(userID, categoryID int64, url string, crawler bool)
|
||||||
return nil, errors.NewLocalizedError(errEncoding, err)
|
return nil, errors.NewLocalizedError(errEncoding, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
subscription, err := parseFeed(body)
|
subscription, feedErr := parseFeed(body)
|
||||||
if err != nil {
|
if feedErr != nil {
|
||||||
return nil, err
|
return nil, feedErr
|
||||||
}
|
}
|
||||||
|
|
||||||
feedProcessor := processor.NewFeedProcessor(userID, h.store, subscription)
|
feedProcessor := processor.NewFeedProcessor(userID, h.store, subscription)
|
||||||
|
@ -110,6 +112,13 @@ func (h *Handler) CreateFeed(userID, categoryID int64, url string, crawler bool)
|
||||||
// RefreshFeed fetch and update a feed if necessary.
|
// RefreshFeed fetch and update a feed if necessary.
|
||||||
func (h *Handler) RefreshFeed(userID, feedID int64) error {
|
func (h *Handler) RefreshFeed(userID, feedID int64) error {
|
||||||
defer timer.ExecutionTime(time.Now(), fmt.Sprintf("[Handler:RefreshFeed] feedID=%d", feedID))
|
defer timer.ExecutionTime(time.Now(), fmt.Sprintf("[Handler:RefreshFeed] feedID=%d", feedID))
|
||||||
|
userLanguage, err := h.store.UserLanguage(userID)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("[Handler:RefreshFeed] %v", err)
|
||||||
|
userLanguage = "en_US"
|
||||||
|
}
|
||||||
|
|
||||||
|
currentLanguage := h.translator.GetLanguage(userLanguage)
|
||||||
|
|
||||||
originalFeed, err := h.store.FeedByID(userID, feedID)
|
originalFeed, err := h.store.FeedByID(userID, feedID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -124,14 +133,14 @@ func (h *Handler) RefreshFeed(userID, feedID int64) error {
|
||||||
response, err := client.Get()
|
response, err := client.Get()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var customErr errors.LocalizedError
|
var customErr errors.LocalizedError
|
||||||
if lerr, ok := err.(errors.LocalizedError); ok {
|
if lerr, ok := err.(*errors.LocalizedError); ok {
|
||||||
customErr = lerr
|
customErr = *lerr
|
||||||
} else {
|
} else {
|
||||||
customErr = errors.NewLocalizedError(errRequestFailed, err)
|
customErr = *errors.NewLocalizedError(errRequestFailed, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
originalFeed.ParsingErrorCount++
|
originalFeed.ParsingErrorCount++
|
||||||
originalFeed.ParsingErrorMsg = customErr.Error()
|
originalFeed.ParsingErrorMsg = customErr.Localize(currentLanguage)
|
||||||
h.store.UpdateFeed(originalFeed)
|
h.store.UpdateFeed(originalFeed)
|
||||||
return customErr
|
return customErr
|
||||||
}
|
}
|
||||||
|
@ -141,7 +150,7 @@ func (h *Handler) RefreshFeed(userID, feedID int64) error {
|
||||||
if response.HasServerFailure() {
|
if response.HasServerFailure() {
|
||||||
err := errors.NewLocalizedError(errServerFailure, response.StatusCode)
|
err := errors.NewLocalizedError(errServerFailure, response.StatusCode)
|
||||||
originalFeed.ParsingErrorCount++
|
originalFeed.ParsingErrorCount++
|
||||||
originalFeed.ParsingErrorMsg = err.Error()
|
originalFeed.ParsingErrorMsg = err.Localize(currentLanguage)
|
||||||
h.store.UpdateFeed(originalFeed)
|
h.store.UpdateFeed(originalFeed)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -153,7 +162,7 @@ func (h *Handler) RefreshFeed(userID, feedID int64) error {
|
||||||
if response.ContentLength == 0 {
|
if response.ContentLength == 0 {
|
||||||
err := errors.NewLocalizedError(errEmptyFeed)
|
err := errors.NewLocalizedError(errEmptyFeed)
|
||||||
originalFeed.ParsingErrorCount++
|
originalFeed.ParsingErrorCount++
|
||||||
originalFeed.ParsingErrorMsg = err.Error()
|
originalFeed.ParsingErrorMsg = err.Localize(currentLanguage)
|
||||||
h.store.UpdateFeed(originalFeed)
|
h.store.UpdateFeed(originalFeed)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -163,10 +172,10 @@ func (h *Handler) RefreshFeed(userID, feedID int64) error {
|
||||||
return errors.NewLocalizedError(errEncoding, err)
|
return errors.NewLocalizedError(errEncoding, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
subscription, err := parseFeed(body)
|
subscription, parseErr := parseFeed(body)
|
||||||
if err != nil {
|
if parseErr != nil {
|
||||||
originalFeed.ParsingErrorCount++
|
originalFeed.ParsingErrorCount++
|
||||||
originalFeed.ParsingErrorMsg = err.Error()
|
originalFeed.ParsingErrorMsg = parseErr.Localize(currentLanguage)
|
||||||
h.store.UpdateFeed(originalFeed)
|
h.store.UpdateFeed(originalFeed)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -209,6 +218,6 @@ func (h *Handler) RefreshFeed(userID, feedID int64) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFeedHandler returns a feed handler.
|
// NewFeedHandler returns a feed handler.
|
||||||
func NewFeedHandler(store *storage.Storage) *Handler {
|
func NewFeedHandler(store *storage.Storage, translator *locale.Translator) *Handler {
|
||||||
return &Handler{store: store}
|
return &Handler{store, translator}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,11 @@ package feed
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"errors"
|
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/miniflux/miniflux/errors"
|
||||||
"github.com/miniflux/miniflux/logger"
|
"github.com/miniflux/miniflux/logger"
|
||||||
"github.com/miniflux/miniflux/model"
|
"github.com/miniflux/miniflux/model"
|
||||||
"github.com/miniflux/miniflux/reader/atom"
|
"github.com/miniflux/miniflux/reader/atom"
|
||||||
|
@ -66,13 +66,13 @@ func DetectFeedFormat(r io.Reader) string {
|
||||||
return FormatUnknown
|
return FormatUnknown
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseFeed(r io.Reader) (*model.Feed, error) {
|
func parseFeed(r io.Reader) (*model.Feed, *errors.LocalizedError) {
|
||||||
defer timer.ExecutionTime(time.Now(), "[Feed:ParseFeed]")
|
defer timer.ExecutionTime(time.Now(), "[Feed:ParseFeed]")
|
||||||
|
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
size, _ := io.Copy(&buffer, r)
|
size, _ := io.Copy(&buffer, r)
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return nil, errors.New("This feed is empty")
|
return nil, errors.NewLocalizedError("This feed is empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
str := stripInvalidXMLCharacters(buffer.String())
|
str := stripInvalidXMLCharacters(buffer.String())
|
||||||
|
@ -90,7 +90,7 @@ func parseFeed(r io.Reader) (*model.Feed, error) {
|
||||||
case FormatRDF:
|
case FormatRDF:
|
||||||
return rdf.Parse(reader)
|
return rdf.Parse(reader)
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("Unsupported feed format")
|
return nil, errors.NewLocalizedError("Unsupported feed format")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Parse returns a normalized feed struct from a JON feed.
|
// Parse returns a normalized feed struct from a JON feed.
|
||||||
func Parse(data io.Reader) (*model.Feed, error) {
|
func Parse(data io.Reader) (*model.Feed, *errors.LocalizedError) {
|
||||||
feed := new(jsonFeed)
|
feed := new(jsonFeed)
|
||||||
decoder := json.NewDecoder(data)
|
decoder := json.NewDecoder(data)
|
||||||
if err := decoder.Decode(&feed); err != nil {
|
if err := decoder.Decode(&feed); err != nil {
|
||||||
|
|
|
@ -9,8 +9,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/miniflux/miniflux/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParseJsonFeed(t *testing.T) {
|
func TestParseJsonFeed(t *testing.T) {
|
||||||
|
@ -377,8 +375,4 @@ func TestParseInvalidJSON(t *testing.T) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("Parse should returns an error")
|
t.Error("Parse should returns an error")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := err.(errors.LocalizedError); !ok {
|
|
||||||
t.Error("The error returned must be a LocalizedError")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Parse reads an OPML file and returns a SubcriptionList.
|
// Parse reads an OPML file and returns a SubcriptionList.
|
||||||
func Parse(data io.Reader) (SubcriptionList, error) {
|
func Parse(data io.Reader) (SubcriptionList, *errors.LocalizedError) {
|
||||||
feeds := new(opml)
|
feeds := new(opml)
|
||||||
decoder := xml.NewDecoder(data)
|
decoder := xml.NewDecoder(data)
|
||||||
decoder.CharsetReader = encoding.CharsetReader
|
decoder.CharsetReader = encoding.CharsetReader
|
||||||
|
|
|
@ -7,8 +7,6 @@ package opml
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/miniflux/miniflux/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParseOpmlWithoutCategories(t *testing.T) {
|
func TestParseOpmlWithoutCategories(t *testing.T) {
|
||||||
|
@ -130,8 +128,4 @@ func TestParseInvalidXML(t *testing.T) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("Parse should generate an error")
|
t.Error("Parse should generate an error")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := err.(errors.LocalizedError); !ok {
|
|
||||||
t.Error("The error returned must be a LocalizedError")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Parse returns a normalized feed struct from a RDF feed.
|
// Parse returns a normalized feed struct from a RDF feed.
|
||||||
func Parse(data io.Reader) (*model.Feed, error) {
|
func Parse(data io.Reader) (*model.Feed, *errors.LocalizedError) {
|
||||||
feed := new(rdfFeed)
|
feed := new(rdfFeed)
|
||||||
decoder := xml.NewDecoder(data)
|
decoder := xml.NewDecoder(data)
|
||||||
decoder.CharsetReader = encoding.CharsetReader
|
decoder.CharsetReader = encoding.CharsetReader
|
||||||
|
|
|
@ -9,8 +9,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/miniflux/miniflux/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParseRDFSample(t *testing.T) {
|
func TestParseRDFSample(t *testing.T) {
|
||||||
|
@ -330,8 +328,4 @@ func TestParseInvalidXml(t *testing.T) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("Parse should returns an error")
|
t.Error("Parse should returns an error")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := err.(errors.LocalizedError); !ok {
|
|
||||||
t.Error("The error returned must be a LocalizedError")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Parse returns a normalized feed struct from a RSS feed.
|
// Parse returns a normalized feed struct from a RSS feed.
|
||||||
func Parse(data io.Reader) (*model.Feed, error) {
|
func Parse(data io.Reader) (*model.Feed, *errors.LocalizedError) {
|
||||||
feed := new(rssFeed)
|
feed := new(rssFeed)
|
||||||
decoder := xml.NewDecoder(data)
|
decoder := xml.NewDecoder(data)
|
||||||
decoder.CharsetReader = encoding.CharsetReader
|
decoder.CharsetReader = encoding.CharsetReader
|
||||||
|
|
|
@ -8,8 +8,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/miniflux/miniflux/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParseRss2Sample(t *testing.T) {
|
func TestParseRss2Sample(t *testing.T) {
|
||||||
|
@ -564,8 +562,4 @@ func TestParseInvalidXml(t *testing.T) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("Parse should returns an error")
|
t.Error("Parse should returns an error")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := err.(errors.LocalizedError); !ok {
|
|
||||||
t.Error("The error returned must be a LocalizedError")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,6 +175,18 @@ func (s *Storage) UpdateUser(user *model.User) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UserLanguage returns the language of the given user.
|
||||||
|
func (s *Storage) UserLanguage(userID int64) (language string, err error) {
|
||||||
|
defer timer.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:UserLanguage] userID=%d", userID))
|
||||||
|
err = s.db.QueryRow(`SELECT language FROM users WHERE id = $1`, userID).Scan(&language)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return "en_US", nil
|
||||||
|
} else if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to fetch user language: %v", err)
|
||||||
|
}
|
||||||
|
return language, nil
|
||||||
|
}
|
||||||
|
|
||||||
// UserByID finds a user by the ID.
|
// UserByID finds a user by the ID.
|
||||||
func (s *Storage) UserByID(userID int64) (*model.User, error) {
|
func (s *Storage) UserByID(userID int64) (*model.User, error) {
|
||||||
defer timer.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:UserByID] userID=%d", userID))
|
defer timer.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:UserByID] userID=%d", userID))
|
||||||
|
|
|
@ -85,8 +85,9 @@ func (f *funcMap) Map() template.FuncMap {
|
||||||
case string:
|
case string:
|
||||||
return f.Language.Get(key.(string), args...)
|
return f.Language.Get(key.(string), args...)
|
||||||
case errors.LocalizedError:
|
case errors.LocalizedError:
|
||||||
err := key.(errors.LocalizedError)
|
return key.(errors.LocalizedError).Localize(f.Language)
|
||||||
return err.Localize(f.Language)
|
case *errors.LocalizedError:
|
||||||
|
return key.(*errors.LocalizedError).Localize(f.Language)
|
||||||
case error:
|
case error:
|
||||||
return key.(error).Error()
|
return key.(error).Error()
|
||||||
default:
|
default:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue