diff --git a/pkg/app/app.go b/pkg/app/app.go index f5915f7..b4097ae 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -6,14 +6,17 @@ package app import ( - "golang.org/x/text/language" + "context" "net/http" "path" "strings" + "golang.org/x/text/language" + "dev.tandem.ws/tandem/camper/pkg/database" httplib "dev.tandem.ws/tandem/camper/pkg/http" "dev.tandem.ws/tandem/camper/pkg/locale" + "dev.tandem.ws/tandem/camper/pkg/template" ) func shiftPath(p string) (head, tail string) { @@ -55,28 +58,43 @@ func New(db *database.DB) http.Handler { } func (h *App) ServeHTTP(w http.ResponseWriter, r *http.Request) { + requestURL := r.URL.Path var head string head, r.URL.Path = shiftPath(r.URL.Path) if head == "static" { h.fileHandler.ServeHTTP(w, r) } else { - cookie := getSessionCookie(r) - conn := h.db.MustAcquire(r.Context(), cookie) + conn, err := h.db.Acquire(r.Context()) + if err != nil { + panic(err) + } defer conn.Release() + cookie := getSessionCookie(r) + user, err := h.getUser(r.Context(), conn, cookie) + if err != nil { + panic(err) + } + l := h.matchLocale(r, user) + if head == "login" { switch r.Method { case http.MethodPost: - h.handleLogin(w, r, conn) + h.handleLogin(w, r, l, conn) default: methodNotAllowed(w, r, http.MethodPost) } } else { + if !user.LoggedIn { + h.serveLoginForm(w, r, l, requestURL) + return + } + switch head { case "": switch r.Method { case http.MethodGet: - h.handleGet(w, r) + h.serveDashboard(w, r, l) default: methodNotAllowed(w, r, http.MethodGet) } @@ -86,3 +104,51 @@ func (h *App) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } } + +type User struct { + Email string + LoggedIn bool + Role string + Language language.Tag + CsrfToken string +} + +func (h *App) getUser(ctx context.Context, conn *database.Conn, cookie string) (*User, error) { + if _, err := conn.Exec(ctx, "select set_cookie($1)", cookie); err != nil { + conn.Release() + return nil, err + } + + user := &User{ + Email: "", + LoggedIn: false, + Role: "guest", + } + row := conn.QueryRow(ctx, "select coalesce(email, ''), email is not null, role, lang_tag, csrf_token from user_profile") + var langTag string + if err := row.Scan(&user.Email, &user.LoggedIn, &user.Role, &langTag, &user.CsrfToken); err != nil { + return nil, err + } + if lang, err := language.Parse(langTag); err == nil { + user.Language = lang + } else { + return nil, err + } + + return user, nil +} + +func (h *App) matchLocale(r *http.Request, user *User) *locale.Locale { + l := h.locales[user.Language] + if l == nil { + l = locale.Match(r.Header.Get("Accept-Language"), h.locales, h.languageMatcher) + if l == nil { + l = h.defaultLocale + } + } + return l +} + +func (h *App) serveDashboard(w http.ResponseWriter, _ *http.Request, l *locale.Locale) { + template.MustRender(w, l, "dashboard.gohtml", nil) +} diff --git a/pkg/app/login.go b/pkg/app/login.go index 9646ceb..4d73e0f 100644 --- a/pkg/app/login.go +++ b/pkg/app/login.go @@ -24,6 +24,7 @@ const ( type loginForm struct { Email *form.Input Password *form.Input + Redirect *form.Input Error error } @@ -35,6 +36,9 @@ func newLoginForm() *loginForm { Password: &form.Input{ Name: "password", }, + Redirect: &form.Input{ + Name: "redirect", + }, } } @@ -44,6 +48,10 @@ func (f *loginForm) Parse(r *http.Request) error { } f.Email.FillValue(r) f.Password.FillValue(r) + f.Redirect.FillValue(r) + if f.Redirect.Val == "" { + f.Redirect.Val = "/" + } return nil } @@ -56,28 +64,24 @@ func (f *loginForm) Valid(l *locale.Locale) bool { return v.AllOK } -func (h *App) handleGet(w http.ResponseWriter, r *http.Request) { - l := h.matchLocale(r) +func (h *App) serveLoginForm(w http.ResponseWriter, _ *http.Request, l *locale.Locale, requestURL string) { login := newLoginForm() + login.Redirect.Val = requestURL + w.WriteHeader(http.StatusUnauthorized) template.MustRender(w, l, "login.gohtml", login) } -func (h *App) matchLocale(r *http.Request) *locale.Locale { - return locale.Match(r, h.locales, h.defaultLocale, h.languageMatcher) -} - -func (h *App) handleLogin(w http.ResponseWriter, r *http.Request, conn *database.Conn) { +func (h *App) handleLogin(w http.ResponseWriter, r *http.Request, l *locale.Locale, conn *database.Conn) { login := newLoginForm() if err := login.Parse(r); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } - l := h.matchLocale(r) if login.Valid(l) { cookie := conn.MustGetText(r.Context(), "select login($1, $2, $3)", login.Email, login.Password, httplib.RemoteAddr(r)) if cookie != "" { setSessionCookie(w, cookie) - http.Redirect(w, r, "/", http.StatusSeeOther) + http.Redirect(w, r, login.Redirect.Val, http.StatusSeeOther) return } login.Error = errors.New(l.Gettext("Invalid user or password.")) diff --git a/pkg/database/db.go b/pkg/database/db.go index 14e8d8a..818afce 100644 --- a/pkg/database/db.go +++ b/pkg/database/db.go @@ -53,17 +53,6 @@ func (db *DB) Acquire(ctx context.Context) (*Conn, error) { return &Conn{conn}, nil } -func (db *DB) MustAcquire(ctx context.Context, cookie string) *Conn { - conn, err := db.Acquire(ctx) - if err != nil { - panic(err) - } - if _, err = conn.Exec(ctx, "select set_cookie($1)", cookie); err != nil { - panic(false) - } - return conn -} - type Conn struct { *pgxpool.Conn } diff --git a/pkg/locale/locale.go b/pkg/locale/locale.go index 5d96797..1074ad6 100644 --- a/pkg/locale/locale.go +++ b/pkg/locale/locale.go @@ -7,8 +7,6 @@ package locale import ( "context" - "net/http" - "github.com/leonelquinteros/gotext" "golang.org/x/text/language" @@ -60,23 +58,18 @@ func (l *Locale) GettextNoop(str string) string { return str } -func Match(r *http.Request, locales Locales, defaultLocale *Locale, matcher language.Matcher) *Locale { - var locale *Locale - // TODO: find user locale - if locale == nil { - t, _, err := language.ParseAcceptLanguage(r.Header.Get("Accept-Language")) - if err == nil { - tag, _, _ := matcher.Match(t...) - var ok bool - locale, ok = locales[tag] - for !ok && !tag.IsRoot() { - tag = tag.Parent() - locale, ok = locales[tag] - } - } +func Match(acceptLanguage string, locales Locales, matcher language.Matcher) *Locale { + t, _, err := language.ParseAcceptLanguage(acceptLanguage) + if err != nil { + return nil } - if locale == nil { - locale = defaultLocale + 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 } diff --git a/po/ca.po b/po/ca.po index f31fc43..68e3ef4 100644 --- a/po/ca.po +++ b/po/ca.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: camper\n" "Report-Msgid-Bugs-To: jordi@tandem.blog\n" -"POT-Creation-Date: 2023-07-24 17:04+0200\n" +"POT-Creation-Date: 2023-07-26 01:33+0200\n" "PO-Revision-Date: 2023-07-22 23:45+0200\n" "Last-Translator: jordi fita mas \n" "Language-Team: Catalan \n" @@ -18,22 +18,27 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: web/templates/login.gohtml:2 web/templates/login.gohtml:8 +#: web/templates/dashboard.gohtml:2 web/templates/dashboard.gohtml:6 +msgctxt "title" +msgid "Dashboard" +msgstr "Tauler" + +#: web/templates/login.gohtml:2 web/templates/login.gohtml:9 msgctxt "title" msgid "Login" msgstr "Entrada" -#: web/templates/login.gohtml:17 +#: web/templates/login.gohtml:18 msgctxt "input" msgid "Email" msgstr "Correu-e" -#: web/templates/login.gohtml:26 +#: web/templates/login.gohtml:27 msgctxt "input" msgid "Password" msgstr "Contrasenya" -#: web/templates/login.gohtml:35 +#: web/templates/login.gohtml:36 msgctxt "action" msgid "Login" msgstr "Entra" @@ -42,18 +47,18 @@ msgstr "Entra" msgid "Skip to main content" msgstr "Salta al contingut principal" -#: pkg/app/login.go:50 +#: pkg/app/login.go:60 msgid "Email can not be empty." msgstr "No podeu deixar el correu en blanc." -#: pkg/app/login.go:51 +#: pkg/app/login.go:61 msgid "This email is not valid. It should be like name@domain.com." msgstr "Aquest correu-e no és vàlid. Hauria de ser similar a nom@domini.com." -#: pkg/app/login.go:53 +#: pkg/app/login.go:63 msgid "Password can not be empty." msgstr "No podeu deixar la contrasenya en blanc." -#: pkg/app/login.go:82 +#: pkg/app/login.go:86 msgid "Invalid user or password." msgstr "Nom d’usuari o contrasenya incorrectes." diff --git a/po/es.po b/po/es.po index 5ccfb31..3eb1bea 100644 --- a/po/es.po +++ b/po/es.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: camper\n" "Report-Msgid-Bugs-To: jordi@tandem.blog\n" -"POT-Creation-Date: 2023-07-24 17:04+0200\n" +"POT-Creation-Date: 2023-07-26 01:33+0200\n" "PO-Revision-Date: 2023-07-22 23:46+0200\n" "Last-Translator: jordi fita mas \n" "Language-Team: Spanish \n" @@ -18,22 +18,27 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: web/templates/login.gohtml:2 web/templates/login.gohtml:8 +#: web/templates/dashboard.gohtml:2 web/templates/dashboard.gohtml:6 +msgctxt "title" +msgid "Dashboard" +msgstr "Panel" + +#: web/templates/login.gohtml:2 web/templates/login.gohtml:9 msgctxt "title" msgid "Login" msgstr "Entrada" -#: web/templates/login.gohtml:17 +#: web/templates/login.gohtml:18 msgctxt "input" msgid "Email" msgstr "Correo-e" -#: web/templates/login.gohtml:26 +#: web/templates/login.gohtml:27 msgctxt "input" msgid "Password" msgstr "Contraseña" -#: web/templates/login.gohtml:35 +#: web/templates/login.gohtml:36 msgctxt "action" msgid "Login" msgstr "Entrar" @@ -42,18 +47,18 @@ msgstr "Entrar" msgid "Skip to main content" msgstr "Saltar al contenido principal" -#: pkg/app/login.go:50 +#: pkg/app/login.go:60 msgid "Email can not be empty." msgstr "No podéis dejar el correo-e en blanco." -#: pkg/app/login.go:51 +#: pkg/app/login.go:61 msgid "This email is not valid. It should be like name@domain.com." msgstr "Este correo-e no es válido. Tiene que ser parecido a nombre@dominio.com." -#: pkg/app/login.go:53 +#: pkg/app/login.go:63 msgid "Password can not be empty." msgstr "No podéis dejar la contraseña en blanco." -#: pkg/app/login.go:82 +#: pkg/app/login.go:86 msgid "Invalid user or password." msgstr "Usuario o contraseña incorrectos." diff --git a/web/templates/dashboard.gohtml b/web/templates/dashboard.gohtml new file mode 100644 index 0000000..39186bf --- /dev/null +++ b/web/templates/dashboard.gohtml @@ -0,0 +1,11 @@ + +{{ define "title" -}} + {{( pgettext "Dashboard" "title" )}} +{{- end }} + +{{ define "content" -}} +

{{( pgettext "Dashboard" "title" )}}

+{{- end }} diff --git a/web/templates/login.gohtml b/web/templates/login.gohtml index f4642e9..9b94c62 100644 --- a/web/templates/login.gohtml +++ b/web/templates/login.gohtml @@ -1,3 +1,7 @@ + {{ define "title" -}} {{( pgettext "Login" "title" )}} {{- end }} @@ -5,6 +9,7 @@ {{ define "content" -}} {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/app.loginForm */ -}}
+

{{( pgettext "Login" "title" )}}

{{ if .Error -}}