Convert registration and rassword reset forms to new form system

This commit is contained in:
2019-02-25 22:53:09 +01:00
parent 185cc4dc60
commit b78a9d9e51
10 changed files with 405 additions and 174 deletions

View File

@@ -44,7 +44,8 @@ func (e Error) Error() string { return e.Value }
// SuccessResponse is a generic response the API returns when the action was // SuccessResponse is a generic response the API returns when the action was
// successful and there is nothing interesting to report // successful and there is nothing interesting to report
type SuccessResponse struct { type SuccessResponse struct {
Success bool `json:"success"` Success bool `json:"success"`
Message string `json:"message"`
} }
func (p *PixelAPI) jsonRequest(method, url string, target interface{}) error { func (p *PixelAPI) jsonRequest(method, url string, target interface{}) error {
@@ -72,7 +73,7 @@ func (p *PixelAPI) jsonRequest(method, url string, target interface{}) error {
} }
defer resp.Body.Close() defer resp.Body.Close()
return parseJSONResponse(resp, target) return parseJSONResponse(resp, target, true)
} }
func (p *PixelAPI) getString(url string) (string, error) { func (p *PixelAPI) getString(url string) (string, error) {
@@ -84,8 +85,6 @@ func (p *PixelAPI) getString(url string) (string, error) {
req.SetBasicAuth("", p.apiKey) req.SetBasicAuth("", p.apiKey)
} }
client := &http.Client{}
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return "", err return "", err
@@ -115,10 +114,16 @@ func (p *PixelAPI) getRaw(url string) (io.ReadCloser, error) {
return resp.Body, err return resp.Body, err
} }
func (p *PixelAPI) postForm(url string, vals url.Values, target interface{}) error { func (p *PixelAPI) form(
req, err := http.NewRequest("POST", url, strings.NewReader(vals.Encode())) method string,
url string,
vals url.Values,
target interface{},
catchErrors bool,
) error {
req, err := http.NewRequest(method, url, strings.NewReader(vals.Encode()))
if err != nil { if err != nil {
return &Error{ return Error{
ReqError: true, ReqError: true,
Success: false, Success: false,
Value: err.Error(), Value: err.Error(),
@@ -129,9 +134,11 @@ func (p *PixelAPI) postForm(url string, vals url.Values, target interface{}) err
req.SetBasicAuth("", p.apiKey) req.SetBasicAuth("", p.apiKey)
} }
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return &Error{ return Error{
ReqError: true, ReqError: true,
Success: false, Success: false,
Value: err.Error(), Value: err.Error(),
@@ -140,22 +147,22 @@ func (p *PixelAPI) postForm(url string, vals url.Values, target interface{}) err
} }
defer resp.Body.Close() defer resp.Body.Close()
return parseJSONResponse(resp, target) return parseJSONResponse(resp, target, catchErrors)
} }
func parseJSONResponse(resp *http.Response, target interface{}) error { func parseJSONResponse(resp *http.Response, target interface{}, catchErrors bool) error {
var jdec = json.NewDecoder(resp.Body) var jdec = json.NewDecoder(resp.Body)
var err error var err error
// Test for client side and server side errors // Test for client side and server side errors
if resp.StatusCode >= 400 { if catchErrors && resp.StatusCode >= 400 {
var errResp = &Error{ var errResp = Error{
ReqError: false, ReqError: false,
} }
err = jdec.Decode(&errResp) err = jdec.Decode(&errResp)
if err != nil { if err != nil {
log.Error("Can't decode this: %v", err) log.Error("Can't decode this: %v", err)
return &Error{ return Error{
ReqError: true, ReqError: true,
Success: false, Success: false,
Value: err.Error(), Value: err.Error(),
@@ -169,7 +176,7 @@ func parseJSONResponse(resp *http.Response, target interface{}) error {
if err != nil { if err != nil {
r, _ := ioutil.ReadAll(resp.Body) r, _ := ioutil.ReadAll(resp.Body)
log.Error("Can't decode this: %v. %s", err, r) log.Error("Can't decode this: %v. %s", err, r)
return &Error{ return Error{
ReqError: true, ReqError: true,
Success: false, Success: false,
Value: err.Error(), Value: err.Error(),

View File

@@ -5,22 +5,24 @@ import (
"net/url" "net/url"
) )
// Registration is the response to the UserRegister API. The register API can
// return multiple errors, which will be stored in the Errors array. Check for
// len(Errors) == 0 to see if an error occurred
type Registration struct { type Registration struct {
Success bool `json:"success"` Success bool `json:"success"`
Message string `json:"message,omitempty"` Message string `json:"message,omitempty"`
Errors []RegistrationError `json:"errors,omitempty"` Errors []Error `json:"errors,omitempty"`
}
type RegistrationError struct {
Code string `json:"error_code"`
Message string `json:"message"`
} }
// UserRegister registers a new user on the Pixeldrain server. username and // UserRegister registers a new user on the Pixeldrain server. username and
// password are always required. email is optional, but without it you will // password are always required. email is optional, but without it you will not
// never be able to reset your password in case you forget it. captcha depends // be able to reset your password in case you forget it. captcha depends on
// on whether reCaptcha is enabled on the Pixeldrain server, this can be checked // whether reCaptcha is enabled on the Pixeldrain server, this can be checked
// through the GetRecaptcha function. // through the GetRecaptcha function.
//
// The register API can return multiple errors, which will be stored in the
// Errors array. Check for len(Errors) == 0 to see if an error occurred. If err
// != nil it means a connection error occurred
func (p *PixelAPI) UserRegister(username, email, password, captcha string) (resp *Registration, err error) { func (p *PixelAPI) UserRegister(username, email, password, captcha string) (resp *Registration, err error) {
resp = &Registration{} resp = &Registration{}
var form = url.Values{} var form = url.Values{}
@@ -28,13 +30,38 @@ func (p *PixelAPI) UserRegister(username, email, password, captcha string) (resp
form.Add("email", email) form.Add("email", email)
form.Add("password", password) form.Add("password", password)
form.Add("recaptcha_response", captcha) form.Add("recaptcha_response", captcha)
err = p.postForm(p.apiEndpoint+"/user/register", form, resp) err = p.form("POST", p.apiEndpoint+"/user/register", form, resp, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return resp, nil return resp, nil
} }
// Login is the success response to the `user/login` API
type Login struct {
Success bool `json:"success"`
APIKey string `json:"api_key"`
}
// UserLogin logs a user in with the provided credentials. The response will
// contain the returned API key. If saveKey is true the API key will also be
// saved in the client and following requests with this client will be
// autenticated
func (p *PixelAPI) UserLogin(username, password string, saveKey bool) (resp *Login, err error) {
resp = &Login{}
var form = url.Values{}
form.Add("username", username)
form.Add("password", password)
err = p.form("POST", p.apiEndpoint+"/user/login", form, resp, true)
if err != nil {
return nil, err
}
if saveKey {
p.apiKey = resp.APIKey
}
return resp, nil
}
// UserInfo contains information about the logged in user // UserInfo contains information about the logged in user
type UserInfo struct { type UserInfo struct {
Success bool `json:"success"` Success bool `json:"success"`
@@ -97,3 +124,15 @@ func (p *PixelAPI) UserLists(page, limit int) (resp *UserLists, err error) {
} }
return resp, nil return resp, nil
} }
func (p *PixelAPI) UserPasswordSet(oldPW, newPW string) (resp *SuccessResponse, err error) {
resp = &SuccessResponse{}
var form = url.Values{}
form.Add("old_password", oldPW)
form.Add("new_password", newPW)
err = p.form("PUT", p.apiEndpoint+"/user/password", form, resp, true)
if err != nil {
return nil, err
}
return resp, nil
}

View File

@@ -11,6 +11,14 @@
{{template "menu" .}} {{template "menu" .}}
<h1 class="highlight_middle border_bottom">Welcome home, {{.Username}}!</h1> <h1 class="highlight_middle border_bottom">Welcome home, {{.Username}}!</h1>
<!--<a href="/user/settings">Change user settings</a><br/>-->
<h2>Actions</h2>
<ul>
<li><a href="/user/logout">Log out</a></li>
<li><a href="/user/change_password">Change my password</a></li>
</ul>
<h2>Your most recently uploaded files:</h2> <h2>Your most recently uploaded files:</h2>
<div class="highlight_dark border_top border_bottom"> <div class="highlight_dark border_top border_bottom">
{{$files := .PixelAPI.UserFiles 0 18}} {{$files := .PixelAPI.UserFiles 0 18}}

View File

@@ -1,83 +0,0 @@
{{define "user_password"}}<!DOCTYPE html>
<html>
<head>
{{template "meta_tags" "Updating Password"}}
{{template "user_style"}}
<script type="text/javascript">var apiEndpoint = '{{.APIEndpoint}}';</script>
</head>
<body>
<div id='body' class="body">
{{template "menu" .}}
<h1>Update Password</h1>
<div id="submit_result"></div>
<form id="the_form" class="highlight_dark border_top border_bottom">
<input type="text" autocomplete="username" value="{{.Username}}" style="display: none;"/>
<table class="form">
<tr class="form">
<td>Old Password</td>
<td><input id="old_password" type="password" autocomplete="current-password" class="form_input"/></td>
</tr>
<tr class="form">
<td>New Password</td>
<td><input id="new_password1" type="password" autocomplete="new-password" class="form_input"/></td>
</tr>
<tr class="form">
<td>New password verification</td>
<td><input id="new_password2" type="password" autocomplete="new-password" class="form_input"/></td>
</tr>
<tr class="form">
<td style="text-align: left;"><a href="/user" class="button button_red"/>Back</a>
<td style="text-align: right;"><input type="submit" value="Confirm" class="button_highlight"/></td>
</tr>
</table>
</form>
<br/>
{{template "footer"}}
</div>
<script type="text/javascript">
document.getElementById("the_form").onsubmit = function(){
var oldpasswd = document.getElementById("old_password");
var passwd1 = document.getElementById("new_password1");
var passwd2 = document.getElementById("new_password2");
if (passwd1.value !== passwd2.value) {
alert("Passwords do not match! Good thing we checked.");
return false;
}
var req = new XMLHttpRequest();
req.onreadystatechange = function(){
if (this.readyState === 4) {
var response = JSON.parse(req.responseText);
var resultDiv = document.getElementById("submit_result");
if (response.success) {
resultDiv.className = "border_top border_bottom highlight_green";
resultDiv.innerHTML = 'Success! Your password has been updated';
oldpasswd.value = "";
passwd1.value = "";
passwd2.value = "";
} else {
resultDiv.className = "border_top border_bottom highlight_red";
resultDiv.innerHTML = response.message;
}
console.log(response);
}
}
var data = new FormData();
data.append("old_password", oldpasswd.value);
data.append("new_password", passwd1.value);
req.open("PUT", apiEndpoint+"/user/password", true);
req.send(data);
return false;
}
</script>
</body>
</html>
{{end}}

View File

@@ -10,8 +10,10 @@
<div id='body' class="body"> <div id='body' class="body">
{{template "menu" .}} {{template "menu" .}}
<h1>User configuration</h1> <h1>User configuration</h1>
<p>What would you like to do?</p>
<ul>
<li><a href="/user/change_password">Change my password</a></li>
</ul>
{{template "footer"}} {{template "footer"}}
</div> </div>

21
res/template/admin.html Normal file
View File

@@ -0,0 +1,21 @@
{{define "widgets"}}
<!DOCTYPE html>
<html>
<head>
{{template "meta_tags" "Administrator panel"}}
{{template "user_style" .}}
<script type="text/javascript">var apiEndpoint = '{{.APIEndpoint}}';</script>
</head>
<body>
<img id="header_image" class="header_image" src="/res/img/header_neuropol.png" alt="Header image"/>
<br/>
<div id="body" class="body">
{{template "menu" .}}
<h1>System statistics</h1>
{{template "footer"}}
</div>
</body>
</html>
{{end}}

View File

@@ -1,8 +1,7 @@
{{define "form"}} {{define "form"}}
<h1>{{.Title}}</h1> <h1>{{.Title}}</h1>
{{.PreFormHTML}} {{.PreFormHTML}}
<br/><br/> {{if eq .Submitted true}}
{{if eq .Submit true}}
{{if eq .SubmitSuccess true}} {{if eq .SubmitSuccess true}}
<div id="submit_result" class="highlight_green border_top border_bottom"> <div id="submit_result" class="highlight_green border_top border_bottom">
{{index .SubmitMessages 0}} {{index .SubmitMessages 0}}
@@ -19,7 +18,12 @@
{{end}} {{end}}
{{end}} {{end}}
<form class="highlight_dark border_top border_bottom"> <form class="highlight_dark border_top border_bottom" method="POST">
<input type="text" name="form" value="{{.Name}}" style="display: none;" readonly="readonly"/>
{{if ne .Username ""}}
<!-- The invisible username field is so browsers know which user the form was for -->
<input type="text" autocomplete="username" value="{{.Username}}" style="display: none;" readonly="readonly"/>
{{end}}
<table style="margin-left: auto; margin-right: auto; text-align: left; max-width: 30em;"> <table style="margin-left: auto; margin-right: auto; text-align: left; max-width: 30em;">
{{range $index, $field := .Fields}} {{range $index, $field := .Fields}}
<tr class="form"> <tr class="form">
@@ -40,14 +44,16 @@
<div class="g-recaptcha" data-theme="dark" data-sitekey="{{$field.CaptchaSiteKey}}"></div> <div class="g-recaptcha" data-theme="dark" data-sitekey="{{$field.CaptchaSiteKey}}"></div>
{{end}} {{end}}
</td> </td>
{{if ne $field.Description ""}} {{if or (ne $field.Description "") (eq $field.Separator true)}}
<tr class="form"> <tr class="form">
<td colspan="2">{{$field.Description}}</td> <td colspan="2">
{{$field.Description}}
{{if eq $field.Separator true}}
<hr/>
{{end}}
</td>
</tr> </tr>
{{end}} {{end}}
{{if eq $field.Separator true}}
<tr class="form"><td colspan="2"><hr/></td></tr>
{{end}}
</tr> </tr>
{{end}} {{end}}
<tr class="form"> <tr class="form">
@@ -74,9 +80,7 @@
</tr> </tr>
</table> </table>
</form> </form>
<br/>
{{.PostFormHTML}} {{.PostFormHTML}}
<br/>
{{end}} {{end}}
{{define "form_page"}} {{define "form_page"}}
<!DOCTYPE html> <!DOCTYPE html>

View File

@@ -1,11 +1,16 @@
package forms package forms
import ( import (
"fmt"
"html/template" "html/template"
"net/http"
) )
// Form is a form which can be rendered in HTML and submitted // Form is a form which can be rendered in HTML and submitted
type Form struct { type Form struct {
// Name of the form. When this form is submitted this name will be in the `form` parameter
Name string
Title string // Shown in a large font above the form Title string // Shown in a large font above the form
PreFormHTML template.HTML // Content to be rendered above the form PreFormHTML template.HTML // Content to be rendered above the form
@@ -18,27 +23,39 @@ type Form struct {
PostFormHTML template.HTML // Content to be rendered below the form PostFormHTML template.HTML // Content to be rendered below the form
// Fields to render if the form has been submitted once // Fields to render if the form has been submitted once
Submit bool // If the form has been submitted Submitted bool // If the form has been submitted
SubmitSuccess bool // If the submission was a success SubmitSuccess bool // If the submission was a success
SubmitMessages []string // Messages telling the user the results SubmitMessages []template.HTML // Messages telling the user the results
// Used for letting the browser know which user is logged in
Username string
} }
// Field is a single input field in a form // Field is a single input field in a form
type Field struct { type Field struct {
// Used for reading the data. Entered data is POSTed back to the same URL with this name // Used for reading the data. Entered data is POSTed back to the same URL with this name
Name string Name string
// Is entered in the input field by default
// Is entered in the input field by default. If this is empty when running
// Form.ReadInput() it will be set to the value entered by the user
DefaultValue string DefaultValue string
// The value entered by the user. Filled in when running Form.ReadInput()
EnteredValue string
// Text next to the input field // Text next to the input field
Label string Label string
// Text below the input field // Text below the input field
Description string Description string
// Separates fields with a horizontal rule // Separates fields with a horizontal rule
Separator bool Separator bool
Type FieldType Type FieldType
// Only used when Type = `captcha` // Only used when Type = `captcha`. When using reCaptcha the field name has
// to be `recaptcha_response`
CaptchaSiteKey string CaptchaSiteKey string
} }
@@ -47,10 +64,48 @@ type FieldType string
// Fields which can be in a form // Fields which can be in a form
const ( const (
FieldTypeText FieldType = "text" FieldTypeText FieldType = "text"
FieldTypeUsername FieldType = "username" FieldTypeUsername FieldType = "username"
FieldTypeEmail FieldType = "email" FieldTypeEmail FieldType = "email"
FieldTypeOldPassword FieldType = "current-password" FieldTypeCurrentPassword FieldType = "current-password"
FieldTypeNewPassword FieldType = "new-password" FieldTypeNewPassword FieldType = "new-password"
FieldTypeCaptcha FieldType = "captcha" FieldTypeCaptcha FieldType = "captcha"
) )
// ReadInput reads the form of a request and fills in the values for each field.
// The return value will be true if this form was submitted and false if the
// form was not submitted
func (f *Form) ReadInput(r *http.Request) (success bool) {
if r.FormValue("form") != f.Name {
f.Submitted = false
return false
}
f.Submitted = true
var val string
for i, field := range f.Fields {
val = r.FormValue(field.Name)
field.EnteredValue = val
if field.DefaultValue == "" {
field.DefaultValue = val
}
f.Fields[i] = field // Update the new values in the array
}
return true
}
// FieldVal is a utility function for getting the entered value of a field by
// its name. By using this function you don't have to use nondescriptive array
// indexes to get the values. It panics if the field name is not found in the
// form
func (f *Form) FieldVal(name string) (enteredValue string) {
for _, field := range f.Fields {
if field.Name == name {
return field.EnteredValue
}
}
panic(fmt.Errorf("FieldVal called on unregistered field name '%s'", name))
}

View File

@@ -19,8 +19,7 @@ func (wc *WebController) serveRegister(
// This only runs on the first request // This only runs on the first request
if wc.captchaSiteKey == "" { if wc.captchaSiteKey == "" {
var api = pixelapi.New(wc.conf.APIURLInternal, "") capt, err := tpld.PixelAPI.GetRecaptcha()
capt, err := api.GetRecaptcha()
if err != nil { if err != nil {
log.Error("Error getting recaptcha key: %s", err) log.Error("Error getting recaptcha key: %s", err)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
@@ -57,37 +56,161 @@ func (wc *WebController) serveLogout(
http.Redirect(w, r, "/", http.StatusSeeOther) http.Redirect(w, r, "/", http.StatusSeeOther)
} }
func (wc *WebController) formPassword( func (wc *WebController) registerForm(td *TemplateData, r *http.Request) (f forms.Form) {
w http.ResponseWriter, // This only runs on the first request
r *http.Request, if wc.captchaSiteKey == "" {
p httprouter.Params, capt, err := td.PixelAPI.GetRecaptcha()
) { if err != nil {
td := wc.newTemplateData(w, r) log.Error("Error getting recaptcha key: %s", err)
td.Form = forms.Form{ f.SubmitMessages = []template.HTML{
Title: "Test Form", "An internal server error had occurred. Registration is " +
PreFormHTML: template.HTML("preform"), "unavailable at the moment. Please return later",
}
return f
}
if capt.SiteKey == "" {
wc.captchaSiteKey = "none"
} else {
wc.captchaSiteKey = capt.SiteKey
}
}
// Construct the form
td.Title = "Register a new Pixeldrain account"
f = forms.Form{
Name: "register",
Title: td.Title,
Fields: []forms.Field{ Fields: []forms.Field{
forms.Field{ {
Name: "field_1", Name: "username",
DefaultValue: "def val 1", Label: "Username",
Label: "Field 1", Description: "used for logging into your account",
Description: "Description of field one", Separator: true,
Separator: false, Type: forms.FieldTypeUsername,
Type: forms.FieldTypeUsername, }, {
Name: "e-mail",
Label: "E-mail address",
Description: "not required. your e-mail address will only be " +
"used for password resets and important account " +
"notifications",
Separator: true,
Type: forms.FieldTypeEmail,
}, {
Name: "password1",
Label: "Password",
Type: forms.FieldTypeNewPassword,
}, {
Name: "password2",
Label: "Password verification",
Description: "you need to enter your password twice so we " +
"can verify that no typing errors were made, which would " +
"prevent you from logging into your new account",
Separator: true,
Type: forms.FieldTypeNewPassword,
}, {
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 to flood the " +
"website with fake accounts",
Separator: true,
Type: forms.FieldTypeCaptcha,
CaptchaSiteKey: wc.captchaSiteKey,
}, },
}, },
BackLink: "/", BackLink: "/",
SubmitLabel: "ayy lmao send", SubmitLabel: "Register",
SubmitRed: false, PostFormHTML: template.HTML("<p>Welcome to the club!</p>"),
PostFormHTML: template.HTML("postform"),
Submit: true,
SubmitSuccess: true,
SubmitMessages: []string{"yay success"},
} }
err := wc.templates.Get().ExecuteTemplate(w, "form_page", td) if f.ReadInput(r) {
if err != nil { if f.FieldVal("password1") != f.FieldVal("password2") {
log.Error("Error executing template '%s': %s", "register", err) f.SubmitMessages = []template.HTML{
} "Password verification failed. Please enter the same " +
"password in both password fields"}
return f
}
resp, err := td.PixelAPI.UserRegister(
f.FieldVal("username"),
f.FieldVal("e-mail"),
f.FieldVal("password1"),
f.FieldVal("recaptcha_response"),
)
if 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 if len(resp.Errors) != 0 {
// Registration errors occurred
for _, rerr := range resp.Errors {
f.SubmitMessages = append(f.SubmitMessages, template.HTML(rerr.Message))
}
} else {
// Request was a success
f.SubmitSuccess = true
f.SubmitMessages = []template.HTML{
`Registration completed! You can now <a href="/login">log in ` +
`to your account</a>.<br/>We're glad to have you on ` +
`board, have fun sharing!`}
}
}
return f
}
func (wc *WebController) passwordForm(td *TemplateData, r *http.Request) (f forms.Form) {
td.Title = "Change Password"
f = forms.Form{
Name: "password_change",
Title: td.Title,
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 verification",
Type: forms.FieldTypeCurrentPassword,
},
},
BackLink: "/user",
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 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
} }

View File

@@ -7,6 +7,7 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
"fornaxian.com/pixeldrain-web/init/conf" "fornaxian.com/pixeldrain-web/init/conf"
"fornaxian.com/pixeldrain-web/webcontroller/forms"
"github.com/Fornaxian/log" "github.com/Fornaxian/log"
"github.com/julienschmidt/httprouter" "github.com/julienschmidt/httprouter"
) )
@@ -47,19 +48,21 @@ func New(r *httprouter.Router, prefix string, conf *conf.PixelWebConfig) *WebCon
}) })
// General navigation // General navigation
r.GET(p+"/" /* */, wc.serveTemplate("home", false)) r.GET(p+"/" /* */, wc.serveTemplate("home", false))
r.GET(p+"/favicon.ico" /* */, wc.serveFile("/favicon.ico")) r.GET(p+"/favicon.ico" /* */, wc.serveFile("/favicon.ico"))
r.GET(p+"/api" /* */, wc.serveTemplate("apidoc", false)) r.GET(p+"/api" /* */, wc.serveTemplate("apidoc", false))
r.GET(p+"/history" /* */, wc.serveTemplate("history_cookies", false)) r.GET(p+"/history" /* */, wc.serveTemplate("history_cookies", false))
r.GET(p+"/u/:id" /* */, wc.serveFileViewer) r.GET(p+"/u/:id" /* */, wc.serveFileViewer)
r.GET(p+"/u/:id/preview" /* */, wc.serveFilePreview) r.GET(p+"/u/:id/preview" /**/, wc.serveFilePreview)
r.GET(p+"/l/:id" /* */, wc.serveListViewer) r.GET(p+"/l/:id" /* */, wc.serveListViewer)
r.GET(p+"/t" /* */, wc.serveTemplate("paste", false)) r.GET(p+"/t" /* */, wc.serveTemplate("paste", false))
r.GET(p+"/donation" /* */, wc.serveTemplate("donation", false)) r.GET(p+"/donation" /* */, wc.serveTemplate("donation", false))
r.GET(p+"/widgets" /* */, wc.serveTemplate("widgets", false)) r.GET(p+"/widgets" /* */, wc.serveTemplate("widgets", false))
// User account pages // User account pages
r.GET(p+"/register" /* */, wc.serveRegister) r.GET(p+"/register_old" /* */, wc.serveRegister)
r.GET(p+"/register" /* */, wc.serveForm(wc.registerForm, false))
r.POST(p+"/register" /* */, wc.serveForm(wc.registerForm, false))
r.GET(p+"/login" /* */, wc.serveTemplate("login", false)) r.GET(p+"/login" /* */, wc.serveTemplate("login", false))
r.GET(p+"/logout" /* */, wc.serveTemplate("logout", true)) r.GET(p+"/logout" /* */, wc.serveTemplate("logout", true))
r.POST(p+"/logout" /* */, wc.serveLogout) r.POST(p+"/logout" /* */, wc.serveLogout)
@@ -67,9 +70,11 @@ func New(r *httprouter.Router, prefix string, conf *conf.PixelWebConfig) *WebCon
r.GET(p+"/user/files" /* */, wc.serveTemplate("user_files", true)) r.GET(p+"/user/files" /* */, wc.serveTemplate("user_files", true))
r.GET(p+"/user/lists" /* */, wc.serveTemplate("user_lists", true)) r.GET(p+"/user/lists" /* */, wc.serveTemplate("user_lists", true))
r.GET(p+"/user/filemanager" /**/, wc.serveTemplate("file_manager", true)) r.GET(p+"/user/filemanager" /**/, wc.serveTemplate("file_manager", true))
r.GET(p+"/user/password" /* */, wc.serveTemplate("user_password", true))
r.GET(p+"/testform", wc.formPassword) // 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.NotFound = http.HandlerFunc(wc.serveNotFound) r.NotFound = http.HandlerFunc(wc.serveNotFound)
@@ -107,6 +112,56 @@ func (wc *WebController) serveFile(path string) httprouter.Handle {
} }
} }
func (wc *WebController) serveForm(
handler func(*TemplateData, *http.Request) forms.Form,
requireAuth bool,
) httprouter.Handle {
return func(
w http.ResponseWriter,
r *http.Request,
p httprouter.Params,
) {
var td = wc.newTemplateData(w, r)
if requireAuth && !td.Authenticated {
http.Redirect(w, r, "/login", http.StatusSeeOther)
return
}
// The handler retuns the form which will be rendered
td.Form = handler(td, r)
td.Form.Username = td.Username
// Remove the recaptcha field if captcha is disabled
if wc.captchaSiteKey == "none" {
for i, field := range td.Form.Fields {
if field.Type == forms.FieldTypeCaptcha {
td.Form.Fields = append(
td.Form.Fields[:i],
td.Form.Fields[i+1:]...,
)
}
}
}
// Clear the entered values if the request was successful
if td.Form.SubmitSuccess {
w.WriteHeader(http.StatusOK)
for i, field := range td.Form.Fields {
field.EnteredValue = ""
td.Form.Fields[i] = field
}
} else {
w.WriteHeader(http.StatusBadRequest)
}
err := wc.templates.Get().ExecuteTemplate(w, "form_page", td)
if err != nil {
log.Error("Error executing form page: %s", err)
}
}
}
func (wc *WebController) serveNotFound(w http.ResponseWriter, r *http.Request) { func (wc *WebController) serveNotFound(w http.ResponseWriter, r *http.Request) {
log.Debug("Not Found: %s", r.URL) log.Debug("Not Found: %s", r.URL)
w.WriteHeader(http.StatusNotFound) w.WriteHeader(http.StatusNotFound)