fix tests

This commit is contained in:
Evan Goode 2025-07-13 22:12:11 -04:00
parent 8a0651ded5
commit dbe1b4c9b2
4 changed files with 52 additions and 35 deletions

View File

@ -79,18 +79,15 @@ type OIDCProvider struct {
} }
type UserError struct { type UserError struct {
Code mo.Option[int] Code mo.Option[int]
Message string Message string
MessagePlural mo.Option[string] Plural mo.Option[Plural]
N mo.Option[int] Params []interface{}
Params []interface{}
} }
func (e *UserError) Error() string { func (e *UserError) Error() string {
if n, ok := e.N.Get(); ok { if plural, ok := e.Plural.Get(); ok && plural.N > 1 {
if plural, ok := e.MessagePlural.Get(); ok && n > 1 { return fmt.Sprintf(plural.Message, e.Params...)
return fmt.Sprintf(plural, e.Params...)
}
} }
return fmt.Sprintf(e.Message, e.Params...) return fmt.Sprintf(e.Message, e.Params...)
} }
@ -107,6 +104,9 @@ func (e *UserError) TranslatedError(l *gotext.Locale) string {
} }
} }
if plural, ok := e.Plural.Get(); ok {
return l.GetN(e.Message, plural.Message, plural.N, translatedParams...)
}
return l.Get(e.Message, translatedParams...) return l.Get(e.Message, translatedParams...)
} }

View File

@ -194,7 +194,7 @@ type baseContext struct {
} }
func (app *App) NewBaseContext(c *echo.Context) baseContext { func (app *App) NewBaseContext(c *echo.Context) baseContext {
l := getLocale(c) l := app.getLocale(c)
T := l.Get T := l.Get
TN := l.GetN TN := l.GetN
return baseContext{ return baseContext{
@ -209,8 +209,12 @@ func (app *App) NewBaseContext(c *echo.Context) baseContext {
} }
} }
func getLocale(c *echo.Context) *gotext.Locale { func (app *App) getLocale(c *echo.Context) *gotext.Locale {
return (*c).Get(CONTEXT_KEY_LOCALE).(*gotext.Locale) if l := (*c).Get(CONTEXT_KEY_LOCALE); l != nil {
return (*c).Get(CONTEXT_KEY_LOCALE).(*gotext.Locale)
} else {
return app.DefaultLocale
}
} }
type errorContext struct { type errorContext struct {
@ -222,7 +226,7 @@ type errorContext struct {
// Set error message and redirect // Set error message and redirect
func (app *App) HandleWebError(err error, c *echo.Context) error { func (app *App) HandleWebError(err error, c *echo.Context) error {
l := getLocale(c) l := app.getLocale(c)
var webError *WebError var webError *WebError
var userError *UserError var userError *UserError
@ -608,7 +612,7 @@ func FrontCompleteRegistration(app *App) func(c echo.Context) error {
func (app *App) FrontOIDCUnlink() func(c echo.Context) error { func (app *App) FrontOIDCUnlink() func(c echo.Context) error {
return withBrowserAuthentication(app, true, func(c echo.Context, user *User) error { return withBrowserAuthentication(app, true, func(c echo.Context, user *User) error {
l := getLocale(&c) l := app.getLocale(&c)
returnURL := getReturnURL(app, &c) returnURL := getReturnURL(app, &c)
targetUUID := c.FormValue("userUuid") targetUUID := c.FormValue("userUuid")
@ -644,7 +648,7 @@ func makeOIDCAuthURL(c *echo.Context, provider *OIDCProvider, stateBase64 string
} }
func (app *App) oidcLink(c echo.Context, oidcProvider *OIDCProvider, tokens *oidc.Tokens[*oidc.IDTokenClaims], state oidcState, user *User) error { func (app *App) oidcLink(c echo.Context, oidcProvider *OIDCProvider, tokens *oidc.Tokens[*oidc.IDTokenClaims], state oidcState, user *User) error {
l := getLocale(&c) l := app.getLocale(&c)
returnURL := state.ReturnURL returnURL := state.ReturnURL
@ -871,7 +875,7 @@ func FrontDeleteInvite(app *App) func(c echo.Context) error {
// POST /web/admin/update-users // POST /web/admin/update-users
func FrontUpdateUsers(app *App) func(c echo.Context) error { func FrontUpdateUsers(app *App) func(c echo.Context) error {
return withBrowserAdmin(app, func(c echo.Context, user *User) error { return withBrowserAdmin(app, func(c echo.Context, user *User) error {
l := getLocale(&c) l := app.getLocale(&c)
returnURL := getReturnURL(app, &c) returnURL := getReturnURL(app, &c)
@ -1142,7 +1146,7 @@ func getFormValue(c *echo.Context, key string) mo.Option[string] {
// POST /update-user // POST /update-user
func FrontUpdateUser(app *App) func(c echo.Context) error { func FrontUpdateUser(app *App) func(c echo.Context) error {
return withBrowserAuthentication(app, true, func(c echo.Context, user *User) error { return withBrowserAuthentication(app, true, func(c echo.Context, user *User) error {
l := getLocale(&c) l := app.getLocale(&c)
returnURL := getReturnURL(app, &c) returnURL := getReturnURL(app, &c)
targetUUID := nilIfEmpty(c.FormValue("uuid")) targetUUID := nilIfEmpty(c.FormValue("uuid"))
@ -1209,7 +1213,7 @@ func FrontUpdateUser(app *App) func(c echo.Context) error {
// POST /web/update-player // POST /web/update-player
func FrontUpdatePlayer(app *App) func(c echo.Context) error { func FrontUpdatePlayer(app *App) func(c echo.Context) error {
return withBrowserAuthentication(app, true, func(c echo.Context, user *User) error { return withBrowserAuthentication(app, true, func(c echo.Context, user *User) error {
l := getLocale(&c) l := app.getLocale(&c)
returnURL := getReturnURL(app, &c) returnURL := getReturnURL(app, &c)
playerUUID := c.FormValue("uuid") playerUUID := c.FormValue("uuid")
@ -1413,7 +1417,7 @@ func frontChallenge(app *App, action string) func(c echo.Context) error {
// POST /web/create-player // POST /web/create-player
func FrontCreatePlayer(app *App) func(c echo.Context) error { func FrontCreatePlayer(app *App) func(c echo.Context) error {
return withBrowserAuthentication(app, true, func(c echo.Context, caller *User) error { return withBrowserAuthentication(app, true, func(c echo.Context, caller *User) error {
l := getLocale(&c) l := app.getLocale(&c)
userUUID := c.FormValue("userUuid") userUUID := c.FormValue("userUuid")
@ -1460,7 +1464,7 @@ func FrontCreatePlayer(app *App) func(c echo.Context) error {
func FrontRegister(app *App) func(c echo.Context) error { func FrontRegister(app *App) func(c echo.Context) error {
returnURL := Unwrap(url.JoinPath(app.FrontEndURL, "web/user")) returnURL := Unwrap(url.JoinPath(app.FrontEndURL, "web/user"))
return func(c echo.Context) error { return func(c echo.Context) error {
l := getLocale(&c) l := app.getLocale(&c)
useIDToken := c.FormValue("useIdToken") == "on" useIDToken := c.FormValue("useIdToken") == "on"
honeypot := c.FormValue("email") honeypot := c.FormValue("email")
@ -1595,7 +1599,7 @@ func addDestination(url_ string, destination string) (string, error) {
// POST /web/oidc-migrate // POST /web/oidc-migrate
func (app *App) FrontOIDCMigrate() func(c echo.Context) error { func (app *App) FrontOIDCMigrate() func(c echo.Context) error {
return func(c echo.Context) error { return func(c echo.Context) error {
l := getLocale(&c) l := app.getLocale(&c)
failureURL := getReturnURL(app, &c) failureURL := getReturnURL(app, &c)
username := c.FormValue("username") username := c.FormValue("username")
@ -1695,7 +1699,7 @@ func FrontLogin(app *App) func(c echo.Context) error {
// POST /web/delete-user // POST /web/delete-user
func FrontDeleteUser(app *App) func(c echo.Context) error { func FrontDeleteUser(app *App) func(c echo.Context) error {
return withBrowserAuthentication(app, true, func(c echo.Context, user *User) error { return withBrowserAuthentication(app, true, func(c echo.Context, user *User) error {
l := getLocale(&c) l := app.getLocale(&c)
returnURL := getReturnURL(app, &c) returnURL := getReturnURL(app, &c)
var targetUser *User var targetUser *User
@ -1733,7 +1737,7 @@ func FrontDeleteUser(app *App) func(c echo.Context) error {
// POST /web/delete-player // POST /web/delete-player
func FrontDeletePlayer(app *App) func(c echo.Context) error { func FrontDeletePlayer(app *App) func(c echo.Context) error {
return withBrowserAuthentication(app, true, func(c echo.Context, user *User) error { return withBrowserAuthentication(app, true, func(c echo.Context, user *User) error {
l := getLocale(&c) l := app.getLocale(&c)
returnURL := getReturnURL(app, &c) returnURL := getReturnURL(app, &c)
playerUUID := c.FormValue("uuid") playerUUID := c.FormValue("uuid")

22
main.go
View File

@ -75,6 +75,7 @@ type App struct {
OIDCProvidersByIssuer map[string]*OIDCProvider OIDCProvidersByIssuer map[string]*OIDCProvider
FallbackAPIServers []FallbackAPIServer FallbackAPIServers []FallbackAPIServer
Locales map[language.Tag]*gotext.Locale Locales map[language.Tag]*gotext.Locale
DefaultLocale *gotext.Locale
LocaleTags []language.Tag LocaleTags []language.Tag
} }
@ -195,17 +196,16 @@ func (app *App) MakeServer() *echo.Echo {
})) }))
} }
e.Use(app.GetLanguageMiddleware())
// Front // Front
if app.Config.EnableWebFrontEnd { if app.Config.EnableWebFrontEnd {
t := NewTemplate(app) t := NewTemplate(app)
e.Renderer = t e.Renderer = t
frontUser := FrontUser(app) frontUser := FrontUser(app)
getLanguage := app.GetLanguageMiddleware() e.GET("/", FrontRoot(app))
e.GET("/", FrontRoot(app), getLanguage)
g := e.Group("/web") g := e.Group("/web")
g.Use(getLanguage)
g.GET("/admin", FrontAdmin(app)) g.GET("/admin", FrontAdmin(app))
g.GET("/complete-registration", FrontCompleteRegistration(app)) g.GET("/complete-registration", FrontCompleteRegistration(app))
g.GET("/create-player-challenge", FrontCreatePlayerChallenge(app)) g.GET("/create-player-challenge", FrontCreatePlayerChallenge(app))
@ -438,18 +438,23 @@ func setup(config *Config) *App {
// Locales // Locales
locales := map[language.Tag]*gotext.Locale{} locales := map[language.Tag]*gotext.Locale{}
localeTags := make([]language.Tag, 0) localeTags := make([]language.Tag, 0)
locales_path := path.Join(config.DataDirectory, "locales") localesPath := path.Join(config.DataDirectory, "locales")
lang_paths, err := filepath.Glob(path.Join(locales_path, "*")) langPaths, err := filepath.Glob(path.Join(localesPath, "*"))
for _, lang_path := range lang_paths { defaultLang := "en-US" // TODO config option?
var defaultLocale *gotext.Locale
for _, lang_path := range langPaths {
lang := filepath.Base(lang_path) lang := filepath.Base(lang_path)
tag, err := language.Parse(lang) tag, err := language.Parse(lang)
if err != nil { if err != nil {
log.Fatalf("Unrecognized language tag: %s", lang) log.Fatalf("Unrecognized language tag: %s", lang)
} }
l := gotext.NewLocale(locales_path, lang) l := gotext.NewLocale(localesPath, lang)
l.AddDomain("default") l.AddDomain("default")
localeTags = append(localeTags, tag) localeTags = append(localeTags, tag)
locales[tag] = l locales[tag] = l
if lang == defaultLang {
defaultLocale = l
}
} }
// Crypto // Crypto
@ -603,6 +608,7 @@ func setup(config *Config) *App {
OIDCProvidersByIssuer: oidcProvidersByIssuer, OIDCProvidersByIssuer: oidcProvidersByIssuer,
FallbackAPIServers: fallbackAPIServers, FallbackAPIServers: fallbackAPIServers,
Locales: locales, Locales: locales,
DefaultLocale: defaultLocale,
LocaleTags: localeTags, LocaleTags: localeTags,
} }

View File

@ -106,6 +106,11 @@ func ParseUUID(idOrUUID string) (string, error) {
return "", errors.New("invalid ID or UUID") return "", errors.New("invalid ID or UUID")
} }
type Plural struct {
Message string
N int
}
func (app *App) ValidatePlayerName(playerName string) error { func (app *App) ValidatePlayerName(playerName string) error {
if app.TransientLoginEligible(playerName) { if app.TransientLoginEligible(playerName) {
return &UserError{Message: "name is reserved for transient login"} return &UserError{Message: "name is reserved for transient login"}
@ -116,10 +121,12 @@ func (app *App) ValidatePlayerName(playerName string) error {
} }
if len(playerName) > maxLength { if len(playerName) > maxLength {
return &UserError{ return &UserError{
Message: "can't be longer than %d character", Message: "can't be longer than %d character",
MessagePlural: mo.Some("can't be longer than %d characters"), Plural: mo.Some(Plural{
N: mo.Some(maxLength), Message: "can't be longer than %d characters",
Params: []interface{}{maxLength}, N: maxLength,
}),
Params: []interface{}{maxLength},
} }
} }