2018-06-17 16:15:58 +02:00
|
|
|
package webcontroller
|
|
|
|
|
|
|
|
import (
|
2020-07-29 16:29:25 +02:00
|
|
|
"bytes"
|
2018-06-23 21:17:53 +02:00
|
|
|
"errors"
|
2020-01-31 19:16:20 +01:00
|
|
|
"fmt"
|
2020-07-29 16:29:25 +02:00
|
|
|
"html/template"
|
2018-06-17 16:15:58 +02:00
|
|
|
"net/http"
|
2021-03-04 17:10:59 +01:00
|
|
|
"net/http/httputil"
|
|
|
|
"net/url"
|
2020-01-31 19:16:20 +01:00
|
|
|
"os"
|
2019-09-18 22:30:29 +02:00
|
|
|
"strings"
|
2020-02-04 19:37:46 +01:00
|
|
|
"time"
|
2018-06-17 16:15:58 +02:00
|
|
|
|
2021-11-16 15:20:15 +01:00
|
|
|
"fornaxian.tech/log"
|
2021-03-10 20:13:32 +01:00
|
|
|
"fornaxian.tech/pixeldrain_api_client/pixelapi"
|
2018-06-17 16:15:58 +02:00
|
|
|
"github.com/julienschmidt/httprouter"
|
2020-07-29 16:29:25 +02:00
|
|
|
blackfriday "github.com/russross/blackfriday/v2"
|
2018-06-17 16:15:58 +02:00
|
|
|
)
|
|
|
|
|
2018-07-09 23:19:16 +02:00
|
|
|
// WebController controls how requests are handled and makes sure they have
|
|
|
|
// proper context when running
|
2018-06-17 16:15:58 +02:00
|
|
|
type WebController struct {
|
2019-12-23 23:56:57 +01:00
|
|
|
templates *TemplateManager
|
2018-09-19 22:26:52 +02:00
|
|
|
|
2019-12-30 13:00:00 +01:00
|
|
|
resourceDir string
|
|
|
|
|
2020-01-31 19:16:20 +01:00
|
|
|
hostname string
|
|
|
|
|
2021-03-04 17:10:59 +01:00
|
|
|
apiURLInternal string
|
|
|
|
apiURLExternal string
|
|
|
|
websiteAddress string
|
2019-12-30 13:00:00 +01:00
|
|
|
sessionCookieDomain string
|
2021-03-04 17:10:59 +01:00
|
|
|
proxyAPIRequests bool
|
2019-12-30 13:00:00 +01:00
|
|
|
|
2018-09-19 22:26:52 +02:00
|
|
|
// page-specific variables
|
|
|
|
captchaSiteKey string
|
2020-02-04 19:37:46 +01:00
|
|
|
|
2021-11-29 16:13:19 +01:00
|
|
|
// The cache ID is used to invalidate caches when the webserver is restarted
|
|
|
|
cacheID int64
|
|
|
|
|
2020-02-04 19:37:46 +01:00
|
|
|
httpClient *http.Client
|
2020-02-21 14:23:29 +01:00
|
|
|
|
2021-01-05 00:00:46 +01:00
|
|
|
// API client to use for all requests. If the user is authenticated you
|
|
|
|
// should call Login() on this object. Calling Login will create a copy and
|
|
|
|
// not alter the original PixelAPI, but it will use the same HTTP Transport
|
2021-03-10 20:13:32 +01:00
|
|
|
api pixelapi.PixelAPI
|
2018-06-17 16:15:58 +02:00
|
|
|
}
|
|
|
|
|
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
|
2019-12-30 13:00:00 +01:00
|
|
|
func New(
|
|
|
|
r *httprouter.Router,
|
|
|
|
prefix string,
|
|
|
|
resourceDir string,
|
|
|
|
apiURLInternal string,
|
|
|
|
apiURLExternal string,
|
2021-01-12 23:20:32 +01:00
|
|
|
websiteAddress string,
|
2019-12-30 13:00:00 +01:00
|
|
|
sessionCookieDomain string,
|
|
|
|
maintenanceMode bool,
|
|
|
|
debugMode bool,
|
2021-03-04 17:10:59 +01:00
|
|
|
proxyAPIRequests bool,
|
2019-12-30 13:00:00 +01:00
|
|
|
) (wc *WebController) {
|
2020-01-31 19:16:20 +01:00
|
|
|
var err error
|
2019-12-30 13:00:00 +01:00
|
|
|
wc = &WebController{
|
|
|
|
resourceDir: resourceDir,
|
|
|
|
apiURLInternal: apiURLInternal,
|
|
|
|
apiURLExternal: apiURLExternal,
|
2021-01-12 23:20:32 +01:00
|
|
|
websiteAddress: websiteAddress,
|
2019-12-30 13:00:00 +01:00
|
|
|
sessionCookieDomain: sessionCookieDomain,
|
2021-03-04 17:10:59 +01:00
|
|
|
proxyAPIRequests: proxyAPIRequests,
|
2021-11-29 16:13:19 +01:00
|
|
|
cacheID: time.Now().Unix() / 3600,
|
2020-02-04 19:37:46 +01:00
|
|
|
httpClient: &http.Client{Timeout: time.Minute * 10},
|
2021-03-10 20:13:32 +01:00
|
|
|
api: pixelapi.New(apiURLInternal),
|
2018-06-17 16:15:58 +02:00
|
|
|
}
|
2019-12-30 13:00:00 +01:00
|
|
|
wc.templates = NewTemplateManager(resourceDir, apiURLExternal, debugMode)
|
2018-06-20 23:47:47 +02:00
|
|
|
wc.templates.ParseTemplates(false)
|
2018-06-17 16:15:58 +02:00
|
|
|
|
2020-01-31 19:16:20 +01:00
|
|
|
if wc.hostname, err = os.Hostname(); err != nil {
|
2021-06-29 12:08:31 +02:00
|
|
|
panic(fmt.Errorf("could not get hostname: %s", err))
|
2020-01-31 19:16:20 +01:00
|
|
|
}
|
|
|
|
|
2018-06-17 16:15:58 +02:00
|
|
|
// Serve static files
|
2019-12-30 13:00:00 +01:00
|
|
|
var fs = http.FileServer(http.Dir(resourceDir + "/static"))
|
2021-11-29 16:13:19 +01:00
|
|
|
var resourceHandler = func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
2021-11-24 11:51:39 +01:00
|
|
|
// Cache resources for a year
|
2021-11-29 16:13:19 +01:00
|
|
|
w.Header().Set("Cache-Control", "public, max-age=31536000")
|
2019-02-18 14:17:31 +01:00
|
|
|
r.URL.Path = p.ByName("filepath")
|
|
|
|
fs.ServeHTTP(w, r)
|
2021-11-29 16:13:19 +01:00
|
|
|
}
|
|
|
|
r.HEAD(prefix+"/res/*filepath", resourceHandler)
|
|
|
|
r.OPTIONS(prefix+"/res/*filepath", resourceHandler)
|
|
|
|
r.GET(prefix+"/res/*filepath", resourceHandler)
|
2018-06-17 16:15:58 +02:00
|
|
|
|
2019-05-30 09:29:50 +02:00
|
|
|
// Static assets
|
2020-07-20 16:57:43 +02:00
|
|
|
r.GET(prefix+"/favicon.ico" /* */, wc.serveFile("/favicon.ico"))
|
|
|
|
r.GET(prefix+"/robots.txt" /* */, wc.serveFile("/robots.txt"))
|
2021-08-17 18:03:05 +02:00
|
|
|
r.GET(prefix+"/ads.txt" /* */, wc.serveFile("/ads.txt"))
|
2019-05-30 09:29:50 +02:00
|
|
|
|
2019-12-30 13:00:00 +01:00
|
|
|
if maintenanceMode {
|
2019-05-30 09:29:50 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2021-03-04 17:10:59 +01:00
|
|
|
if proxyAPIRequests {
|
2021-11-02 10:17:10 +01:00
|
|
|
remoteURL, err := url.Parse(strings.TrimSuffix(apiURLInternal, "/api"))
|
2021-03-04 17:10:59 +01:00
|
|
|
if err != nil {
|
2021-11-02 10:17:10 +01:00
|
|
|
panic(fmt.Errorf("failed to parse reverse proxy URL '%s': %w", apiURLInternal, err))
|
2021-03-04 17:10:59 +01:00
|
|
|
}
|
|
|
|
|
2021-11-02 10:17:10 +01:00
|
|
|
log.Info("Starting API proxy to %s", remoteURL)
|
|
|
|
var prox = httputil.NewSingleHostReverseProxy(remoteURL)
|
2021-03-04 17:10:59 +01:00
|
|
|
prox.Transport = wc.httpClient.Transport
|
2021-11-02 10:17:10 +01:00
|
|
|
|
|
|
|
var proxyHandler = func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
|
|
|
log.Info("Proxying request to %s", r.URL)
|
|
|
|
r.Host = remoteURL.Host
|
|
|
|
r.Header.Set("Origin", remoteURL.String())
|
|
|
|
prox.ServeHTTP(w, r)
|
|
|
|
}
|
|
|
|
|
|
|
|
r.Handle("OPTIONS", "/api/*p", proxyHandler)
|
|
|
|
r.Handle("POST", "/api/*p", proxyHandler)
|
|
|
|
r.Handle("GET", "/api/*p", proxyHandler)
|
|
|
|
r.Handle("PUT", "/api/*p", proxyHandler)
|
|
|
|
r.Handle("PATCH", "/api/*p", proxyHandler)
|
|
|
|
r.Handle("DELETE", "/api/*p", proxyHandler)
|
2021-03-04 17:10:59 +01:00
|
|
|
}
|
|
|
|
|
2018-06-17 16:15:58 +02:00
|
|
|
r.NotFound = http.HandlerFunc(wc.serveNotFound)
|
|
|
|
|
2020-07-20 16:57:43 +02:00
|
|
|
// Request method shorthands. These help keep the array of handlers aligned
|
|
|
|
const PST, GET, PUT, DEL = "POST", "GET", "PUT", "DELETE"
|
|
|
|
|
|
|
|
// Loop over the handlers and register all of them in the router
|
|
|
|
for _, h := range []struct {
|
|
|
|
method string // HTTP request method this API handler uses
|
|
|
|
path string // The URL path this API will be registered on
|
|
|
|
handler httprouter.Handle // The function to run when this API is called
|
|
|
|
}{
|
|
|
|
// General navigation
|
2021-03-09 17:00:43 +01:00
|
|
|
{GET, "" /* */, wc.serveTemplate("home", handlerOpts{})},
|
2021-09-21 22:47:13 +02:00
|
|
|
{GET, "api" /* */, wc.serveMarkdown("api.md", handlerOpts{})},
|
2021-03-09 17:00:43 +01:00
|
|
|
{GET, "history" /* */, wc.serveTemplate("history_cookies", handlerOpts{})},
|
2020-12-15 16:25:20 +01:00
|
|
|
{GET, "u/:id/preview" /* */, wc.serveFilePreview},
|
2021-11-02 10:17:10 +01:00
|
|
|
{GET, "u/:id" /* */, wc.serveFileViewer},
|
|
|
|
{GET, "l/:id" /* */, wc.serveListViewer},
|
2021-02-23 16:50:13 +01:00
|
|
|
{GET, "d/*path" /* */, wc.serveDirectory},
|
2022-02-21 23:25:44 +01:00
|
|
|
{GET, "t" /* */, wc.serveTemplate("text_upload", handlerOpts{})},
|
2021-03-09 17:00:43 +01:00
|
|
|
{GET, "donation" /* */, wc.serveMarkdown("donation.md", handlerOpts{})},
|
2021-08-17 18:03:05 +02:00
|
|
|
{GET, "advertising" /* */, wc.serveMarkdown("advertising.md", handlerOpts{})},
|
2021-03-09 17:00:43 +01:00
|
|
|
{GET, "widgets" /* */, wc.serveTemplate("widgets", handlerOpts{})},
|
|
|
|
{GET, "about" /* */, wc.serveMarkdown("about.md", handlerOpts{})},
|
|
|
|
{GET, "appearance" /* */, wc.serveTemplate("appearance", handlerOpts{})},
|
|
|
|
{GET, "hosting" /* */, wc.serveMarkdown("hosting.md", handlerOpts{})},
|
|
|
|
{GET, "brave" /* */, wc.serveMarkdown("brave.md", handlerOpts{})},
|
|
|
|
{GET, "acknowledgements" /**/, wc.serveMarkdown("acknowledgements.md", handlerOpts{})},
|
|
|
|
{GET, "business" /* */, wc.serveMarkdown("business.md", handlerOpts{})},
|
2022-02-28 16:35:51 +01:00
|
|
|
{GET, "limits" /* */, wc.serveMarkdown("limits.md", handlerOpts{})},
|
2021-03-09 17:00:43 +01:00
|
|
|
{GET, "apps" /* */, wc.serveTemplate("apps", handlerOpts{})},
|
2022-02-21 21:55:09 +01:00
|
|
|
{GET, "directory_upload" /**/, wc.serveTemplate("directory_upload", handlerOpts{})},
|
2020-07-20 16:57:43 +02:00
|
|
|
|
|
|
|
// User account pages
|
2021-03-09 17:00:43 +01:00
|
|
|
{GET, "register" /* */, wc.serveForm(wc.registerForm, handlerOpts{NoEmbed: true})},
|
|
|
|
{PST, "register" /* */, wc.serveForm(wc.registerForm, handlerOpts{NoEmbed: true})},
|
|
|
|
{GET, "login" /* */, wc.serveForm(wc.loginForm, handlerOpts{NoEmbed: true})},
|
|
|
|
{PST, "login" /* */, wc.serveForm(wc.loginForm, handlerOpts{NoEmbed: true})},
|
|
|
|
{GET, "password_reset" /* */, wc.serveForm(wc.passwordResetForm, handlerOpts{NoEmbed: true})},
|
|
|
|
{PST, "password_reset" /* */, wc.serveForm(wc.passwordResetForm, handlerOpts{NoEmbed: true})},
|
|
|
|
{GET, "logout" /* */, wc.serveTemplate("logout", handlerOpts{Auth: true, NoEmbed: true})},
|
2021-03-08 15:30:05 +01:00
|
|
|
{PST, "logout" /* */, wc.serveLogout},
|
2021-03-09 17:00:43 +01:00
|
|
|
{GET, "user/buckets" /* */, wc.serveTemplate("user_buckets", handlerOpts{Auth: true})},
|
|
|
|
{GET, "user/filemanager" /* */, wc.serveTemplate("file_manager", handlerOpts{Auth: true})},
|
2021-03-08 15:30:05 +01:00
|
|
|
{GET, "user/export/files" /**/, wc.serveUserExportFiles},
|
|
|
|
{GET, "user/export/lists" /**/, wc.serveUserExportLists},
|
2020-07-20 16:57:43 +02:00
|
|
|
|
|
|
|
// User account settings
|
2021-09-23 22:21:27 +02:00
|
|
|
{GET, "user" /* */, wc.serveTemplate("user_home", handlerOpts{Auth: true})},
|
2021-11-16 13:58:00 +01:00
|
|
|
{GET, "user/home" /* */, wc.serveTemplate("user_home", handlerOpts{Auth: true})},
|
2021-09-23 22:21:27 +02:00
|
|
|
{GET, "user/settings" /* */, wc.serveTemplate("user_home", handlerOpts{Auth: true})},
|
2022-04-26 15:23:57 +02:00
|
|
|
{GET, "user/sharing" /* */, wc.serveTemplate("user_home", handlerOpts{Auth: true})},
|
2021-09-23 22:21:27 +02:00
|
|
|
{GET, "user/api_keys" /* */, wc.serveTemplate("user_home", handlerOpts{Auth: true})},
|
2022-02-01 18:43:52 +01:00
|
|
|
{GET, "user/activity" /* */, wc.serveTemplate("user_home", handlerOpts{Auth: true})},
|
2021-12-20 20:31:39 +01:00
|
|
|
{GET, "user/connect_app" /* */, wc.serveTemplate("user_home", handlerOpts{Auth: true})},
|
2021-11-16 13:58:00 +01:00
|
|
|
{GET, "user/transactions" /* */, wc.serveTemplate("user_home", handlerOpts{Auth: true})},
|
2021-11-16 21:11:59 +01:00
|
|
|
{GET, "user/subscription" /* */, wc.serveTemplate("user_home", handlerOpts{Auth: true})},
|
2020-07-20 16:57:43 +02:00
|
|
|
{GET, "user/confirm_email" /* */, wc.serveEmailConfirm},
|
2021-03-09 17:00:43 +01:00
|
|
|
{GET, "user/password_reset_confirm" /**/, wc.serveForm(wc.passwordResetConfirmForm, handlerOpts{NoEmbed: true})},
|
|
|
|
{PST, "user/password_reset_confirm" /**/, wc.serveForm(wc.passwordResetConfirmForm, handlerOpts{NoEmbed: true})},
|
2020-07-20 16:57:43 +02:00
|
|
|
|
2021-11-28 21:42:01 +01:00
|
|
|
{GET, "patreon_activate", wc.serveForm(wc.patreonLinkForm, handlerOpts{Auth: true})},
|
|
|
|
{PST, "patreon_activate", wc.serveForm(wc.patreonLinkForm, handlerOpts{Auth: true})},
|
2021-01-26 19:01:00 +01:00
|
|
|
|
2021-11-28 21:42:01 +01:00
|
|
|
{GET, "knoxfs_activate", wc.serveForm(wc.knoxfsLinkForm, handlerOpts{Auth: true})},
|
|
|
|
{PST, "knoxfs_activate", wc.serveForm(wc.knoxfsLinkForm, handlerOpts{Auth: true})},
|
|
|
|
|
2021-11-29 23:32:25 +01:00
|
|
|
{GET, "coupon_redeem", wc.serveForm(wc.couponForm, handlerOpts{})},
|
|
|
|
{PST, "coupon_redeem", wc.serveForm(wc.couponForm, handlerOpts{})},
|
2020-10-19 21:28:11 +02:00
|
|
|
|
2020-07-20 16:57:43 +02:00
|
|
|
// Admin settings
|
2021-05-25 22:15:29 +02:00
|
|
|
{GET, "admin" /* */, wc.serveTemplate("admin", handlerOpts{Auth: true})},
|
2021-06-14 14:56:04 +02:00
|
|
|
{GET, "admin/status" /* */, wc.serveTemplate("admin", handlerOpts{Auth: true})},
|
2021-09-23 22:16:53 +02:00
|
|
|
{GET, "admin/block_files" /* */, wc.serveTemplate("admin", handlerOpts{Auth: true})},
|
2021-05-25 22:15:29 +02:00
|
|
|
{GET, "admin/abuse_reporters" /**/, wc.serveTemplate("admin", handlerOpts{Auth: true})},
|
|
|
|
{GET, "admin/abuse_reports" /* */, wc.serveTemplate("admin", handlerOpts{Auth: true})},
|
|
|
|
{GET, "admin/ip_bans" /* */, wc.serveTemplate("admin", handlerOpts{Auth: true})},
|
2021-11-16 13:58:00 +01:00
|
|
|
{GET, "admin/subscriptions" /* */, wc.serveTemplate("admin", handlerOpts{Auth: true})},
|
2021-03-09 17:00:43 +01:00
|
|
|
{GET, "admin/globals" /* */, wc.serveForm(wc.adminGlobalsForm, handlerOpts{Auth: true})},
|
|
|
|
{PST, "admin/globals" /* */, wc.serveForm(wc.adminGlobalsForm, handlerOpts{Auth: true})},
|
2020-07-20 16:57:43 +02:00
|
|
|
|
|
|
|
// Advertising related
|
2020-10-27 08:25:03 +01:00
|
|
|
{GET, "click/:id" /* */, wc.serveAdClick},
|
|
|
|
{GET, "campaign/:id" /* */, wc.serveCampaignPartner},
|
2021-01-12 14:07:55 +01:00
|
|
|
|
|
|
|
// Misc
|
|
|
|
{GET, "misc/sharex/pixeldrain.com.sxcu", wc.serveShareXConfig},
|
2020-07-20 16:57:43 +02:00
|
|
|
} {
|
|
|
|
r.Handle(h.method, prefix+"/"+h.path, h.handler)
|
|
|
|
}
|
|
|
|
|
2018-06-17 16:15:58 +02:00
|
|
|
return wc
|
|
|
|
}
|
|
|
|
|
2021-03-09 17:00:43 +01:00
|
|
|
type handlerOpts struct {
|
|
|
|
Auth bool
|
|
|
|
NoEmbed bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (wc *WebController) serveTemplate(tpl string, opts handlerOpts) httprouter.Handle {
|
2021-03-08 15:30:05 +01:00
|
|
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
2021-03-09 17:00:43 +01:00
|
|
|
if opts.NoEmbed {
|
|
|
|
w.Header().Set("X-Frame-Options", "DENY")
|
|
|
|
}
|
|
|
|
|
|
|
|
var td = wc.newTemplateData(w, r)
|
|
|
|
if opts.Auth && !td.Authenticated {
|
2018-07-09 21:41:17 +02:00
|
|
|
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
|
|
|
return
|
|
|
|
}
|
2021-03-09 17:00:43 +01:00
|
|
|
err := wc.templates.Get().ExecuteTemplate(w, tpl, td)
|
2019-09-18 22:30:29 +02:00
|
|
|
if err != nil && !strings.Contains(err.Error(), "broken pipe") {
|
2018-06-17 16:15:58 +02:00
|
|
|
log.Error("Error executing template '%s': %s", tpl, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-09 17:00:43 +01:00
|
|
|
func (wc *WebController) serveMarkdown(tpl string, opts handlerOpts) httprouter.Handle {
|
2020-07-29 16:29:25 +02:00
|
|
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
|
|
|
var err error
|
2021-03-09 17:00:43 +01:00
|
|
|
if opts.NoEmbed {
|
|
|
|
w.Header().Set("X-Frame-Options", "DENY")
|
|
|
|
}
|
|
|
|
|
2020-07-29 16:29:25 +02:00
|
|
|
var tpld = wc.newTemplateData(w, r)
|
2021-03-09 17:00:43 +01:00
|
|
|
if opts.Auth && !tpld.Authenticated {
|
2020-07-29 16:29:25 +02:00
|
|
|
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Execute the raw markdown template and save the result in a buffer
|
|
|
|
var tplBuf bytes.Buffer
|
|
|
|
if err = wc.templates.Get().ExecuteTemplate(&tplBuf, tpl, tpld); err != nil {
|
|
|
|
log.Error("Error executing template '%s': %s", tpl, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the markdown document and save the resulting HTML in a buffer
|
|
|
|
renderer := blackfriday.NewHTMLRenderer(blackfriday.HTMLRendererParameters{
|
|
|
|
Flags: blackfriday.CommonHTMLFlags,
|
|
|
|
})
|
|
|
|
|
|
|
|
// We parse the markdown document, walk through the nodes. Extract the
|
|
|
|
// title of the document, and the rest of the nodes are rendered like
|
|
|
|
// normal
|
|
|
|
var mdBuf bytes.Buffer
|
|
|
|
var inHeader = false
|
|
|
|
blackfriday.New(
|
|
|
|
blackfriday.WithRenderer(renderer),
|
2021-01-05 00:00:46 +01:00
|
|
|
blackfriday.WithExtensions(blackfriday.CommonExtensions|blackfriday.AutoHeadingIDs),
|
2020-07-29 16:29:25 +02:00
|
|
|
).Parse(
|
|
|
|
tplBuf.Bytes(),
|
|
|
|
).Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
|
|
|
|
// Capture the title of the document so we can put it at the top of
|
|
|
|
// the template and in the metadata. When entering a h1 node the
|
|
|
|
// next node will be the title of the document. Save that value
|
|
|
|
if node.Type == blackfriday.Heading && node.HeadingData.Level == 1 {
|
|
|
|
inHeader = entering
|
|
|
|
return blackfriday.GoToNext
|
|
|
|
}
|
|
|
|
if inHeader {
|
|
|
|
tpld.Title = string(node.Literal)
|
|
|
|
return blackfriday.GoToNext
|
|
|
|
}
|
|
|
|
|
|
|
|
return renderer.RenderNode(&mdBuf, node, entering)
|
|
|
|
})
|
|
|
|
|
|
|
|
// Pass the buffer's parsed contents to the wrapper template
|
|
|
|
tpld.Other = template.HTML(mdBuf.Bytes())
|
|
|
|
|
|
|
|
// Execute the wrapper template
|
|
|
|
err = wc.templates.Get().ExecuteTemplate(w, "markdown_wrapper", tpld)
|
|
|
|
if err != nil && !strings.Contains(err.Error(), "broken pipe") {
|
|
|
|
log.Error("Error executing template '%s': %s", tpl, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-17 16:15:58 +02:00
|
|
|
func (wc *WebController) serveFile(path string) httprouter.Handle {
|
|
|
|
return func(
|
|
|
|
w http.ResponseWriter,
|
|
|
|
r *http.Request,
|
|
|
|
p httprouter.Params,
|
|
|
|
) {
|
2019-12-30 13:00:00 +01:00
|
|
|
http.ServeFile(w, r, wc.resourceDir+"/static"+path)
|
2018-06-17 16:15:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-25 22:53:09 +01:00
|
|
|
func (wc *WebController) serveForm(
|
2019-12-23 23:56:57 +01:00
|
|
|
handler func(*TemplateData, *http.Request) Form,
|
2021-03-09 17:00:43 +01:00
|
|
|
opts handlerOpts,
|
2019-02-25 22:53:09 +01:00
|
|
|
) httprouter.Handle {
|
|
|
|
return func(
|
|
|
|
w http.ResponseWriter,
|
|
|
|
r *http.Request,
|
|
|
|
p httprouter.Params,
|
|
|
|
) {
|
2021-03-09 17:00:43 +01:00
|
|
|
if opts.NoEmbed {
|
|
|
|
w.Header().Set("X-Frame-Options", "DENY")
|
|
|
|
}
|
|
|
|
|
2019-02-25 22:53:09 +01:00
|
|
|
var td = wc.newTemplateData(w, r)
|
2021-03-09 17:00:43 +01:00
|
|
|
if opts.Auth && !td.Authenticated {
|
2019-02-25 22:53:09 +01:00
|
|
|
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// The handler retuns the form which will be rendered
|
|
|
|
td.Form = handler(td, r)
|
2020-07-31 21:21:14 +02:00
|
|
|
td.Title = td.Form.Title
|
2020-06-07 21:12:48 +02:00
|
|
|
td.Form.Username = td.User.Username
|
2019-02-25 22:53:09 +01:00
|
|
|
|
2019-03-31 22:33:22 +02:00
|
|
|
// Execute the extra actions if any
|
|
|
|
if td.Form.Extra.SetCookie != nil {
|
2020-02-05 11:56:08 +01:00
|
|
|
w.Header().Del("Set-Cookie")
|
2019-03-31 22:33:22 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2019-02-25 22:53:09 +01:00
|
|
|
// Remove the recaptcha field if captcha is disabled
|
2019-03-31 22:33:22 +02:00
|
|
|
if wc.captchaKey() == "none" {
|
2019-02-25 22:53:09 +01:00
|
|
|
for i, field := range td.Form.Fields {
|
2019-12-23 23:56:57 +01:00
|
|
|
if field.Type == FieldTypeCaptcha {
|
2019-02-25 22:53:09 +01:00
|
|
|
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 {
|
2019-02-25 22:53:09 +01:00
|
|
|
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") {
|
2019-02-25 22:53:09 +01:00
|
|
|
log.Error("Error executing form page: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-17 16:15:58 +02:00
|
|
|
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 {
|
2021-11-02 10:33:07 +01:00
|
|
|
if len(cookie.Value) == 36 {
|
2018-06-23 21:17:53 +02:00
|
|
|
return cookie.Value, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return "", errors.New("not a valid pixeldrain authentication cookie")
|
2018-06-17 16:15:58 +02:00
|
|
|
}
|
2019-03-28 10:47:27 +01:00
|
|
|
|
|
|
|
func (wc *WebController) captchaKey() string {
|
|
|
|
// This only runs on the first request
|
|
|
|
if wc.captchaSiteKey == "" {
|
2021-03-10 20:13:32 +01:00
|
|
|
capt, err := wc.api.GetMiscRecaptcha()
|
2019-03-28 10:47:27 +01:00
|
|
|
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
|
|
|
|
}
|