jordi fita mas 1ef6dcc4cf Get user from database based on cookie and serve login if not logged in
To get the user from the database i have to set the cookie first, that
was already done in database.MustAcquire, but i thought they were too
far apart, even thought they are so related.  So, the cookie, and thus
the role, is set when getting the user, that is actually the first thing
to do once the connection is acquired.  However, that way the database
package has no knowledge of cookies, and the code that sets the cookie
and retrieves the user are next to each other.

I applied the same logic to the changes of locale.Match: it has not
business knowing that the accept language string comes from a request;
it only needs the actual string.  Also, the TODO comment about getting
the user’s locale made no sense, now, because app already knows that
locale, so there is no need to pass the user to the locale package.

Getting the locale is done after retrieving the user from the database,
for the same reason the connection is Acquired as far up as possible:
almost every request will need this value, together with the user and
the database connection.

I am a bit affraid that i will end up with functions that always expect
these three values.  Maybe i can put the locale inside user, as it is
the user’s locale, after all, no matter if it came from the database or
the user agent, but connection and user must be separate, i think.

We’ll see.
2023-07-26 01:50:39 +02:00

105 lines
2.1 KiB
Go

/*
* SPDX-FileCopyrightText: 2023 jordi fita mas <jfita@peritasoft.com>
* SPDX-License-Identifier: AGPL-3.0-only
*/
package locale
import (
"context"
"github.com/leonelquinteros/gotext"
"golang.org/x/text/language"
"dev.tandem.ws/tandem/camper/pkg/database"
)
type Locale struct {
*gotext.Locale
CurrencyPattern string
Language language.Tag
}
type Locales map[language.Tag]*Locale
func (m Locales) Tags() []language.Tag {
keys := make([]language.Tag, len(m))
i := 0
for k := range m {
keys[i] = k
i++
}
return keys
}
func MustGetAll(db *database.DB) Locales {
availableLanguages := mustGetAvailableLanguages(db)
locales := map[language.Tag]*Locale{}
for _, lang := range availableLanguages {
locale := newLocale(lang)
locale.AddDomain("camper")
locales[lang.tag] = locale
}
return locales
}
func newLocale(lang availableLanguage) *Locale {
return &Locale{
gotext.NewLocale("locale", lang.tag.String()),
lang.currencyPattern,
lang.tag,
}
}
func (l *Locale) Gettext(str string) string {
return l.GetD(l.GetDomain(), str)
}
func (l *Locale) GettextNoop(str string) string {
return str
}
func Match(acceptLanguage string, locales Locales, matcher language.Matcher) *Locale {
t, _, err := language.ParseAcceptLanguage(acceptLanguage)
if err != nil {
return nil
}
var locale *Locale
tag, _, _ := matcher.Match(t...)
var ok bool
locale, ok = locales[tag]
for !ok && !tag.IsRoot() {
tag = tag.Parent()
locale, ok = locales[tag]
}
return locale
}
type availableLanguage struct {
tag language.Tag
currencyPattern string
}
func mustGetAvailableLanguages(db *database.DB) []availableLanguage {
rows, err := db.Query(context.Background(), "select lang_tag, currency_pattern from language where selectable")
if err != nil {
panic(err)
}
defer rows.Close()
var languages []availableLanguage
for rows.Next() {
var langTag string
var currencyPattern string
err = rows.Scan(&langTag, &currencyPattern)
if err != nil {
panic(err)
}
languages = append(languages, availableLanguage{language.MustParse(langTag), currencyPattern})
}
if rows.Err() != nil {
panic(rows.Err())
}
return languages
}