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' go-version-file: 'go.mod'
cache: true 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 - name: Run GoReleaser
uses: goreleaser/goreleaser-action@v3 uses: goreleaser/goreleaser-action@v3
with: 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 @rm -Rvf .cache coverage.txt dist relay
@find . -name ".DS_Store" -exec rm -v {} \; @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-restart: docker-stop docker-start
docker-start: docker-start:
@ -31,26 +34,16 @@ fmt:
i18n-extract: i18n-extract:
goi18n extract -format yaml -outdir web/locales -crowdin 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 release: clean
goreleaser release goreleaser release
test: tidy fmt lint #gosec test: tidy fmt
go test -cover ./... go test -cover ./...
test-ext: tidy fmt lint #gosec test-ext: tidy fmt
go test --tags=postgres -cover ./... 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 ./... go test -run=XXX -bench=. --tags=postgres -cover ./...
tidy: tidy:
@ -59,4 +52,4 @@ tidy:
vendor: tidy vendor: tidy
go mod vendor 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 FooterExtraScript []string
HeadLinks []libtemplate.HeadLink HeadLinks []libtemplate.HeadLink
NavBar Navbar NavBar libtemplate.Navbar
NavBarDark bool NavBarDark bool
PageTitle string PageTitle string
} }
@ -58,7 +58,7 @@ func (t *Common) SetLocalizer(l *language.Localizer) {
} }
// SetNavbar sets the top level navbar used by the template. // 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 t.NavBar = nodes
} }

View File

@ -1,12 +1,54 @@
package template package template
// LoginName is the name of the login template. import (
const LoginName = "login" 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 { type Login struct {
Common libtemplate.Login
FormError string
FormInstance string
} }
// 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 package template
import ( /*
"regexp"
libtemplate "github.com/feditools/go-lib/template"
)
// Navbar is a navbar that can be added to a page. // Navbar is a navbar that can be added to a page.
type Navbar []NavbarNode type Navbar []NavbarNode
@ -51,3 +46,4 @@ type NavbarNode struct {
Children Navbar Children Navbar
} }
*/

View File

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

View File

@ -2,50 +2,59 @@ package webapp
import ( import (
"errors" "errors"
libtemplate "github.com/feditools/go-lib/template"
"github.com/feditools/relay/internal/db" "github.com/feditools/relay/internal/db"
"github.com/feditools/relay/internal/http/template" "github.com/feditools/relay/internal/http/template"
"github.com/feditools/relay/internal/language" "github.com/feditools/relay/internal/language"
nethttp "net/http" "net/http"
) )
// LoginGetHandler serves the login page. // LoginGetHandler serves the login page.
func (m *Module) LoginGetHandler(w nethttp.ResponseWriter, r *nethttp.Request) { func (m *Module) LoginGetHandler(w http.ResponseWriter, r *http.Request) {
m.displayLoginPage(w, r, "", "") m.displayLoginPage(w, r, "", nil)
} }
// LoginPostHandler attempts a login. // 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") l := logger.WithField("func", "LoginPostHandler")
// parse form data // parse form data
if err := r.ParseForm(); err != nil { if err := r.ParseForm(); err != nil {
m.returnErrorPage(w, r, nethttp.StatusInternalServerError, err.Error()) m.returnErrorPage(w, r, http.StatusInternalServerError, err.Error())
return return
} }
// split account // 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 // 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) { if err != nil && !errors.Is(err, db.ErrNoEntries) {
// actual db error // actual db error
m.returnErrorPage(w, r, nethttp.StatusInternalServerError, err.Error()) m.returnErrorPage(w, r, http.StatusInternalServerError, err.Error())
return return
} }
if errors.Is(err, db.ErrNoEntries) { 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 { if err != nil {
m.returnErrorPage(w, r, nethttp.StatusInternalServerError, err.Error()) m.returnErrorPage(w, r, http.StatusInternalServerError, err.Error())
return return
} }
err = m.db.CreateInstance(r.Context(), instance) err = m.db.CreateInstance(r.Context(), instance)
if err != nil { if err != nil {
m.returnErrorPage(w, r, nethttp.StatusInternalServerError, err.Error()) m.returnErrorPage(w, r, http.StatusInternalServerError, err.Error())
return return
} }
@ -55,28 +64,32 @@ func (m *Module) LoginPostHandler(w nethttp.ResponseWriter, r *nethttp.Request)
loginURL, err := m.logic.GetLoginURL(r.Context(), instance) loginURL, err := m.logic.GetLoginURL(r.Context(), instance)
if err != nil { if err != nil {
l.Errorf("get login url: %s", err.Error()) 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 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) { func (m *Module) displayLoginPage(w http.ResponseWriter, r *http.Request, instance string, formError *libtemplate.Alert) {
// l := logger.WithField("func", "displayLoginPage")
// get localizer // get localizer
localizer := r.Context().Value(ContextKeyLocalizer).(*language.Localizer) // nolint localizer := r.Context().Value(ContextKeyLocalizer).(*language.Localizer) // nolint
// Init template variables // Init template variables
tmplVars := &template.Login{} 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,
},
}
tmplVars.PageTitle = localizer.TextLogin().String() m.executeTemplate(w, r, libtemplate.LoginName, tmplVars)
// set form values
tmplVars.FormError = formError
tmplVars.FormInstance = account
m.executeTemplate(w, r, template.LoginName, tmplVars)
} }

View File

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

View File

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

View File

@ -88,18 +88,6 @@ db-crypto-key: "test1234test5678test9123test4567"
# Default: "" # Default: ""
redis-password: "test" 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 # # 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 }} {{- if .Children }}
<li class="nav-item dropdown"> <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"> <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> </a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink"> <ul class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
{{- range .Children}} {{- range .Children}}
@ -27,7 +27,7 @@
{{- else }} {{- else }}
<li class="nav-item"> <li class="nav-item">
<a class="dropdown-item{{if .Active}} active{{end}}{{if .Disabled}} disabled{{end}}" href="{{.URL}}"> <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> </a>
</li> </li>
{{- end }} {{- end }}
@ -37,7 +37,7 @@
{{- else}} {{- else}}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link{{if .Active}} active{{end}}{{if .Disabled}} disabled{{end}}"{{if .Active}} aria-current="page"{{end}} href="{{.URL}}"> <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> </a>
</li> </li>
{{- end -}} {{- end -}}

View File

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