common login (#124)

This commit is contained in:
Tyr Mactire 2022-09-01 13:59:04 -07:00 committed by GitHub
parent 3836bd0fb8
commit 448fda02a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 124 additions and 152 deletions

View File

@ -24,12 +24,7 @@ jobs:
go-version-file: 'go.mod'
cache: true
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v3
with:
args: build --rm-dist --snapshot
args: build --rm-dist --snapshot --single-target

View File

@ -16,6 +16,9 @@ clean:
@rm -Rvf .cache coverage.txt dist relay
@find . -name ".DS_Store" -exec rm -v {} \;
docker-pull:
docker-compose --project-name ${PROJECT_NAME} -f deployments/docker-compose-test.yaml pull
docker-restart: docker-stop docker-start
docker-start:
@ -31,26 +34,16 @@ fmt:
i18n-extract:
goi18n extract -format yaml -outdir web/locales -crowdin
i18n-merge:
goi18n merge -format yaml -outdir locales web/locales/active.*.toml web/locales/translate.*.toml -crowdin
i18n-translations:
goi18n merge -format yaml -outdir locales web/locales/active.*.toml -crowdin
lint:
@echo linting
@golint $(shell go list ./... | grep -v /vendor/)
release: clean
goreleaser release
test: tidy fmt lint #gosec
test: tidy fmt
go test -cover ./...
test-ext: tidy fmt lint #gosec
test-ext: tidy fmt
go test --tags=postgres -cover ./...
test-bench-ext: tidy fmt lint #gosec
test-bench-ext: tidy fmt
go test -run=XXX -bench=. --tags=postgres -cover ./...
tidy:
@ -59,4 +52,4 @@ tidy:
vendor: tidy
go mod vendor
.PHONY: build-snapshot bun-new-migration clean fmt i18n-extract i18n-merge i18n-translations lint stage-static npm-scss npm-upgrade docker-restart docker-start docker-stop release stage-static test test-ext test-race test-race-ext test-verbose tidy vendor
.PHONY: build-snapshot bun-new-migration clean docker-pull docker-restart docker-start docker-stop fmt i18n-extract release stage-static test test-ext test-bench-ext tidy vendor

View File

@ -18,7 +18,7 @@ type Common struct {
FooterExtraScript []string
HeadLinks []libtemplate.HeadLink
NavBar Navbar
NavBar libtemplate.Navbar
NavBarDark bool
PageTitle string
}
@ -58,7 +58,7 @@ func (t *Common) SetLocalizer(l *language.Localizer) {
}
// SetNavbar sets the top level navbar used by the template.
func (t *Common) SetNavbar(nodes Navbar) {
func (t *Common) SetNavbar(nodes libtemplate.Navbar) {
t.NavBar = nodes
}

View File

@ -1,12 +1,54 @@
package template
// LoginName is the name of the login template.
const LoginName = "login"
import (
libtemplate "github.com/feditools/go-lib/template"
"github.com/feditools/relay/internal/language"
"github.com/feditools/relay/internal/models"
)
// Login contains the variables for the "login" template.
type Login struct {
Common
FormError string
FormInstance string
libtemplate.Login
}
// AddHeadLink adds a headder link to the template.
func (t *Login) AddHeadLink(l libtemplate.HeadLink) {
if t.HeadLinks == nil {
t.HeadLinks = []libtemplate.HeadLink{}
}
t.HeadLinks = append(t.HeadLinks, l)
}
// AddFooterScript adds a footer script to the template.
func (t *Login) AddFooterScript(s libtemplate.Script) {
if t.FooterScripts == nil {
t.FooterScripts = []libtemplate.Script{}
}
t.FooterScripts = append(t.FooterScripts, s)
}
// AddFooterExtraScript adds a footer script to the template.
func (t *Login) AddFooterExtraScript(s string) {
if t.FooterExtraScript == nil {
t.FooterExtraScript = []string{}
}
t.FooterExtraScript = append(t.FooterExtraScript, s)
}
// SetLanguage sets the template's default language.
func (t *Login) SetLanguage(l string) {
t.Language = l
}
// SetLocalizer sets the localizer the template will use to generate text.
func (t *Login) SetLocalizer(l *language.Localizer) {
t.TextLogin = l.TextLogin()
}
// SetNavbar sets the top level navbar used by the template.
func (t *Login) SetNavbar(nodes libtemplate.Navbar) {}
// SetNavbarDark sets the navbar theme.
func (t *Login) SetNavbarDark(dark bool) {}
// SetAccount sets the currently logged-in account.
func (t *Login) SetAccount(account *models.Account) {}

View File

@ -1,11 +1,6 @@
package template
import (
"regexp"
libtemplate "github.com/feditools/go-lib/template"
)
/*
// Navbar is a navbar that can be added to a page.
type Navbar []NavbarNode
@ -51,3 +46,4 @@ type NavbarNode struct {
Children Navbar
}
*/

View File

@ -29,7 +29,7 @@ type InitTemplate interface {
SetAccount(account *models.Account)
SetLanguage(l string)
SetLocalizer(l *language.Localizer)
SetNavbar(nodes Navbar)
SetNavbar(nodes libtemplate.Navbar)
SetNavbarDark(dark bool)
}

View File

@ -2,50 +2,59 @@ package webapp
import (
"errors"
libtemplate "github.com/feditools/go-lib/template"
"github.com/feditools/relay/internal/db"
"github.com/feditools/relay/internal/http/template"
"github.com/feditools/relay/internal/language"
nethttp "net/http"
"net/http"
)
// LoginGetHandler serves the login page.
func (m *Module) LoginGetHandler(w nethttp.ResponseWriter, r *nethttp.Request) {
m.displayLoginPage(w, r, "", "")
func (m *Module) LoginGetHandler(w http.ResponseWriter, r *http.Request) {
m.displayLoginPage(w, r, "", nil)
}
// LoginPostHandler attempts a login.
func (m *Module) LoginPostHandler(w nethttp.ResponseWriter, r *nethttp.Request) {
func (m *Module) LoginPostHandler(w http.ResponseWriter, r *http.Request) {
l := logger.WithField("func", "LoginPostHandler")
// parse form data
if err := r.ParseForm(); err != nil {
m.returnErrorPage(w, r, nethttp.StatusInternalServerError, err.Error())
m.returnErrorPage(w, r, http.StatusInternalServerError, err.Error())
return
}
// split account
formInstance := r.Form.Get(FormInstance)
domain := r.Form.Get(FormInstance)
if domain == "" {
m.displayLoginPage(w, r, "", &libtemplate.Alert{
Color: libtemplate.ColorWarning,
Text: "Please enter an instance domain.",
})
return
}
// get instance
instance, err := m.db.ReadInstanceByDomain(r.Context(), formInstance)
instance, err := m.db.ReadInstanceByDomain(r.Context(), domain)
if err != nil && !errors.Is(err, db.ErrNoEntries) {
// actual db error
m.returnErrorPage(w, r, nethttp.StatusInternalServerError, err.Error())
m.returnErrorPage(w, r, http.StatusInternalServerError, err.Error())
return
}
if errors.Is(err, db.ErrNoEntries) {
instance, err = m.fedi.GenInstanceFromDomain(r.Context(), formInstance)
instance, err = m.fedi.GenInstanceFromDomain(r.Context(), domain)
if err != nil {
m.returnErrorPage(w, r, nethttp.StatusInternalServerError, err.Error())
m.returnErrorPage(w, r, http.StatusInternalServerError, err.Error())
return
}
err = m.db.CreateInstance(r.Context(), instance)
if err != nil {
m.returnErrorPage(w, r, nethttp.StatusInternalServerError, err.Error())
m.returnErrorPage(w, r, http.StatusInternalServerError, err.Error())
return
}
@ -55,28 +64,32 @@ func (m *Module) LoginPostHandler(w nethttp.ResponseWriter, r *nethttp.Request)
loginURL, err := m.logic.GetLoginURL(r.Context(), instance)
if err != nil {
l.Errorf("get login url: %s", err.Error())
m.returnErrorPage(w, r, nethttp.StatusInternalServerError, err.Error())
m.returnErrorPage(w, r, http.StatusInternalServerError, err.Error())
return
}
nethttp.Redirect(w, r, loginURL.String(), nethttp.StatusFound)
http.Redirect(w, r, loginURL.String(), http.StatusFound)
}
func (m *Module) displayLoginPage(w nethttp.ResponseWriter, r *nethttp.Request, account, formError string) {
// l := logger.WithField("func", "displayLoginPage")
func (m *Module) displayLoginPage(w http.ResponseWriter, r *http.Request, instance string, formError *libtemplate.Alert) {
// get localizer
localizer := r.Context().Value(ContextKeyLocalizer).(*language.Localizer) // nolint
// Init template variables
tmplVars := &template.Login{}
tmplVars.PageTitle = localizer.TextLogin().String()
// set form values
tmplVars.FormError = formError
tmplVars.FormInstance = account
m.executeTemplate(w, r, template.LoginName, tmplVars)
tmplVars := &template.Login{
Login: libtemplate.Login{
FormError: formError,
FormInstance: libtemplate.FormInput{
Type: libtemplate.FormInputTypeText,
Name: FormInstance,
Placeholder: localizer.TextInstance().String(),
Value: instance,
Required: true,
},
LogoHRef: m.logoSrcDark,
},
}
m.executeTemplate(w, r, libtemplate.LoginName, tmplVars)
}

View File

@ -2,6 +2,7 @@ package webapp
import (
"bytes"
libtemplate "github.com/feditools/go-lib/template"
"github.com/feditools/relay/internal/http/template"
"github.com/feditools/relay/internal/language"
"github.com/feditools/relay/internal/models"
@ -89,7 +90,7 @@ func (m *Module) initTemplate(_ http.ResponseWriter, r *http.Request, tmpl templ
return nil
}
func (m *Module) makeNavbar(r *http.Request) (template.Navbar, error) {
func (m *Module) makeNavbar(r *http.Request) (libtemplate.Navbar, error) {
tctx, span := m.tracer.Start(r.Context(), "makeNavbar", m.tracerAttrs...)
defer span.End()
@ -99,11 +100,11 @@ func (m *Module) makeNavbar(r *http.Request) (template.Navbar, error) {
l := r.Context().Value(ContextKeyLocalizer).(*language.Localizer) // nolint
// create navbar
newNavbar := template.Navbar{
newNavbar := libtemplate.Navbar{
{
Text: l.TextHomeWeb().String(),
MatchStr: path.ReAppHome,
FAIcon: "house",
Icon: "house",
URL: path.AppHome,
},
}
@ -116,10 +117,10 @@ func (m *Module) makeNavbar(r *http.Request) (template.Navbar, error) {
return nil, err
}
if blockCount > 0 {
newNode := template.NavbarNode{
newNode := libtemplate.NavbarNode{
Text: l.TextBlocks().String(),
MatchStr: path.ReAppBlocks,
FAIcon: "user-slash",
Icon: "user-slash",
URL: path.AppBlocks,
}
newNavbar = append(newNavbar, newNode)
@ -130,34 +131,34 @@ func (m *Module) makeNavbar(r *http.Request) (template.Navbar, error) {
if loggedIn {
if account.ContactFor != nil {
newNavbar = append(newNavbar, template.NavbarNode{
newNavbar = append(newNavbar, libtemplate.NavbarNode{
Text: l.TextInstance().String(),
MatchStr: path.ReAppInstance,
FAIcon: "server",
Icon: "server",
URL: path.AppInstance,
})
}
newNavbar = append(newNavbar, template.NavbarNode{
newNavbar = append(newNavbar, libtemplate.NavbarNode{
Text: l.TextSettings().String(),
MatchStr: path.ReAppSettings,
FAIcon: "gear",
Icon: "gear",
URL: path.AppSettings,
})
if account.IsAdmin || account.IsMod {
newNode := template.NavbarNode{
newNode := libtemplate.NavbarNode{
Text: l.TextAdmin().String(),
MatchStr: path.ReAppAdminPre,
FAIcon: "screwdriver-wrench",
Icon: "screwdriver-wrench",
URL: path.AppAdminHome,
Children: template.Navbar{
Children: libtemplate.Navbar{
{
Text: l.TextConfiguration().String(),
MatchStr: path.ReAppAdminHome,
FAIcon: "gear",
Icon: "gear",
URL: path.AppAdminHome,
},
{
@ -166,19 +167,19 @@ func (m *Module) makeNavbar(r *http.Request) (template.Navbar, error) {
{
Text: l.TextAccounts().String(),
MatchStr: path.ReAppAdminAccountPre,
FAIcon: "user",
Icon: "user",
URL: path.AppAdminAccount,
},
{
Text: l.TextBlocks().String(),
MatchStr: path.ReAppAdminBlockPre,
FAIcon: "user-slash",
Icon: "user-slash",
URL: path.AppAdminBlock,
},
{
Text: l.TextInstances().String(),
MatchStr: path.ReAppAdminInstancesPre,
FAIcon: "server",
Icon: "server",
URL: path.AppAdminInstance,
},
{
@ -187,13 +188,13 @@ func (m *Module) makeNavbar(r *http.Request) (template.Navbar, error) {
{
Text: l.TextJobs().String(),
MatchStr: path.ReAppAdminJobPre,
FAIcon: "person-running",
Icon: "person-running",
URL: path.AppAdminJob,
},
{
Text: l.TextStatistics().String(),
MatchStr: path.ReAppAdminStatisticsPre,
FAIcon: "chart-line",
Icon: "chart-line",
URL: path.AppAdminStatistics,
},
},

View File

@ -51,8 +51,9 @@ type Module struct {
footerScriptChartJS libtemplate.Script
}
//revive:disable:argument-limit
// New returns a new webapp module.
//
//revive:disable:argument-limit
func New(
ctx context.Context,
d db.DB,

View File

@ -88,18 +88,6 @@ db-crypto-key: "test1234test5678test9123test4567"
# Default: ""
redis-password: "test"
#############
## METRICS ##
#############
# String. Address and port of the statsd instance
# Default: "localhost:8125"
#statsd-addr: "localhost:8125"
# String. Prefix to add to statsd metrics
# Default: "democrablock"
#statsd-prefix: ""
#######
# WEB #
#######

View File

@ -1,58 +0,0 @@
{{ define "login" -}}
<!doctype html>
<html lang="{{ .Language }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>{{ .PageTitle }}</title>
{{ range $link := .HeadLinks }}
{{template "head_link" $link -}}
{{- end }}
<style>
html,
body {
height: 100%;
}
body {
display: -ms-flexbox;
display: flex;
-ms-flex-align: center;
align-items: center;
padding-top: 40px;
padding-bottom: 40px;
}
.centerbox {
width: 100%;
padding: 15px;
margin: auto;
}
.centerbox .form-control {
margin-bottom: 20px;
}
</style>
</head>
<body class="text-center">
<div class="centerbox" style="max-width: 330px">
<form method="post">
<img class="mb-4" src="{{ logoSrcDark }}" alt="" width="100" height="100">
<h1 class="h3 mb-3 font-weight-normal">{{ .Localizer.TextLogin }}</h1>
{{- if .FormError }}
<div class="alert alert-danger alert-dismissible fade show" role="alert">
{{.FormError}}
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{{- end }}
<input class="form-control" type="text" name="instance" {{if .FormInstance}}value="{{.FormInstance}}" {{end}}placeholder="{{ .Localizer.TextInstance }}" aria-label="instance">
<button type="submit" class="btn btn-lg btn-primary btn-block">{{ .Localizer.TextLogin }}</button>
</form>
</div>
{{- range $link := .FooterScripts}}
{{template "script" $link }}
{{- end}}
</body>
</html>
{{ end }}

View File

@ -18,7 +18,7 @@
{{- if .Children }}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle{{if .Active}} active{{end}}{{if .Disabled}} disabled{{end}}" href="{{.URL}}" id="navbarDropdownMenuLink" role="button" data-bs-toggle="dropdown" aria-expanded="false">
{{if .FAIcon}}<i class="fas fa-{{.FAIcon}}"></i> {{end}}{{.Text}}
{{if .Icon}}<i class="fas fa-{{.Icon}}"></i> {{end}}{{.Text}}
</a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
{{- range .Children}}
@ -27,7 +27,7 @@
{{- else }}
<li class="nav-item">
<a class="dropdown-item{{if .Active}} active{{end}}{{if .Disabled}} disabled{{end}}" href="{{.URL}}">
{{if .FAIcon}}<i class="fas fa-{{.FAIcon}}"></i> {{end}}{{.Text}}
{{if .Icon}}<i class="fas fa-{{.Icon}}"></i> {{end}}{{.Text}}
</a>
</li>
{{- end }}
@ -37,7 +37,7 @@
{{- else}}
<li class="nav-item">
<a class="nav-link{{if .Active}} active{{end}}{{if .Disabled}} disabled{{end}}"{{if .Active}} aria-current="page"{{end}} href="{{.URL}}">
{{if .FAIcon}}<i class="fas fa-{{.FAIcon}}"></i> {{end}}{{.Text}}
{{if .Icon}}<i class="fas fa-{{.Icon}}"></i> {{end}}{{.Text}}
</a>
</li>
{{- end -}}

View File

@ -3,6 +3,7 @@ package web
import "embed"
// Files contains static files required by the application
//
//go:embed locales/active.*.yaml
//go:embed template/*
var Files embed.FS