Files
fnx_web/webcontroller/web_controller.go

230 lines
7.0 KiB
Go
Raw Normal View History

package webcontroller
import (
2018-06-23 21:17:53 +02:00
"errors"
"net/http"
2019-09-18 22:30:29 +02:00
"strings"
2018-06-23 21:17:53 +02:00
"github.com/google/uuid"
"fornaxian.com/pixeldrain-web/init/conf"
2019-03-28 10:47:27 +01:00
"fornaxian.com/pixeldrain-web/pixelapi"
"fornaxian.com/pixeldrain-web/webcontroller/forms"
"github.com/Fornaxian/log"
"github.com/julienschmidt/httprouter"
)
2018-07-09 23:19:16 +02:00
// WebController controls how requests are handled and makes sure they have
// proper context when running
type WebController struct {
conf *conf.PixelWebConfig
2018-09-19 22:26:52 +02:00
templates *TemplateManager
staticResourceDir string
2018-09-19 22:26:52 +02:00
// page-specific variables
captchaSiteKey string
}
2018-07-09 23:19:16 +02:00
// New initializes a new WebController by registering all the request handlers
// and parsing all templates in the resource directory
func New(r *httprouter.Router, prefix string, conf *conf.PixelWebConfig) *WebController {
var wc = &WebController{
conf: conf,
staticResourceDir: conf.StaticResourceDir,
}
2018-09-19 22:26:52 +02:00
wc.templates = NewTemplateManager(
conf.TemplateDir,
conf.APIURLExternal,
conf.DebugMode,
)
wc.templates.ParseTemplates(false)
2018-10-04 23:36:34 +02:00
var p = prefix
// Serve static files
2019-02-18 14:17:31 +01:00
var fs = http.FileServer(http.Dir(wc.staticResourceDir))
r.GET(p+"/res/*filepath", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
w.Header().Set("Cache-Control", "public, max-age=86400") // Cache for one day
r.URL.Path = p.ByName("filepath")
fs.ServeHTTP(w, r)
})
2019-05-30 09:29:50 +02:00
// Static assets
r.GET(p+"/favicon.ico" /* */, wc.serveFile("/favicon.ico"))
2019-05-19 22:24:54 +02:00
r.GET(p+"/robots.txt" /* */, wc.serveFile("/robots.txt"))
2019-05-30 09:29:50 +02:00
if conf.MaintenanceMode {
r.NotFound = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusServiceUnavailable)
wc.templates.Get().ExecuteTemplate(w, "maintenance", wc.newTemplateData(w, r))
})
return wc
}
// General navigation
r.GET(p+"/" /* */, wc.serveTemplate("home", false))
r.GET(p+"/api" /* */, wc.serveTemplate("apidoc", false))
r.GET(p+"/history" /* */, wc.serveTemplate("history_cookies", false))
r.GET(p+"/u/:id" /* */, wc.serveFileViewer)
r.GET(p+"/u/:id/preview" /**/, wc.serveFilePreview)
r.GET(p+"/l/:id" /* */, wc.serveListViewer)
r.GET(p+"/t" /* */, wc.serveTemplate("paste", false))
r.GET(p+"/donation" /* */, wc.serveTemplate("donation", false))
r.GET(p+"/widgets" /* */, wc.serveTemplate("widgets", false))
2019-09-17 23:38:40 +02:00
r.GET(p+"/about" /* */, wc.serveTemplate("about", false))
2019-09-18 22:23:12 +02:00
r.GET(p+"/appearance" /* */, wc.serveTemplate("appearance", false))
2018-07-09 23:19:16 +02:00
// User account pages
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))
2019-12-17 19:28:30 +01:00
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))
2018-10-04 23:36:34 +02:00
r.GET(p+"/logout" /* */, wc.serveTemplate("logout", true))
r.POST(p+"/logout" /* */, wc.serveLogout)
r.GET(p+"/user" /* */, wc.serveTemplate("user_home", true))
r.GET(p+"/user/files" /* */, wc.serveTemplate("user_files", true))
2019-02-07 23:09:54 +01:00
r.GET(p+"/user/lists" /* */, wc.serveTemplate("user_lists", true))
2018-10-04 23:36:34 +02:00
r.GET(p+"/user/filemanager" /**/, wc.serveTemplate("file_manager", true))
// User account settings
2019-12-17 19:28:30 +01:00
r.GET(p+"/user/settings" /* */, wc.serveUserSettings)
r.POST(p+"/user/settings" /* */, wc.serveUserSettings)
r.GET(p+"/user/confirm_email" /**/, wc.serveEmailConfirm)
// 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))
2019-07-07 10:11:18 +02:00
r.NotFound = http.HandlerFunc(wc.serveNotFound)
return wc
}
2018-07-09 21:41:17 +02:00
func (wc *WebController) serveTemplate(
tpl string,
requireAuth bool,
) httprouter.Handle {
return func(
w http.ResponseWriter,
r *http.Request,
p httprouter.Params,
) {
2018-07-09 21:41:17 +02:00
var tpld = wc.newTemplateData(w, r)
if requireAuth && !tpld.Authenticated {
http.Redirect(w, r, "/login", http.StatusSeeOther)
return
}
err := wc.templates.Get().ExecuteTemplate(w, tpl, tpld)
2019-09-18 22:30:29 +02:00
if err != nil && !strings.Contains(err.Error(), "broken pipe") {
log.Error("Error executing template '%s': %s", tpl, err)
}
}
}
func (wc *WebController) serveFile(path string) httprouter.Handle {
return func(
w http.ResponseWriter,
r *http.Request,
p httprouter.Params,
) {
2019-05-19 22:24:54 +02:00
http.ServeFile(w, r, wc.staticResourceDir+path)
}
}
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
// Execute the extra actions if any
if td.Form.Extra.SetCookie != nil {
http.SetCookie(w, td.Form.Extra.SetCookie)
}
if td.Form.Extra.RedirectTo != "" {
http.Redirect(w, r, td.Form.Extra.RedirectTo, http.StatusSeeOther)
log.Debug("redirect: %s", td.Form.Extra.RedirectTo)
return // Don't need to render a form if the user is redirected
}
// Remove the recaptcha field if captcha is disabled
if wc.captchaKey() == "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
}
2019-06-04 22:33:56 +02:00
} else if td.Form.Submitted {
w.WriteHeader(http.StatusBadRequest)
}
err := wc.templates.Get().ExecuteTemplate(w, "form_page", td)
2019-09-18 22:30:29 +02:00
if err != nil && !strings.Contains(err.Error(), "broken pipe") {
log.Error("Error executing form page: %s", err)
}
}
}
func (wc *WebController) serveNotFound(w http.ResponseWriter, r *http.Request) {
log.Debug("Not Found: %s", r.URL)
2018-10-04 23:36:34 +02:00
w.WriteHeader(http.StatusNotFound)
2018-07-09 21:41:17 +02:00
wc.templates.Get().ExecuteTemplate(w, "404", wc.newTemplateData(w, r))
2018-06-23 21:17:53 +02:00
}
func (wc *WebController) getAPIKey(r *http.Request) (key string, err error) {
if cookie, err := r.Cookie("pd_auth_key"); err == nil {
if _, err := uuid.Parse(cookie.Value); err == nil {
return cookie.Value, nil
}
}
return "", errors.New("not a valid pixeldrain authentication cookie")
}
2019-03-28 10:47:27 +01:00
func (wc *WebController) captchaKey() string {
// This only runs on the first request
if wc.captchaSiteKey == "" {
var api = pixelapi.New(wc.conf.APIURLInternal, "")
capt, err := api.GetRecaptcha()
if err != nil {
log.Error("Error getting recaptcha key: %s", err)
return ""
}
if capt.SiteKey == "" {
wc.captchaSiteKey = "none"
} else {
wc.captchaSiteKey = capt.SiteKey
}
}
return wc.captchaSiteKey
}