add user config page and admin menu
This commit is contained in:
97
webcontroller/admin_panel.go
Normal file
97
webcontroller/admin_panel.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package webcontroller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"fornaxian.com/pixeldrain-web/pixelapi"
|
||||
"fornaxian.com/pixeldrain-web/webcontroller/forms"
|
||||
"github.com/Fornaxian/log"
|
||||
)
|
||||
|
||||
func (wc *WebController) adminGlobalsForm(td *TemplateData, r *http.Request) (f forms.Form) {
|
||||
if isAdmin, err := td.PixelAPI.UserIsAdmin(); err != nil {
|
||||
td.Title = err.Error()
|
||||
return forms.Form{Title: td.Title}
|
||||
} else if !isAdmin.IsAdmin {
|
||||
td.Title = ";)"
|
||||
return forms.Form{Title: td.Title}
|
||||
}
|
||||
|
||||
td.Title = "Pixeldrain global configuration"
|
||||
f = forms.Form{
|
||||
Name: "admin_globals",
|
||||
Title: td.Title,
|
||||
PreFormHTML: template.HTML("<p>Careful! The slightest typing error could bring the whole website down</p>"),
|
||||
BackLink: "/admin",
|
||||
SubmitLabel: "Submit",
|
||||
}
|
||||
|
||||
globals, err := td.PixelAPI.AdminGetGlobals()
|
||||
if err != nil {
|
||||
f.SubmitMessages = []template.HTML{template.HTML(err.Error())}
|
||||
return f
|
||||
}
|
||||
var globalsMap = make(map[string]string)
|
||||
for _, v := range globals.Globals {
|
||||
f.Fields = append(f.Fields, forms.Field{
|
||||
Name: v.Key,
|
||||
DefaultValue: v.Value,
|
||||
Label: v.Key,
|
||||
Type: func() forms.FieldType {
|
||||
switch v.Key {
|
||||
case
|
||||
"email_address_change_body",
|
||||
"email_password_reset_body":
|
||||
return forms.FieldTypeTextarea
|
||||
case
|
||||
"api_ratelimit_limit",
|
||||
"api_ratelimit_rate",
|
||||
"cron_interval_seconds",
|
||||
"file_inactive_expiry_days",
|
||||
"max_file_size",
|
||||
"pixelstore_min_redundancy":
|
||||
return forms.FieldTypeNumber
|
||||
default:
|
||||
return forms.FieldTypeText
|
||||
}
|
||||
}(),
|
||||
})
|
||||
globalsMap[v.Key] = v.Value
|
||||
}
|
||||
|
||||
if f.ReadInput(r) {
|
||||
var successfulUpdates = 0
|
||||
for k, v := range f.Fields {
|
||||
if v.EnteredValue == globalsMap[v.Name] {
|
||||
continue // Change changes, no need to update
|
||||
}
|
||||
|
||||
// Value changed, try to update global setting
|
||||
if _, err = td.PixelAPI.AdminSetGlobals(v.Name, v.EnteredValue); err != nil {
|
||||
if apiErr, ok := err.(pixelapi.Error); ok {
|
||||
f.SubmitMessages = append(f.SubmitMessages, template.HTML(apiErr.Message))
|
||||
} else {
|
||||
log.Error("%s", err)
|
||||
f.SubmitMessages = append(f.SubmitMessages, template.HTML(
|
||||
fmt.Sprintf("Failed to set '%s': %s", v.Name, err),
|
||||
))
|
||||
return f
|
||||
}
|
||||
} else {
|
||||
f.Fields[k].DefaultValue = v.EnteredValue
|
||||
successfulUpdates++
|
||||
}
|
||||
|
||||
}
|
||||
if len(f.SubmitMessages) == 0 {
|
||||
// Request was a success
|
||||
f.SubmitSuccess = true
|
||||
f.SubmitMessages = []template.HTML{template.HTML(
|
||||
fmt.Sprintf("Success! %d values updated", successfulUpdates),
|
||||
)}
|
||||
}
|
||||
}
|
||||
return f
|
||||
}
|
@@ -79,6 +79,8 @@ type FieldType string
|
||||
// Fields which can be in a form
|
||||
const (
|
||||
FieldTypeText FieldType = "text"
|
||||
FieldTypeTextarea FieldType = "textarea"
|
||||
FieldTypeNumber FieldType = "number"
|
||||
FieldTypeUsername FieldType = "username"
|
||||
FieldTypeEmail FieldType = "email"
|
||||
FieldTypeCurrentPassword FieldType = "current-password"
|
||||
|
@@ -16,6 +16,7 @@ import (
|
||||
type TemplateData struct {
|
||||
Authenticated bool
|
||||
Username string
|
||||
Email string
|
||||
UserAgent string
|
||||
UserStyle template.CSS
|
||||
APIEndpoint template.URL
|
||||
@@ -67,6 +68,7 @@ func (wc *WebController) newTemplateData(w http.ResponseWriter, r *http.Request)
|
||||
// Authentication succeeded
|
||||
t.Authenticated = true
|
||||
t.Username = uinf.Username
|
||||
t.Email = uinf.Email
|
||||
} else {
|
||||
t.PixelAPI = pixelapi.New(wc.conf.APIURLInternal, "")
|
||||
}
|
||||
|
@@ -18,9 +18,8 @@ func (wc *WebController) serveLogout(
|
||||
) {
|
||||
if key, err := wc.getAPIKey(r); err == nil {
|
||||
var api = pixelapi.New(wc.conf.APIURLInternal, key)
|
||||
_, err1 := api.UserSessionDestroy(key)
|
||||
if err1 != nil {
|
||||
log.Warn("logout failed for session '%s': %s", key, err1)
|
||||
if err = api.UserSessionDestroy(key); err != nil {
|
||||
log.Warn("logout failed for session '%s': %s", key, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,9 +150,12 @@ func (wc *WebController) loginForm(td *TemplateData, r *http.Request) (f forms.F
|
||||
BackLink: "/",
|
||||
SubmitLabel: "Login",
|
||||
PostFormHTML: template.HTML(
|
||||
`<br/>If you don't have a pixeldrain account yet, you can ` +
|
||||
`<p>If you don't have a pixeldrain account yet, you can ` +
|
||||
`<a href="/register">register here</a>. No e-mail address is ` +
|
||||
`required.<br/>`,
|
||||
`required.</p>` +
|
||||
`<p>Forgot your password? If your account has a valid e-mail ` +
|
||||
`address you can <a href="/password_reset">request a new ` +
|
||||
`password here</a>.</p>`,
|
||||
),
|
||||
}
|
||||
|
||||
@@ -184,45 +186,35 @@ func (wc *WebController) loginForm(td *TemplateData, r *http.Request) (f forms.F
|
||||
return f
|
||||
}
|
||||
|
||||
func (wc *WebController) passwordForm(td *TemplateData, r *http.Request) (f forms.Form) {
|
||||
td.Title = "Change Password"
|
||||
func (wc *WebController) passwordResetForm(td *TemplateData, r *http.Request) (f forms.Form) {
|
||||
td.Title = "Recover lost password"
|
||||
f = forms.Form{
|
||||
Name: "password_change",
|
||||
Name: "password_reset",
|
||||
Title: td.Title,
|
||||
Fields: []forms.Field{
|
||||
{
|
||||
Name: "old_password",
|
||||
Label: "Old Password",
|
||||
Type: forms.FieldTypeCurrentPassword,
|
||||
Name: "email",
|
||||
Label: "E-mail address",
|
||||
Description: "we will send a password reset link to this " +
|
||||
"e-mail address",
|
||||
Separator: true,
|
||||
Type: forms.FieldTypeEmail,
|
||||
}, {
|
||||
Name: "new_password1",
|
||||
Label: "New Password",
|
||||
Type: forms.FieldTypeNewPassword,
|
||||
}, {
|
||||
Name: "new_password2",
|
||||
Label: "New Password verification",
|
||||
Type: forms.FieldTypeCurrentPassword,
|
||||
Name: "recaptcha_response",
|
||||
Label: "Turing test (click the white box)",
|
||||
Description: "the reCaptcha turing test verifies that you " +
|
||||
"are not an evil robot that is trying hijack accounts",
|
||||
Separator: true,
|
||||
Type: forms.FieldTypeCaptcha,
|
||||
CaptchaSiteKey: wc.captchaKey(),
|
||||
},
|
||||
},
|
||||
BackLink: "/user",
|
||||
BackLink: "/login",
|
||||
SubmitLabel: "Submit",
|
||||
}
|
||||
|
||||
if f.ReadInput(r) {
|
||||
if f.FieldVal("new_password1") != f.FieldVal("new_password2") {
|
||||
f.SubmitMessages = []template.HTML{
|
||||
"Password verification failed. Please enter the same " +
|
||||
"password in both new password fields"}
|
||||
return f
|
||||
}
|
||||
|
||||
// Passwords match, send the request and fill in the response in the
|
||||
// form
|
||||
_, err := td.PixelAPI.UserPasswordSet(
|
||||
f.FieldVal("old_password"),
|
||||
f.FieldVal("new_password1"),
|
||||
)
|
||||
if err != nil {
|
||||
if err := td.PixelAPI.UserPasswordReset(f.FieldVal("email"), f.FieldVal("recaptcha_response")); err != nil {
|
||||
if apiErr, ok := err.(pixelapi.Error); ok {
|
||||
f.SubmitMessages = []template.HTML{template.HTML(apiErr.Message)}
|
||||
} else {
|
||||
@@ -230,9 +222,8 @@ func (wc *WebController) passwordForm(td *TemplateData, r *http.Request) (f form
|
||||
f.SubmitMessages = []template.HTML{"Internal Server Error"}
|
||||
}
|
||||
} else {
|
||||
// Request was a success
|
||||
f.SubmitSuccess = true
|
||||
f.SubmitMessages = []template.HTML{"Success! Your password has been updated"}
|
||||
f.SubmitMessages = []template.HTML{"Success! E-mail sent"}
|
||||
}
|
||||
}
|
||||
return f
|
||||
|
188
webcontroller/user_settings.go
Normal file
188
webcontroller/user_settings.go
Normal file
@@ -0,0 +1,188 @@
|
||||
package webcontroller
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"fornaxian.com/pixeldrain-web/pixelapi"
|
||||
"fornaxian.com/pixeldrain-web/webcontroller/forms"
|
||||
"github.com/Fornaxian/log"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
)
|
||||
|
||||
func (wc *WebController) serveUserSettings(
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
p httprouter.Params,
|
||||
) {
|
||||
td := wc.newTemplateData(w, r)
|
||||
|
||||
if !td.Authenticated {
|
||||
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
td.Title = "Account settings"
|
||||
td.Other = struct {
|
||||
PasswordForm forms.Form
|
||||
EmailForm forms.Form
|
||||
UsernameForm forms.Form
|
||||
}{
|
||||
PasswordForm: wc.passwordForm(td, r),
|
||||
EmailForm: wc.emailForm(td, r),
|
||||
UsernameForm: wc.usernameForm(td, r),
|
||||
}
|
||||
wc.templates.Get().ExecuteTemplate(w, "user_settings", td)
|
||||
}
|
||||
|
||||
func (wc *WebController) passwordForm(td *TemplateData, r *http.Request) (f forms.Form) {
|
||||
f = forms.Form{
|
||||
Name: "password_change",
|
||||
Title: "Change password",
|
||||
Fields: []forms.Field{
|
||||
{
|
||||
Name: "old_password",
|
||||
Label: "Old Password",
|
||||
Type: forms.FieldTypeCurrentPassword,
|
||||
}, {
|
||||
Name: "new_password1",
|
||||
Label: "New Password",
|
||||
Type: forms.FieldTypeNewPassword,
|
||||
}, {
|
||||
Name: "new_password2",
|
||||
Label: "New Password again",
|
||||
Description: "we need you to repeat your password so you " +
|
||||
"won't be locked out of your account if you make a " +
|
||||
"typing error",
|
||||
Type: forms.FieldTypeNewPassword,
|
||||
},
|
||||
},
|
||||
SubmitLabel: "Submit",
|
||||
}
|
||||
|
||||
if f.ReadInput(r) {
|
||||
if f.FieldVal("new_password1") != f.FieldVal("new_password2") {
|
||||
f.SubmitMessages = []template.HTML{
|
||||
"Password verification failed. Please enter the same " +
|
||||
"password in both new password fields"}
|
||||
return f
|
||||
}
|
||||
|
||||
// Passwords match, send the request and fill in the response in the
|
||||
// form
|
||||
if err := td.PixelAPI.UserPasswordSet(
|
||||
f.FieldVal("old_password"),
|
||||
f.FieldVal("new_password1"),
|
||||
); err != nil {
|
||||
if apiErr, ok := err.(pixelapi.Error); ok {
|
||||
f.SubmitMessages = []template.HTML{template.HTML(apiErr.Message)}
|
||||
} else {
|
||||
log.Error("%s", err)
|
||||
f.SubmitMessages = []template.HTML{"Internal Server Error"}
|
||||
}
|
||||
} else {
|
||||
// Request was a success
|
||||
f.SubmitSuccess = true
|
||||
f.SubmitMessages = []template.HTML{"Success! Your password has been updated"}
|
||||
}
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func (wc *WebController) emailForm(td *TemplateData, r *http.Request) (f forms.Form) {
|
||||
f = forms.Form{
|
||||
Name: "email_change",
|
||||
Title: "Change e-mail address",
|
||||
Fields: []forms.Field{
|
||||
{
|
||||
Name: "new_email",
|
||||
Label: "New e-mail address",
|
||||
Description: "we will send an e-mail to the new address to " +
|
||||
"verify that it's real. The address will be saved once " +
|
||||
"the link in the message is clicked. If the e-mail " +
|
||||
"doesn't arrive right away please check your spam box too",
|
||||
Type: forms.FieldTypeEmail,
|
||||
},
|
||||
},
|
||||
SubmitLabel: "Submit",
|
||||
}
|
||||
|
||||
if f.ReadInput(r) {
|
||||
if err := td.PixelAPI.UserEmailReset(
|
||||
f.FieldVal("new_email"),
|
||||
false,
|
||||
); err != nil {
|
||||
if apiErr, ok := err.(pixelapi.Error); ok {
|
||||
f.SubmitMessages = []template.HTML{template.HTML(apiErr.Message)}
|
||||
} else {
|
||||
log.Error("%s", err)
|
||||
f.SubmitMessages = []template.HTML{"Internal Server Error"}
|
||||
}
|
||||
} else {
|
||||
// Request was a success
|
||||
f.SubmitSuccess = true
|
||||
f.SubmitMessages = []template.HTML{"Success! E-mail sent"}
|
||||
}
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func (wc *WebController) serveEmailConfirm(
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
p httprouter.Params,
|
||||
) {
|
||||
var status string
|
||||
if key, err := wc.getAPIKey(r); err == nil {
|
||||
err = pixelapi.New(wc.conf.APIURLInternal, key).UserEmailResetConfirm(r.FormValue("key"))
|
||||
if err != nil && err.Error() == "not_found" {
|
||||
status = "not_found"
|
||||
} else if err != nil {
|
||||
status = "internal_error"
|
||||
} else {
|
||||
status = "success"
|
||||
}
|
||||
}
|
||||
|
||||
td := wc.newTemplateData(w, r)
|
||||
td.Other = status
|
||||
|
||||
wc.templates.Get().ExecuteTemplate(w, "email_confirm", td)
|
||||
}
|
||||
|
||||
func (wc *WebController) usernameForm(td *TemplateData, r *http.Request) (f forms.Form) {
|
||||
f = forms.Form{
|
||||
Name: "username_change",
|
||||
Title: "Change username",
|
||||
Fields: []forms.Field{
|
||||
{
|
||||
Name: "new_username",
|
||||
Label: "New username",
|
||||
Description: "changing your username also changes the name " +
|
||||
"used to log in. If you forget your username you can " +
|
||||
"still log in using your e-mail address if you have one " +
|
||||
"configured",
|
||||
Type: forms.FieldTypeUsername,
|
||||
},
|
||||
},
|
||||
SubmitLabel: "Submit",
|
||||
}
|
||||
|
||||
if f.ReadInput(r) {
|
||||
if err := td.PixelAPI.UserSetUsername(f.FieldVal("new_username")); err != nil {
|
||||
if apiErr, ok := err.(pixelapi.Error); ok {
|
||||
f.SubmitMessages = []template.HTML{template.HTML(apiErr.Message)}
|
||||
} else {
|
||||
log.Error("%s", err)
|
||||
f.SubmitMessages = []template.HTML{"Internal Server Error"}
|
||||
}
|
||||
} else {
|
||||
// Request was a success
|
||||
f.SubmitSuccess = true
|
||||
f.SubmitMessages = []template.HTML{template.HTML(
|
||||
"Success! You are now " + f.FieldVal("new_username"),
|
||||
)}
|
||||
}
|
||||
}
|
||||
return f
|
||||
}
|
@@ -78,7 +78,9 @@ func New(r *httprouter.Router, prefix string, conf *conf.PixelWebConfig) *WebCon
|
||||
r.GET(p+"/register" /* */, wc.serveForm(wc.registerForm, false))
|
||||
r.POST(p+"/register" /* */, wc.serveForm(wc.registerForm, false))
|
||||
r.GET(p+"/login" /* */, wc.serveForm(wc.loginForm, false))
|
||||
r.POST(p+"/login" /* */, wc.serveForm(wc.loginForm, false))
|
||||
r.POST(p+"/login" /* */, wc.serveForm(wc.loginForm, false))
|
||||
r.GET(p+"/password_reset" /* */, wc.serveForm(wc.passwordResetForm, false))
|
||||
r.POST(p+"/password_reset" /* */, wc.serveForm(wc.passwordResetForm, false))
|
||||
r.GET(p+"/logout" /* */, wc.serveTemplate("logout", true))
|
||||
r.POST(p+"/logout" /* */, wc.serveLogout)
|
||||
r.GET(p+"/user" /* */, wc.serveTemplate("user_home", true))
|
||||
@@ -87,11 +89,14 @@ func New(r *httprouter.Router, prefix string, conf *conf.PixelWebConfig) *WebCon
|
||||
r.GET(p+"/user/filemanager" /**/, wc.serveTemplate("file_manager", true))
|
||||
|
||||
// User account settings
|
||||
r.GET(p+"/user/settings" /* */, wc.serveTemplate("user_settings", true))
|
||||
r.GET(p+"/user/change_password" /* */, wc.serveForm(wc.passwordForm, true))
|
||||
r.POST(p+"/user/change_password" /**/, wc.serveForm(wc.passwordForm, true))
|
||||
r.GET(p+"/user/settings" /* */, wc.serveUserSettings)
|
||||
r.POST(p+"/user/settings" /* */, wc.serveUserSettings)
|
||||
r.GET(p+"/user/confirm_email" /**/, wc.serveEmailConfirm)
|
||||
|
||||
r.GET(p+"/admin", wc.serveTemplate("admin_panel", true))
|
||||
// Admin settings
|
||||
r.GET(p+"/admin" /* */, wc.serveTemplate("admin_panel", true))
|
||||
r.GET(p+"/admin/globals" /* */, wc.serveForm(wc.adminGlobalsForm, true))
|
||||
r.POST(p+"/admin/globals" /**/, wc.serveForm(wc.adminGlobalsForm, true))
|
||||
|
||||
r.NotFound = http.HandlerFunc(wc.serveNotFound)
|
||||
|
||||
|
Reference in New Issue
Block a user