Add new debug option for http
This commit is contained in:
parent
0ba34fe80f
commit
0290a42d07
39 changed files with 293 additions and 189 deletions
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth/policy"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth/scopes"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/handling"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/php/users"
|
||||
"github.com/tkw1536/pkglib/httpx"
|
||||
|
|
@ -21,6 +22,7 @@ type Next struct {
|
|||
Auth *auth.Auth
|
||||
Policy *policy.Policy
|
||||
Instances *instances.Instances
|
||||
Handleing *handling.Handling
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -72,7 +74,7 @@ func (next *Next) getInstance(r *http.Request) (wisski *wisski.WissKI, path stri
|
|||
}
|
||||
|
||||
func (next *Next) HandleRoute(ctx context.Context, path string) (http.Handler, error) {
|
||||
return httpx.RedirectHandler(func(r *http.Request) (string, int, error) {
|
||||
return next.dependencies.Handleing.Redirect(func(r *http.Request) (string, int, error) {
|
||||
// get the instance and the path
|
||||
instance, path, err := next.getInstance(r)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -11,25 +11,29 @@ import (
|
|||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth/scopes"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth/tokens"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/handling"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/ssh2"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/ssh2/sshkeys"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/tkw1536/pkglib/httpx"
|
||||
"github.com/tkw1536/pkglib/httpx/form"
|
||||
)
|
||||
|
||||
type UserPanel struct {
|
||||
component.Base
|
||||
dependencies struct {
|
||||
Auth *auth.Auth
|
||||
Auth *auth.Auth
|
||||
|
||||
Handling *handling.Handling
|
||||
Templating *templating.Templating
|
||||
Policy *policy.Policy
|
||||
Tokens *tokens.Tokens
|
||||
Instances *instances.Instances
|
||||
Next *next.Next
|
||||
Keys *sshkeys.SSHKeys
|
||||
SSH2 *ssh2.SSH2
|
||||
|
||||
Policy *policy.Policy
|
||||
Tokens *tokens.Tokens
|
||||
Instances *instances.Instances
|
||||
Next *next.Next
|
||||
Keys *sshkeys.SSHKeys
|
||||
SSH2 *ssh2.SSH2
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -142,12 +146,12 @@ func (panel *UserPanel) HandleRoute(ctx context.Context, route string) (http.Han
|
|||
|
||||
type userFormContext struct {
|
||||
templating.RuntimeFlags
|
||||
httpx.FormContext
|
||||
form.FormContext
|
||||
|
||||
User *models.User
|
||||
}
|
||||
|
||||
func (panel *UserPanel) UserFormContext(tpl *templating.Template[userFormContext], last component.MenuItem, funcs ...templating.FlagFunc) func(ctx httpx.FormContext, r *http.Request) any {
|
||||
func (panel *UserPanel) UserFormContext(tpl *templating.Template[userFormContext], last component.MenuItem, funcs ...templating.FlagFunc) func(ctx form.FormContext, r *http.Request) any {
|
||||
funcs = append(funcs, func(flags templating.Flags, r *http.Request) templating.Flags {
|
||||
// append the last menu item, and prepend the menuUser one!
|
||||
flags.Crumbs = append(flags.Crumbs, last, last)
|
||||
|
|
@ -156,7 +160,7 @@ func (panel *UserPanel) UserFormContext(tpl *templating.Template[userFormContext
|
|||
return flags
|
||||
})
|
||||
|
||||
return func(ctx httpx.FormContext, r *http.Request) any {
|
||||
return func(ctx form.FormContext, r *http.Request) any {
|
||||
uctx := userFormContext{FormContext: ctx}
|
||||
if user, err := panel.dependencies.Auth.UserOfSession(r); err == nil {
|
||||
uctx.User = &user.User
|
||||
|
|
|
|||
|
|
@ -9,14 +9,14 @@ import (
|
|||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
|
||||
"github.com/tkw1536/pkglib/httpx"
|
||||
"github.com/tkw1536/pkglib/httpx/field"
|
||||
"github.com/tkw1536/pkglib/httpx/form"
|
||||
"github.com/tkw1536/pkglib/httpx/form/field"
|
||||
)
|
||||
|
||||
//go:embed "templates/password.html"
|
||||
var passwordHTML []byte
|
||||
var passwordTemplate = templating.Parse[userFormContext](
|
||||
"password.html", passwordHTML, httpx.FormTemplate,
|
||||
"password.html", passwordHTML, form.FormTemplate,
|
||||
|
||||
templating.Title("Change Password"),
|
||||
templating.Assets(assets.AssetsUser),
|
||||
|
|
@ -34,17 +34,17 @@ var (
|
|||
func (panel *UserPanel) routePassword(ctx context.Context) http.Handler {
|
||||
tpl := passwordTemplate.Prepare(panel.dependencies.Templating)
|
||||
|
||||
return &httpx.Form[struct{}]{
|
||||
return &form.Form[struct{}]{
|
||||
Fields: []field.Field{
|
||||
{Name: "old", Type: field.Password, Autocomplete: field.CurrentPassword, EmptyOnError: true, Label: "Current Password"},
|
||||
{Name: "otp", Type: field.Text, Autocomplete: field.OneTimeCode, EmptyOnError: true, Label: "Current Passcode (optional)"},
|
||||
{Name: "new", Type: field.Password, Autocomplete: field.NewPassword, EmptyOnError: true, Label: "New Password"},
|
||||
{Name: "new2", Type: field.Password, Autocomplete: field.NewPassword, EmptyOnError: true, Label: "New Password (again)"},
|
||||
},
|
||||
FieldTemplate: field.PureCSSFieldTemplate,
|
||||
FieldTemplate: assets.PureCSSFieldTemplate,
|
||||
|
||||
RenderTemplate: tpl.Template(),
|
||||
RenderTemplateContext: panel.UserFormContext(tpl, menuChangePassword),
|
||||
Template: tpl.Template(),
|
||||
TemplateContext: panel.UserFormContext(tpl, menuChangePassword),
|
||||
|
||||
Validate: func(r *http.Request, values map[string]string) (struct{}, error) {
|
||||
old, passcode, new, new2 := values["old"], values["otp"], values["new"], values["new2"]
|
||||
|
|
@ -82,7 +82,7 @@ func (panel *UserPanel) routePassword(ctx context.Context) http.Handler {
|
|||
return struct{}{}, nil
|
||||
},
|
||||
|
||||
RenderSuccess: func(_ struct{}, values map[string]string, w http.ResponseWriter, r *http.Request) error {
|
||||
Success: func(_ struct{}, values map[string]string, w http.ResponseWriter, r *http.Request) error {
|
||||
return errPasswordSet
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ import (
|
|||
"github.com/gliderlabs/ssh"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/tkw1536/pkglib/httpx"
|
||||
"github.com/tkw1536/pkglib/httpx/field"
|
||||
"github.com/tkw1536/pkglib/httpx/form"
|
||||
"github.com/tkw1536/pkglib/httpx/form/field"
|
||||
|
||||
gossh "golang.org/x/crypto/ssh"
|
||||
|
||||
|
|
@ -57,7 +58,7 @@ func (panel *UserPanel) sshRoute(ctx context.Context) http.Handler {
|
|||
),
|
||||
)
|
||||
|
||||
return tpl.HTMLHandler(func(r *http.Request) (sc SSHTemplateContext, err error) {
|
||||
return tpl.HTMLHandler(panel.dependencies.Handling, func(r *http.Request) (sc SSHTemplateContext, err error) {
|
||||
user, err := panel.dependencies.Auth.UserOfSession(r)
|
||||
if err != nil {
|
||||
return sc, err
|
||||
|
|
@ -129,7 +130,7 @@ func (panel *UserPanel) sshDeleteRoute(ctx context.Context) http.Handler {
|
|||
//go:embed "templates/ssh_add.html"
|
||||
var sshAddHTML []byte
|
||||
var sshAddTemplate = templating.ParseForm(
|
||||
"ssh_add.html", sshAddHTML, httpx.FormTemplate,
|
||||
"ssh_add.html", sshAddHTML, form.FormTemplate,
|
||||
templating.Title("Add SSH Key"),
|
||||
templating.Assets(assets.AssetsUser),
|
||||
)
|
||||
|
|
@ -150,15 +151,15 @@ func (panel *UserPanel) sshAddRoute(ctx context.Context) http.Handler {
|
|||
),
|
||||
)
|
||||
|
||||
return &httpx.Form[addKeyResult]{
|
||||
return &form.Form[addKeyResult]{
|
||||
Fields: []field.Field{
|
||||
{Name: "comment", Type: field.Text, Label: "Comment"},
|
||||
{Name: "key", Type: field.Textarea, Label: "Key in authorized_keys format"}, // has hacked css!
|
||||
},
|
||||
FieldTemplate: field.PureCSSFieldTemplate,
|
||||
FieldTemplate: assets.PureCSSFieldTemplate,
|
||||
|
||||
RenderTemplate: tpl.Template(),
|
||||
RenderTemplateContext: templating.FormTemplateContext(tpl),
|
||||
Template: tpl.Template(),
|
||||
TemplateContext: templating.FormTemplateContext(tpl),
|
||||
|
||||
Validate: func(r *http.Request, values map[string]string) (ak addKeyResult, err error) {
|
||||
ak.User, err = panel.dependencies.Auth.UserOfSession(r)
|
||||
|
|
@ -181,7 +182,7 @@ func (panel *UserPanel) sshAddRoute(ctx context.Context) http.Handler {
|
|||
return ak, nil
|
||||
},
|
||||
|
||||
RenderSuccess: func(ak addKeyResult, values map[string]string, w http.ResponseWriter, r *http.Request) error {
|
||||
Success: func(ak addKeyResult, values map[string]string, w http.ResponseWriter, r *http.Request) error {
|
||||
// add the key to the user
|
||||
if err := panel.dependencies.Keys.Add(r.Context(), ak.User.User.User, ak.Comment, ak.Key); err != nil {
|
||||
return errAddKey
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ import (
|
|||
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/tkw1536/pkglib/httpx"
|
||||
"github.com/tkw1536/pkglib/httpx/field"
|
||||
"github.com/tkw1536/pkglib/httpx/form"
|
||||
"github.com/tkw1536/pkglib/httpx/form/field"
|
||||
|
||||
_ "embed"
|
||||
)
|
||||
|
|
@ -44,7 +45,7 @@ func (panel *UserPanel) tokensRoute(ctx context.Context) http.Handler {
|
|||
),
|
||||
)
|
||||
|
||||
return tpl.HTMLHandler(func(r *http.Request) (tc TokenTemplateContext, err error) {
|
||||
return tpl.HTMLHandler(panel.dependencies.Handling, func(r *http.Request) (tc TokenTemplateContext, err error) {
|
||||
// list the user
|
||||
user, err := panel.dependencies.Auth.UserOfSession(r)
|
||||
if err != nil || user == nil {
|
||||
|
|
@ -94,7 +95,7 @@ func (panel *UserPanel) tokensDeleteRoute(ctx context.Context) http.Handler {
|
|||
//go:embed "templates/tokens_add.html"
|
||||
var tokensAddHTML []byte
|
||||
var tokensAddTemplate = templating.ParseForm(
|
||||
"tokens_add.html", tokensAddHTML, httpx.FormTemplate,
|
||||
"tokens_add.html", tokensAddHTML, form.FormTemplate,
|
||||
templating.Title("Add Token"),
|
||||
templating.Assets(assets.AssetsUser),
|
||||
)
|
||||
|
|
@ -108,7 +109,7 @@ type addTokenResult struct {
|
|||
//go:embed "templates/token_created.html"
|
||||
var tokenCreatedHTML []byte
|
||||
var tokenCreateTemplate = templating.Parse[TokenCreateContext](
|
||||
"token_created.html", tokenCreatedHTML, httpx.FormTemplate,
|
||||
"token_created.html", tokenCreatedHTML, form.FormTemplate,
|
||||
templating.Title("Add Token"),
|
||||
templating.Assets(assets.AssetsUser),
|
||||
)
|
||||
|
|
@ -139,14 +140,14 @@ func (panel *UserPanel) tokensAddRoute(ctx context.Context) http.Handler {
|
|||
),
|
||||
)
|
||||
|
||||
return &httpx.Form[addTokenResult]{
|
||||
return &form.Form[addTokenResult]{
|
||||
Fields: []field.Field{
|
||||
{Name: "description", Type: field.Text, Label: "Description"},
|
||||
},
|
||||
FieldTemplate: field.PureCSSFieldTemplate,
|
||||
FieldTemplate: assets.PureCSSFieldTemplate,
|
||||
|
||||
RenderTemplate: tplForm.Template(),
|
||||
RenderTemplateContext: templating.FormTemplateContext(tplForm),
|
||||
Template: tplForm.Template(),
|
||||
TemplateContext: templating.FormTemplateContext(tplForm),
|
||||
|
||||
Validate: func(r *http.Request, values map[string]string) (at addTokenResult, err error) {
|
||||
at.User, err = panel.dependencies.Auth.UserOfSession(r)
|
||||
|
|
@ -164,7 +165,7 @@ func (panel *UserPanel) tokensAddRoute(ctx context.Context) http.Handler {
|
|||
return at, nil
|
||||
},
|
||||
|
||||
RenderSuccess: func(at addTokenResult, values map[string]string, w http.ResponseWriter, r *http.Request) error {
|
||||
Success: func(at addTokenResult, values map[string]string, w http.ResponseWriter, r *http.Request) error {
|
||||
// add the key to the user
|
||||
tok, err := panel.dependencies.Tokens.Add(r.Context(), at.User.User.User, at.Description, at.Scopes)
|
||||
if err != nil {
|
||||
|
|
@ -175,10 +176,16 @@ func (panel *UserPanel) tokensAddRoute(ctx context.Context) http.Handler {
|
|||
}
|
||||
|
||||
// render the created context
|
||||
return httpx.WriteHTML(tplDone.Context(r, TokenCreateContext{
|
||||
Domain: template.URL(panel.Config.HTTP.JoinPath().String()),
|
||||
Token: tok,
|
||||
}), nil, tplDone.Template(), "", w, r)
|
||||
return panel.dependencies.Handling.WriteHTML(
|
||||
tplDone.Context(r, TokenCreateContext{
|
||||
Domain: template.URL(panel.Config.HTTP.JoinPath().String()),
|
||||
Token: tok,
|
||||
}),
|
||||
nil,
|
||||
tplDone.Template(),
|
||||
w,
|
||||
r,
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import (
|
|||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
|
||||
"github.com/tkw1536/pkglib/httpx"
|
||||
"github.com/tkw1536/pkglib/httpx/field"
|
||||
"github.com/tkw1536/pkglib/httpx/form"
|
||||
"github.com/tkw1536/pkglib/httpx/form/field"
|
||||
|
||||
_ "embed"
|
||||
)
|
||||
|
|
@ -17,7 +17,7 @@ import (
|
|||
//go:embed "templates/totp_enable.html"
|
||||
var totpEnableHTML []byte
|
||||
var totpEnable = templating.Parse[userFormContext](
|
||||
"totp_enable.html", totpEnableHTML, httpx.FormTemplate,
|
||||
"totp_enable.html", totpEnableHTML, form.FormTemplate,
|
||||
|
||||
templating.Title("Enable TOTP"),
|
||||
templating.Assets(assets.AssetsUser),
|
||||
|
|
@ -26,19 +26,19 @@ var totpEnable = templating.Parse[userFormContext](
|
|||
func (panel *UserPanel) routeTOTPEnable(ctx context.Context) http.Handler {
|
||||
tpl := totpEnable.Prepare(panel.dependencies.Templating)
|
||||
|
||||
return &httpx.Form[struct{}]{
|
||||
return &form.Form[struct{}]{
|
||||
Fields: []field.Field{
|
||||
{Name: "password", Type: field.Password, Autocomplete: field.CurrentPassword, EmptyOnError: true, Label: "Current Password"},
|
||||
},
|
||||
FieldTemplate: field.PureCSSFieldTemplate,
|
||||
FieldTemplate: assets.PureCSSFieldTemplate,
|
||||
|
||||
SkipForm: func(r *http.Request) (data struct{}, skip bool) {
|
||||
Skip: func(r *http.Request) (data struct{}, skip bool) {
|
||||
user, err := panel.dependencies.Auth.UserOfSession(r)
|
||||
return struct{}{}, err == nil && user != nil && user.IsTOTPEnabled()
|
||||
},
|
||||
|
||||
RenderTemplate: tpl.Template(),
|
||||
RenderTemplateContext: panel.UserFormContext(tpl, menuTOTPEnable),
|
||||
Template: tpl.Template(),
|
||||
TemplateContext: panel.UserFormContext(tpl, menuTOTPEnable),
|
||||
|
||||
Validate: func(r *http.Request, values map[string]string) (struct{}, error) {
|
||||
password := values["password"]
|
||||
|
|
@ -64,7 +64,7 @@ func (panel *UserPanel) routeTOTPEnable(ctx context.Context) http.Handler {
|
|||
return struct{}{}, nil
|
||||
},
|
||||
|
||||
RenderSuccess: func(_ struct{}, values map[string]string, w http.ResponseWriter, r *http.Request) error {
|
||||
Success: func(_ struct{}, values map[string]string, w http.ResponseWriter, r *http.Request) error {
|
||||
http.Redirect(w, r, "/user/totp/enroll", http.StatusSeeOther)
|
||||
return nil
|
||||
},
|
||||
|
|
@ -74,7 +74,7 @@ func (panel *UserPanel) routeTOTPEnable(ctx context.Context) http.Handler {
|
|||
//go:embed "templates/totp_enroll.html"
|
||||
var totpEnrollHTML []byte
|
||||
var totpEnrollTemplate = templating.Parse[totpEnrollContext](
|
||||
"totp_enroll.html", totpEnrollHTML, httpx.FormTemplate,
|
||||
"totp_enroll.html", totpEnrollHTML, form.FormTemplate,
|
||||
|
||||
templating.Title("Enable TOTP"),
|
||||
templating.Assets(assets.AssetsUser),
|
||||
|
|
@ -97,18 +97,20 @@ func (panel *UserPanel) routeTOTPEnroll(ctx context.Context) http.Handler {
|
|||
),
|
||||
)
|
||||
|
||||
return &httpx.Form[struct{}]{
|
||||
return &form.Form[struct{}]{
|
||||
Fields: []field.Field{
|
||||
{Name: "password", Type: field.Password, Autocomplete: field.CurrentPassword, EmptyOnError: true, Label: "Current Password"},
|
||||
{Name: "otp", Type: field.Text, Autocomplete: field.OneTimeCode, EmptyOnError: true, Label: "Passcode"},
|
||||
},
|
||||
FieldTemplate: field.PureCSSFieldTemplate,
|
||||
FieldTemplate: assets.PureCSSFieldTemplate,
|
||||
|
||||
SkipForm: func(r *http.Request) (data struct{}, skip bool) {
|
||||
Skip: func(r *http.Request) (data struct{}, skip bool) {
|
||||
user, err := panel.dependencies.Auth.UserOfSession(r)
|
||||
return struct{}{}, err == nil && user != nil && user.IsTOTPEnabled()
|
||||
},
|
||||
RenderTemplateContext: func(context httpx.FormContext, r *http.Request) any {
|
||||
|
||||
Template: tpl.Template(),
|
||||
TemplateContext: func(context form.FormContext, r *http.Request) any {
|
||||
user, err := panel.dependencies.Auth.UserOfSession(r)
|
||||
|
||||
ctx := totpEnrollContext{
|
||||
|
|
@ -131,7 +133,6 @@ func (panel *UserPanel) routeTOTPEnroll(ctx context.Context) http.Handler {
|
|||
|
||||
return tpl.Context(r, ctx)
|
||||
},
|
||||
RenderTemplate: tpl.Template(),
|
||||
|
||||
Validate: func(r *http.Request, values map[string]string) (struct{}, error) {
|
||||
password, otp := values["password"], values["otp"]
|
||||
|
|
@ -157,7 +158,7 @@ func (panel *UserPanel) routeTOTPEnroll(ctx context.Context) http.Handler {
|
|||
return struct{}{}, nil
|
||||
},
|
||||
|
||||
RenderSuccess: func(_ struct{}, values map[string]string, w http.ResponseWriter, r *http.Request) error {
|
||||
Success: func(_ struct{}, values map[string]string, w http.ResponseWriter, r *http.Request) error {
|
||||
http.Redirect(w, r, "/user/", http.StatusSeeOther)
|
||||
return nil
|
||||
},
|
||||
|
|
@ -167,7 +168,7 @@ func (panel *UserPanel) routeTOTPEnroll(ctx context.Context) http.Handler {
|
|||
//go:embed "templates/totp_disable.html"
|
||||
var totpDisableHTML []byte
|
||||
var totpDisableTemplate = templating.Parse[userFormContext](
|
||||
"totp_disable.html", totpDisableHTML, httpx.FormTemplate,
|
||||
"totp_disable.html", totpDisableHTML, form.FormTemplate,
|
||||
|
||||
templating.Title("Disable TOTP"),
|
||||
templating.Assets(assets.AssetsUser),
|
||||
|
|
@ -176,19 +177,20 @@ var totpDisableTemplate = templating.Parse[userFormContext](
|
|||
func (panel *UserPanel) routeTOTPDisable(ctx context.Context) http.Handler {
|
||||
tpl := totpDisableTemplate.Prepare(panel.dependencies.Templating)
|
||||
|
||||
return &httpx.Form[struct{}]{
|
||||
return &form.Form[struct{}]{
|
||||
Fields: []field.Field{
|
||||
{Name: "password", Type: field.Password, Autocomplete: field.CurrentPassword, EmptyOnError: true, Label: "Current Password"},
|
||||
{Name: "otp", Type: field.Text, Autocomplete: field.OneTimeCode, EmptyOnError: true, Label: "Current Passcode"},
|
||||
},
|
||||
FieldTemplate: field.PureCSSFieldTemplate,
|
||||
FieldTemplate: assets.PureCSSFieldTemplate,
|
||||
|
||||
SkipForm: func(r *http.Request) (data struct{}, skip bool) {
|
||||
Skip: func(r *http.Request) (data struct{}, skip bool) {
|
||||
user, err := panel.dependencies.Auth.UserOfSession(r)
|
||||
return struct{}{}, err == nil && user != nil && !user.IsTOTPEnabled()
|
||||
},
|
||||
RenderTemplate: tpl.Template(),
|
||||
RenderTemplateContext: panel.UserFormContext(tpl, menuTOTPDisable),
|
||||
|
||||
Template: tpl.Template(),
|
||||
TemplateContext: panel.UserFormContext(tpl, menuTOTPDisable),
|
||||
|
||||
Validate: func(r *http.Request, values map[string]string) (struct{}, error) {
|
||||
password, otp := values["password"], values["otp"]
|
||||
|
|
@ -214,7 +216,7 @@ func (panel *UserPanel) routeTOTPDisable(ctx context.Context) http.Handler {
|
|||
return struct{}{}, nil
|
||||
},
|
||||
|
||||
RenderSuccess: func(_ struct{}, values map[string]string, w http.ResponseWriter, r *http.Request) error {
|
||||
Success: func(_ struct{}, values map[string]string, w http.ResponseWriter, r *http.Request) error {
|
||||
http.Redirect(w, r, "/user/", http.StatusSeeOther)
|
||||
return nil
|
||||
},
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ func (panel *UserPanel) routeUser(ctx context.Context) http.Handler {
|
|||
templating.Actions(actions...),
|
||||
)
|
||||
|
||||
return tpl.HTMLHandlerWithFlags(func(r *http.Request) (uc userContext, funcs []templating.FlagFunc, err error) {
|
||||
return tpl.HTMLHandlerWithFlags(panel.dependencies.Handling, func(r *http.Request) (uc userContext, funcs []templating.FlagFunc, err error) {
|
||||
// find the user
|
||||
uc.AuthUser, err = panel.dependencies.Auth.UserOfSession(r)
|
||||
if err != nil || uc.AuthUser == nil {
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ import (
|
|||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
|
||||
"github.com/tkw1536/pkglib/httpx"
|
||||
"github.com/tkw1536/pkglib/httpx/field"
|
||||
"github.com/tkw1536/pkglib/httpx/form"
|
||||
"github.com/tkw1536/pkglib/httpx/form/field"
|
||||
|
||||
"github.com/gorilla/sessions"
|
||||
|
||||
|
|
@ -162,7 +162,7 @@ func (auth *Auth) Logout(w http.ResponseWriter, r *http.Request) error {
|
|||
//go:embed "login.html"
|
||||
var loginHTML []byte
|
||||
var loginTemplate = templating.ParseForm(
|
||||
"login.html", loginHTML, httpx.FormTemplate,
|
||||
"login.html", loginHTML, form.FormTemplate,
|
||||
|
||||
templating.Title("Login Required"),
|
||||
templating.Assets(assets.AssetsUser),
|
||||
|
|
@ -182,21 +182,21 @@ func (auth *Auth) authLogin(ctx context.Context) http.Handler {
|
|||
},
|
||||
)
|
||||
|
||||
return &httpx.Form[*AuthUser]{
|
||||
return &form.Form[*AuthUser]{
|
||||
Fields: []field.Field{
|
||||
{Name: "username", Type: field.Text, Autocomplete: field.Username, Label: "Username"},
|
||||
{Name: "password", Type: field.Password, Autocomplete: field.CurrentPassword, EmptyOnError: true, Label: "Password"},
|
||||
{Name: "otp", Type: field.Text, Autocomplete: field.OneTimeCode, EmptyOnError: true, Label: "Passcode (optional)"},
|
||||
},
|
||||
FieldTemplate: field.PureCSSFieldTemplate,
|
||||
FieldTemplate: assets.PureCSSFieldTemplate,
|
||||
|
||||
RenderTemplateContext: func(ctx httpx.FormContext, r *http.Request) any {
|
||||
Template: tpl.Template(),
|
||||
TemplateContext: func(ctx form.FormContext, r *http.Request) any {
|
||||
if ctx.Err != nil {
|
||||
ctx.Err = errLoginFailed
|
||||
}
|
||||
return tpl.Context(r, templating.NewFormContext(ctx))
|
||||
},
|
||||
RenderTemplate: tpl.Template(),
|
||||
|
||||
Validate: func(r *http.Request, values map[string]string) (*AuthUser, error) {
|
||||
username, password, passcode := values["username"], values["password"], values["otp"]
|
||||
|
|
@ -215,12 +215,12 @@ func (auth *Auth) authLogin(ctx context.Context) http.Handler {
|
|||
return user, nil
|
||||
},
|
||||
|
||||
SkipForm: func(r *http.Request) (user *AuthUser, skip bool) {
|
||||
Skip: func(r *http.Request) (user *AuthUser, skip bool) {
|
||||
user, err := auth.UserOfSession(r)
|
||||
return user, err == nil && user != nil
|
||||
},
|
||||
|
||||
RenderSuccess: func(user *AuthUser, _ map[string]string, w http.ResponseWriter, r *http.Request) error {
|
||||
Success: func(user *AuthUser, _ map[string]string, w http.ResponseWriter, r *http.Request) error {
|
||||
if err := auth.Login(w, r, user); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ import (
|
|||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth/scopes"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/handling"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/tkw1536/pkglib/httpx"
|
||||
"github.com/tkw1536/pkglib/lazy"
|
||||
|
||||
_ "embed"
|
||||
|
|
@ -27,6 +27,7 @@ type Resolver struct {
|
|||
dependencies struct {
|
||||
Instances *instances.Instances
|
||||
Templating *templating.Templating
|
||||
Handling *handling.Handling
|
||||
Auth *auth.Auth
|
||||
}
|
||||
|
||||
|
|
@ -106,7 +107,7 @@ func (resolver *Resolver) HandleRoute(ctx context.Context, route string) (http.H
|
|||
if resolver.dependencies.Auth.CheckScope("", scopes.ScopeUserValid, r) != nil {
|
||||
ctx.IndexContext.Prefixes = nil
|
||||
}
|
||||
httpx.WriteHTML(tpl.Context(r, ctx), nil, t, "", w, r)
|
||||
resolver.dependencies.Handling.WriteHTML(tpl.Context(r, ctx), nil, t, w, r)
|
||||
},
|
||||
|
||||
Resolver: resolvers.InOrder{
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import (
|
|||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/tkw1536/pkglib/mux"
|
||||
"github.com/tkw1536/pkglib/httpx/mux"
|
||||
)
|
||||
|
||||
// Routeable is a component that is servable
|
||||
|
|
@ -51,10 +51,29 @@ type Routes struct {
|
|||
Decorator func(http.Handler) http.Handler
|
||||
}
|
||||
|
||||
type routeContextTyp int
|
||||
|
||||
const routeContextKey routeContextTyp = 0
|
||||
|
||||
// RouteContext represents the context passed to a given route
|
||||
type RouteContext struct {
|
||||
DefaultDomain bool
|
||||
}
|
||||
|
||||
// WithRouteContext adds the given RouteContext to the context
|
||||
func WithRouteContext(parent context.Context, value RouteContext) context.Context {
|
||||
return context.WithValue(parent, routeContextKey, value)
|
||||
}
|
||||
|
||||
// RouteContextOf returns the route context of the given context
|
||||
func RouteContextOf(context context.Context) RouteContext {
|
||||
ctx, ok := context.Value(routeContextKey).(RouteContext)
|
||||
if !ok {
|
||||
return RouteContext{}
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
// Predicate returns the predicate corresponding to the given route
|
||||
func (routes Routes) Predicate(context func(*http.Request) RouteContext) mux.Predicate {
|
||||
if routes.MatchAllDomains || routes.Internal {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth/policy"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth/scopes"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/admin/socket"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/handling"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/rs/zerolog"
|
||||
|
|
@ -20,6 +21,7 @@ import (
|
|||
type Admin struct {
|
||||
component.Base
|
||||
dependencies struct {
|
||||
Handling *handling.Handling
|
||||
Fetchers []component.DistilleryFetcher
|
||||
|
||||
Instances *instances.Instances
|
||||
|
|
@ -178,7 +180,7 @@ func (admin *Admin) HandleRoute(ctx context.Context, route string) (handler http
|
|||
func (admin *Admin) loginHandler(ctx context.Context) http.Handler {
|
||||
logger := zerolog.Ctx(ctx)
|
||||
|
||||
return httpx.RedirectHandler(func(r *http.Request) (string, int, error) {
|
||||
return admin.dependencies.Handling.Redirect(func(r *http.Request) (string, int, error) {
|
||||
// parse the form
|
||||
if err := r.ParseForm(); err != nil {
|
||||
logger.Err(err).Msg("failed to parse admin login")
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ func (admin *Admin) index(ctx context.Context) http.Handler {
|
|||
),
|
||||
)
|
||||
|
||||
return tpl.HTMLHandler(func(r *http.Request) (idx indexContext, err error) {
|
||||
return tpl.HTMLHandler(admin.dependencies.Handling, func(r *http.Request) (idx indexContext, err error) {
|
||||
idx.Distillery, idx.Instances, err = admin.Status(r.Context(), false)
|
||||
return
|
||||
})
|
||||
|
|
@ -138,7 +138,7 @@ func (admin *Admin) instances(ctx context.Context) http.Handler {
|
|||
),
|
||||
)
|
||||
|
||||
return tpl.HTMLHandler(func(r *http.Request) (idx indexContext, err error) {
|
||||
return tpl.HTMLHandler(admin.dependencies.Handling, func(r *http.Request) (idx indexContext, err error) {
|
||||
idx.Distillery, idx.Instances, err = admin.Status(r.Context(), true)
|
||||
return
|
||||
})
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ func (admin *Admin) instance(ctx context.Context) http.Handler {
|
|||
),
|
||||
)
|
||||
|
||||
return tpl.HTMLHandlerWithFlags(func(r *http.Request) (ic instanceContext, funcs []templating.FlagFunc, err error) {
|
||||
return tpl.HTMLHandlerWithFlags(admin.dependencies.Handling, func(r *http.Request) (ic instanceContext, funcs []templating.FlagFunc, err error) {
|
||||
slug := httprouter.ParamsFromContext(r.Context()).ByName("slug")
|
||||
|
||||
// find the instance itself!
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ func (admin *Admin) instanceData(ctx context.Context) http.Handler {
|
|||
),
|
||||
)
|
||||
|
||||
return tpl.HTMLHandlerWithFlags(func(r *http.Request) (ctx instanceDataContext, funcs []templating.FlagFunc, err error) {
|
||||
return tpl.HTMLHandlerWithFlags(admin.dependencies.Handling, func(r *http.Request) (ctx instanceDataContext, funcs []templating.FlagFunc, err error) {
|
||||
slug := httprouter.ParamsFromContext(r.Context()).ByName("slug")
|
||||
|
||||
// setup the context with just the instance
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ func (admin *Admin) instanceDrupal(ctx context.Context) http.Handler {
|
|||
),
|
||||
)
|
||||
|
||||
return tpl.HTMLHandlerWithFlags(func(r *http.Request) (ctx instanceDrupalContext, funcs []templating.FlagFunc, err error) {
|
||||
return tpl.HTMLHandlerWithFlags(admin.dependencies.Handling, func(r *http.Request) (ctx instanceDrupalContext, funcs []templating.FlagFunc, err error) {
|
||||
slug := httprouter.ParamsFromContext(r.Context()).ByName("slug")
|
||||
|
||||
// setup the context with just the instance
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ func (admin *Admin) instanceProvision(ctx context.Context) http.Handler {
|
|||
),
|
||||
)
|
||||
|
||||
return tpl.HTMLHandler(func(r *http.Request) (ipc instanceSystemContext, err error) {
|
||||
return tpl.HTMLHandler(admin.dependencies.Handling, func(r *http.Request) (ipc instanceSystemContext, err error) {
|
||||
ipc.prepare(false)
|
||||
ipc.DefaultProfile = manager.DefaultProfile()
|
||||
ipc.Profiles = collection.MapValues(manager.Profiles(), func(_ string, profile manager.Profile) string { return profile.Description })
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ func (admin *Admin) instancePurge(ctx context.Context) http.Handler {
|
|||
),
|
||||
)
|
||||
|
||||
return tpl.HTMLHandlerWithFlags(func(r *http.Request) (ctx instancePurgeContext, funcs []templating.FlagFunc, err error) {
|
||||
return tpl.HTMLHandlerWithFlags(admin.dependencies.Handling, func(r *http.Request) (ctx instancePurgeContext, funcs []templating.FlagFunc, err error) {
|
||||
slug := httprouter.ParamsFromContext(r.Context()).ByName("slug")
|
||||
|
||||
// setup the context with just the instance
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ func (admin *Admin) instanceRebuild(ctx context.Context) http.Handler {
|
|||
),
|
||||
)
|
||||
|
||||
return tpl.HTMLHandlerWithFlags(func(r *http.Request) (isc instanceSystemContext, funcs []templating.FlagFunc, err error) {
|
||||
return tpl.HTMLHandlerWithFlags(admin.dependencies.Handling, func(r *http.Request) (isc instanceSystemContext, funcs []templating.FlagFunc, err error) {
|
||||
slug := httprouter.ParamsFromContext(r.Context()).ByName("slug")
|
||||
|
||||
var instance *wisski.WissKI
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ func (admin *Admin) instanceSnapshots(ctx context.Context) http.Handler {
|
|||
),
|
||||
)
|
||||
|
||||
return tpl.HTMLHandlerWithFlags(func(r *http.Request) (ctx instanceSnapshotsContext, funcs []templating.FlagFunc, err error) {
|
||||
return tpl.HTMLHandlerWithFlags(admin.dependencies.Handling, func(r *http.Request) (ctx instanceSnapshotsContext, funcs []templating.FlagFunc, err error) {
|
||||
slug := httprouter.ParamsFromContext(r.Context()).ByName("slug")
|
||||
|
||||
// setup the context with just the instance
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ func (admin *Admin) instanceSSH(ctx context.Context) http.Handler {
|
|||
),
|
||||
)
|
||||
|
||||
return tpl.HTMLHandlerWithFlags(func(r *http.Request) (ctx instanceSSHContext, funcs []templating.FlagFunc, err error) {
|
||||
return tpl.HTMLHandlerWithFlags(admin.dependencies.Handling, func(r *http.Request) (ctx instanceSSHContext, funcs []templating.FlagFunc, err error) {
|
||||
slug := httprouter.ParamsFromContext(r.Context()).ByName("slug")
|
||||
|
||||
// setup the context with just the instance
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ func (admin *Admin) instanceStats(ctx context.Context) http.Handler {
|
|||
),
|
||||
)
|
||||
|
||||
return tpl.HTMLHandlerWithFlags(func(r *http.Request) (ctx instanceStatsContext, funcs []templating.FlagFunc, err error) {
|
||||
return tpl.HTMLHandlerWithFlags(admin.dependencies.Handling, func(r *http.Request) (ctx instanceStatsContext, funcs []templating.FlagFunc, err error) {
|
||||
slug := httprouter.ParamsFromContext(r.Context()).ByName("slug")
|
||||
|
||||
// setup the context with just the instance
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import (
|
|||
"github.com/FAU-CDI/wisski-distillery/internal/status"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
||||
"github.com/tkw1536/pkglib/httpx"
|
||||
"github.com/tkw1536/pkglib/httpx/field"
|
||||
"github.com/tkw1536/pkglib/httpx/form/field"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"golang.org/x/exp/maps"
|
||||
|
|
@ -55,7 +55,7 @@ func (admin *Admin) instanceUsers(ctx context.Context) http.Handler {
|
|||
),
|
||||
)
|
||||
|
||||
return tpl.HTMLHandlerWithFlags(func(r *http.Request) (instanceUsersContext, []templating.FlagFunc, error) {
|
||||
return tpl.HTMLHandlerWithFlags(admin.dependencies.Handling, func(r *http.Request) (instanceUsersContext, []templating.FlagFunc, error) {
|
||||
if r.Method == http.MethodGet {
|
||||
return admin.getGrantsUsers(r)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -10,8 +10,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/tkw1536/pkglib/httpx"
|
||||
"github.com/tkw1536/pkglib/httpx/websocket"
|
||||
)
|
||||
|
||||
// ActionMap handles a set of WebSocket actions
|
||||
|
|
@ -40,7 +39,7 @@ func (err errPanic) Error() string {
|
|||
// Finally it will send a ResultMessage once handling is complete.
|
||||
//
|
||||
// A corresponding client implementation of this can be found in ..../remote/proto.ts
|
||||
func (am ActionMap) Handle(auth *auth.Auth, conn httpx.WebSocketConnection) (name string, err error) {
|
||||
func (am ActionMap) Handle(auth *auth.Auth, conn *websocket.Connection) (name string, err error) {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// once we have finished executing send a binary message (indicating success) to the client.
|
||||
|
|
@ -67,7 +66,7 @@ func (am ActionMap) Handle(auth *auth.Auth, conn httpx.WebSocketConnection) (nam
|
|||
}
|
||||
|
||||
// encode the result message to json!
|
||||
var message httpx.WebSocketMessage
|
||||
var message websocket.Message
|
||||
message.Type = websocket.BinaryMessage
|
||||
message.Bytes, err = json.Marshal(result)
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import (
|
|||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/admin/socket/actions"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/admin/socket/proto"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/tkw1536/pkglib/httpx"
|
||||
"github.com/tkw1536/pkglib/httpx/websocket"
|
||||
"github.com/tkw1536/pkglib/lazy"
|
||||
)
|
||||
|
||||
|
|
@ -48,14 +48,14 @@ func (socket *Sockets) Routes() component.Routes {
|
|||
}
|
||||
|
||||
func (sockets *Sockets) HandleRoute(ctx context.Context, path string) (http.Handler, error) {
|
||||
return &httpx.WebSocket{
|
||||
return &websocket.Server{
|
||||
Context: ctx,
|
||||
Handler: sockets.Serve,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Serve handles a connection to the websocket api
|
||||
func (socket *Sockets) Serve(conn httpx.WebSocketConnection) {
|
||||
func (socket *Sockets) Serve(conn *websocket.Connection) {
|
||||
// handle the websocket connection!
|
||||
name, err := socket.actions.Get(func() proto.ActionMap { return socket.Actions(conn.Context()) }).Handle(socket.dependencies.Auth, conn)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ import (
|
|||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/tkw1536/pkglib/httpx"
|
||||
"github.com/tkw1536/pkglib/httpx/field"
|
||||
"github.com/tkw1536/pkglib/httpx/form"
|
||||
"github.com/tkw1536/pkglib/httpx/form/field"
|
||||
|
||||
_ "embed"
|
||||
)
|
||||
|
|
@ -43,7 +44,7 @@ func (admin *Admin) users(ctx context.Context) http.Handler {
|
|||
),
|
||||
)
|
||||
|
||||
return tpl.HTMLHandler(func(r *http.Request) (uc usersContext, err error) {
|
||||
return tpl.HTMLHandler(admin.dependencies.Handling, func(r *http.Request) (uc usersContext, err error) {
|
||||
uc.Error = r.URL.Query().Get("error")
|
||||
uc.Users, err = admin.dependencies.Auth.Users(r.Context())
|
||||
return
|
||||
|
|
@ -53,7 +54,7 @@ func (admin *Admin) users(ctx context.Context) http.Handler {
|
|||
//go:embed "html/user_create.html"
|
||||
var userCreateHTML []byte
|
||||
var userCreateTemplate = templating.ParseForm(
|
||||
"user_create.html", userCreateHTML, httpx.FormTemplate,
|
||||
"user_create.html", userCreateHTML, form.FormTemplate,
|
||||
|
||||
templating.Title("Create User"),
|
||||
templating.Assets(assets.AssetsAdmin),
|
||||
|
|
@ -80,16 +81,16 @@ func (admin *Admin) createUser(ctx context.Context) http.Handler {
|
|||
),
|
||||
)
|
||||
|
||||
return &httpx.Form[createUserResult]{
|
||||
return &form.Form[createUserResult]{
|
||||
Fields: []field.Field{
|
||||
{Name: "username", Type: field.Text, Autocomplete: field.Username, Label: "Username"},
|
||||
{Name: "password", Type: field.Password, Autocomplete: field.NewPassword, Label: "Password"},
|
||||
{Name: "admin", Type: field.Checkbox, Label: "Distillery Administrator"},
|
||||
},
|
||||
FieldTemplate: field.PureCSSFieldTemplate,
|
||||
FieldTemplate: assets.PureCSSFieldTemplate,
|
||||
|
||||
RenderTemplate: tpl.Template(),
|
||||
RenderTemplateContext: templating.FormTemplateContext(tpl),
|
||||
Template: tpl.Template(),
|
||||
TemplateContext: templating.FormTemplateContext(tpl),
|
||||
|
||||
Validate: func(r *http.Request, values map[string]string) (cu createUserResult, err error) {
|
||||
cu.User, cu.Passsword, cu.Admin = values["username"], values["password"], values["admin"] == field.CheckboxChecked
|
||||
|
|
@ -110,7 +111,7 @@ func (admin *Admin) createUser(ctx context.Context) http.Handler {
|
|||
return cu, nil
|
||||
},
|
||||
|
||||
RenderSuccess: func(cu createUserResult, values map[string]string, w http.ResponseWriter, r *http.Request) error {
|
||||
Success: func(cu createUserResult, values map[string]string, w http.ResponseWriter, r *http.Request) error {
|
||||
// create the user
|
||||
user, err := admin.dependencies.Auth.CreateUser(r.Context(), cu.User)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -21,4 +21,14 @@ type Assets struct {
|
|||
Styles template.HTML // <link> tags inserted by the asset
|
||||
}
|
||||
|
||||
var PureCSSFieldTemplate = template.Must(template.New("").Parse(`
|
||||
<div class="pure-control-group">
|
||||
<label for="{{.Name}}">{{.Label}}</label>
|
||||
{{ if (eq .Type "textarea" )}}
|
||||
<textarea name="{{.Name}}" id="{{.Name}}" placeholder="{{.Placeholder}}"{{if .Autocomplete }} autocomplete="{{.Autocomplete}}" {{end}}>{{.Value}}</textarea>
|
||||
{{ else }}
|
||||
<input type="{{.Type}}" value="{{.Value}}" name="{{.Name}}" id="{{.Name}}" placeholder="{{.Placeholder}}"{{if .Autocomplete }} autocomplete="{{.Autocomplete}}" {{end}}>
|
||||
{{ end }}
|
||||
</div>`))
|
||||
|
||||
//go:generate node build.mjs Default User Admin AdminProvision AdminRebuild
|
||||
|
|
|
|||
60
internal/dis/component/server/handling/handling.go
Normal file
60
internal/dis/component/server/handling/handling.go
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
package handling
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/tkw1536/pkglib/httpx"
|
||||
"github.com/tkw1536/pkglib/httpx/content"
|
||||
"github.com/tkw1536/pkglib/lazy"
|
||||
)
|
||||
|
||||
type Handling struct {
|
||||
component.Base
|
||||
|
||||
text lazy.Lazy[httpx.ErrInterceptor]
|
||||
html lazy.Lazy[httpx.ErrInterceptor]
|
||||
}
|
||||
|
||||
func (h *Handling) TextInterceptor() httpx.ErrInterceptor {
|
||||
return h.text.Get(func() httpx.ErrInterceptor {
|
||||
return h.interceptor(httpx.TextInterceptor)
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Handling) HTMLInterceptor() httpx.ErrInterceptor {
|
||||
return h.html.Get(func() httpx.ErrInterceptor {
|
||||
return h.interceptor(httpx.TextInterceptor)
|
||||
})
|
||||
}
|
||||
|
||||
// Interceptor returns a copy of the parent interceptor with global distillery interceptor options enabled.
|
||||
func (h *Handling) interceptor(parent httpx.ErrInterceptor) httpx.ErrInterceptor {
|
||||
pf := parent.OnFallback
|
||||
if pf == nil {
|
||||
pf = func(r *http.Request, err error) {}
|
||||
}
|
||||
|
||||
parent.RenderError = h.Config.HTTP.Debug.Set && h.Config.HTTP.Debug.Value
|
||||
parent.OnFallback = func(r *http.Request, err error) {
|
||||
pf(r, err)
|
||||
|
||||
zerolog.Ctx(r.Context()).
|
||||
Err(err).
|
||||
Str("path", r.URL.Path).
|
||||
Msg("unknown error")
|
||||
}
|
||||
return parent
|
||||
}
|
||||
|
||||
func (h *Handling) Redirect(Handler content.RedirectFunc) http.Handler {
|
||||
r := content.Redirect(Handler)
|
||||
r.Interceptor = h.TextInterceptor()
|
||||
return r
|
||||
}
|
||||
|
||||
func (h *Handling) WriteHTML(context any, err error, template *template.Template, w http.ResponseWriter, r *http.Request) error {
|
||||
return content.WriteHTMLI(context, err, template, h.HTMLInterceptor(), w, r)
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/handling"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/list"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
|
||||
)
|
||||
|
|
@ -15,6 +16,7 @@ type Home struct {
|
|||
dependencies struct {
|
||||
ListInstances *list.ListInstances
|
||||
Templating *templating.Templating
|
||||
Handling *handling.Handling
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ func (home *Home) publicHandler(ctx context.Context) http.Handler {
|
|||
|
||||
about := home.dependencies.Templating.GetCustomizable(aboutTemplate)
|
||||
|
||||
return tpl.HTMLHandler(func(r *http.Request) (pc publicContext, err error) {
|
||||
return tpl.HTMLHandler(home.dependencies.Handling, func(r *http.Request) (pc publicContext, err error) {
|
||||
// only act on the root path!
|
||||
if strings.TrimSuffix(r.URL.Path, "/") != "" {
|
||||
return pc, httpx.ErrNotFound
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/handling"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
|
||||
|
||||
_ "embed"
|
||||
|
|
@ -18,6 +19,7 @@ type Legal struct {
|
|||
dependencies struct {
|
||||
Static *assets.Static
|
||||
Templating *templating.Templating
|
||||
Handling *handling.Handling
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -65,7 +67,7 @@ func (legal *Legal) HandleRoute(ctx context.Context, route string) (http.Handler
|
|||
),
|
||||
)
|
||||
|
||||
return tpl.HTMLHandler(func(r *http.Request) (lc legalContext, err error) {
|
||||
return tpl.HTMLHandler(legal.dependencies.Handling, func(r *http.Request) (lc legalContext, err error) {
|
||||
lc.LegalNotices = cli.LegalNotices
|
||||
|
||||
lc.CSRFCookie = server.CSRFCookie
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/handling"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/yuin/goldmark"
|
||||
|
|
@ -23,6 +24,7 @@ type News struct {
|
|||
component.Base
|
||||
dependencies struct {
|
||||
Templating *templating.Templating
|
||||
Handling *handling.Handling
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -143,7 +145,7 @@ func (news *News) HandleRoute(ctx context.Context, path string) (http.Handler, e
|
|||
zerolog.Ctx(ctx).Err(itemsErr).Msg("Unable to load news items")
|
||||
}
|
||||
|
||||
return tpl.HTMLHandler(func(r *http.Request) (nc newsContext, err error) {
|
||||
return tpl.HTMLHandler(news.dependencies.Handling, func(r *http.Request) (nc newsContext, err error) {
|
||||
nc.Items, err = items, itemsErr
|
||||
return
|
||||
}), nil
|
||||
|
|
|
|||
|
|
@ -2,18 +2,17 @@ package server
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/handling"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||
"github.com/tkw1536/pkglib/contextx"
|
||||
"github.com/tkw1536/pkglib/httpx"
|
||||
"github.com/tkw1536/pkglib/httpx/timewrap"
|
||||
"github.com/tkw1536/pkglib/mux"
|
||||
"github.com/tkw1536/pkglib/httpx/mux"
|
||||
"github.com/tkw1536/pkglib/httpx/wrap"
|
||||
"github.com/tkw1536/pkglib/recovery"
|
||||
|
||||
"github.com/gorilla/csrf"
|
||||
"github.com/rs/zerolog"
|
||||
|
|
@ -27,6 +26,7 @@ type Server struct {
|
|||
Cronables []component.Cronable
|
||||
|
||||
Templating *templating.Templating
|
||||
Handleing *handling.Handling
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -39,30 +39,33 @@ var (
|
|||
//
|
||||
// Logging messages are directed to progress
|
||||
func (server *Server) Server(ctx context.Context, progress io.Writer) (public http.Handler, internal http.Handler, err error) {
|
||||
logger := zerolog.Ctx(ctx)
|
||||
interceptor := server.dependencies.Handleing.TextInterceptor()
|
||||
|
||||
var publicM, internalM mux.Mux[component.RouteContext]
|
||||
publicM.Context = func(r *http.Request) component.RouteContext {
|
||||
slug, ok := server.Config.HTTP.NormSlugFromHost(r.Host)
|
||||
return component.RouteContext{
|
||||
DefaultDomain: slug == "" && ok,
|
||||
}
|
||||
}
|
||||
publicM.Panic = func(p any, stack []byte, w http.ResponseWriter, r *http.Request) {
|
||||
// log the panic
|
||||
logger.Error().
|
||||
Str("panic", fmt.Sprint(p)).
|
||||
Str("stack", string(stack)).
|
||||
Str("path", r.URL.Path).
|
||||
Msg("panic serving handler")
|
||||
// wrapHandler wraps individual handlers for errors
|
||||
wrapHandler := func(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// handle any panic()s that occur
|
||||
defer func() {
|
||||
// intercept any panic() that wasn't caught
|
||||
if err := recovery.Recover(recover()); err != nil {
|
||||
interceptor.Intercept(w, r, err)
|
||||
}
|
||||
}()
|
||||
|
||||
// and send an internal server error
|
||||
httpx.TextInterceptor.Fallback.ServeHTTP(w, r)
|
||||
// determine if we are on a slug from a host
|
||||
slug, ok := server.Config.HTTP.NormSlugFromHost(r.Host)
|
||||
|
||||
rctx := component.WithRouteContext(r.Context(), component.RouteContext{
|
||||
DefaultDomain: slug == "" && ok,
|
||||
})
|
||||
ctx := contextx.WithValuesOf(rctx, ctx)
|
||||
|
||||
// serve with the next context
|
||||
h.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
// setup the internal server identically
|
||||
internalM.Panic = publicM.Panic
|
||||
internalM.Context = publicM.Context
|
||||
var publicM, internalM mux.Mux
|
||||
|
||||
// create a csrf protector
|
||||
csrfProtector := server.csrf()
|
||||
|
|
@ -95,7 +98,7 @@ func (server *Server) Server(ctx context.Context, progress io.Writer) (public ht
|
|||
handler = routes.Decorate(handler, csrfProtector)
|
||||
|
||||
// determine the predicate
|
||||
predicate := routes.Predicate(publicM.ContextOf)
|
||||
predicate := routes.Predicate(func(r *http.Request) component.RouteContext { return component.RouteContextOf(r.Context()) })
|
||||
|
||||
// and add all the prefixes
|
||||
for _, prefix := range append([]string{routes.Prefix}, routes.Aliases...) {
|
||||
|
|
@ -107,15 +110,15 @@ func (server *Server) Server(ctx context.Context, progress io.Writer) (public ht
|
|||
}
|
||||
}
|
||||
|
||||
// apply the given context function
|
||||
public = httpx.WithContextWrapper(&publicM, func(rcontext context.Context) context.Context { return contextx.WithValuesOf(rcontext, ctx) })
|
||||
internal = httpx.WithContextWrapper(&internalM, func(rcontext context.Context) context.Context { return contextx.WithValuesOf(rcontext, ctx) })
|
||||
// wrap the handlers
|
||||
public = wrapHandler(&publicM)
|
||||
internal = wrapHandler(&internalM)
|
||||
|
||||
// Add Content-Security-Policy
|
||||
public = WithCSP(public, models.ContentSecurityPolicyDistilery)
|
||||
internal = WithCSP(internal, models.ContentSecurityPolicyNothing)
|
||||
|
||||
public = timewrap.Wrap(public)
|
||||
public = wrap.Time(public)
|
||||
|
||||
err = nil
|
||||
return
|
||||
|
|
@ -141,10 +144,3 @@ func WithCSP(handler http.Handler, policy string) http.Handler {
|
|||
handler.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func init() {
|
||||
httpx.InterceptorOnFallback = func(req *http.Request, err error) {
|
||||
stack := debug.Stack()
|
||||
zerolog.Ctx(req.Context()).Err(err).Str("stack", string(stack)).Msg("unknown error intercepted")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,10 +11,12 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/handling"
|
||||
"github.com/gorilla/csrf"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/tkw1536/pkglib/httpx"
|
||||
"github.com/tkw1536/pkglib/httpx/timewrap"
|
||||
"github.com/tkw1536/pkglib/httpx/content"
|
||||
"github.com/tkw1536/pkglib/httpx/form"
|
||||
"github.com/tkw1536/pkglib/httpx/wrap"
|
||||
)
|
||||
|
||||
//go:embed "src/base.html"
|
||||
|
|
@ -46,7 +48,7 @@ func (tpl *Template[C]) context(r *http.Request, funcs ...FlagFunc) (ctx *tConte
|
|||
// setup the basic properties
|
||||
ctx.ctx = r.Context()
|
||||
ctx.Runtime.RequestURI = r.URL.RequestURI()
|
||||
ctx.Runtime.StartedAt = timewrap.Start(r).UTC()
|
||||
ctx.Runtime.StartedAt = wrap.TimeStart(r).UTC()
|
||||
ctx.Runtime.GeneratedAt = time.Now().UTC()
|
||||
ctx.Runtime.CSRF = csrf.TemplateField(r)
|
||||
ctx.Runtime.Menu = tpl.templating.buildMenu(r)
|
||||
|
|
@ -70,19 +72,19 @@ func (tpl *Template[C]) context(r *http.Request, funcs ...FlagFunc) (ctx *tConte
|
|||
var ParseForm = Parse[FormContext]
|
||||
|
||||
type FormContext struct {
|
||||
httpx.FormContext
|
||||
form.FormContext
|
||||
RuntimeFlags
|
||||
}
|
||||
|
||||
// NewFormContext returns a new FormContext from an underlying context
|
||||
func NewFormContext(context httpx.FormContext) FormContext {
|
||||
func NewFormContext(context form.FormContext) FormContext {
|
||||
return FormContext{FormContext: context}
|
||||
}
|
||||
|
||||
// FormTemplateContext returns a new handler for a form with the given base context
|
||||
func FormTemplateContext(tw *Template[FormContext]) func(ctx httpx.FormContext, r *http.Request) any {
|
||||
func FormTemplateContext(tw *Template[FormContext]) func(ctx form.FormContext, r *http.Request) any {
|
||||
// TODO: Is this needed?
|
||||
return func(ctx httpx.FormContext, r *http.Request) any {
|
||||
return func(ctx form.FormContext, r *http.Request) any {
|
||||
return tw.Context(r, FormContext{FormContext: ctx})
|
||||
}
|
||||
}
|
||||
|
|
@ -113,19 +115,21 @@ func (tw *Template[C]) HandlerWithFlags(worker func(r *http.Request) (C, []FlagF
|
|||
|
||||
// HTMLHandler creates a new httpx.HTMLHandler that calls tw.Handler(worker) and tw.Template.
|
||||
// See also Handler.
|
||||
func (tw *Template[C]) HTMLHandler(worker func(r *http.Request) (C, error)) httpx.HTMLHandler[any] {
|
||||
return httpx.HTMLHandler[any]{
|
||||
Handler: tw.Handler(worker),
|
||||
Template: tw.Template(),
|
||||
func (tw *Template[C]) HTMLHandler(handling *handling.Handling, worker func(r *http.Request) (C, error)) content.HTMLHandler[any] {
|
||||
return content.HTMLHandler[any]{
|
||||
Handler: tw.Handler(worker),
|
||||
Template: tw.Template(),
|
||||
Interceptor: handling.HTMLInterceptor(),
|
||||
}
|
||||
}
|
||||
|
||||
// HTMLHandlerWithFlags creates a new httpx.HTMLHandler that calls tw.HandlerWithFlags(worker) and tw.Template.
|
||||
// See also HandlerWithFlags.
|
||||
func (tw *Template[C]) HTMLHandlerWithFlags(worker func(r *http.Request) (C, []FlagFunc, error)) httpx.HTMLHandler[any] {
|
||||
return httpx.HTMLHandler[any]{
|
||||
Handler: tw.HandlerWithFlags(worker),
|
||||
Template: tw.Template(),
|
||||
func (tw *Template[C]) HTMLHandlerWithFlags(handling *handling.Handling, worker func(r *http.Request) (C, []FlagFunc, error)) content.HTMLHandler[any] {
|
||||
return content.HTMLHandler[any]{
|
||||
Handler: tw.HandlerWithFlags(worker),
|
||||
Template: tw.Template(),
|
||||
Interceptor: handling.HTMLInterceptor(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
||||
"github.com/tkw1536/pkglib/mux"
|
||||
"github.com/tkw1536/pkglib/httpx/mux"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue