Add reverse proxy for local debugging
This commit is contained in:
25
init/init.go
25
init/init.go
@@ -19,18 +19,32 @@ type PixelWebConfig struct {
|
||||
SessionCookieDomain string `toml:"session_cookie_domain"`
|
||||
ResourceDir string `toml:"resource_dir"`
|
||||
DebugMode bool `toml:"debug_mode"`
|
||||
ProxyAPIRequests bool `toml:"proxy_api_requests"`
|
||||
MaintenanceMode bool `toml:"maintenance_mode"`
|
||||
}
|
||||
|
||||
// DefaultConfig is the default configuration for Pixeldrain Web
|
||||
const DefaultConfig = `# Pixeldrain Web UI server configuration
|
||||
const DefaultConfig = `## Pixeldrain Web UI server configuration
|
||||
|
||||
# Address used in the browser for making requests directly to the API. Can be
|
||||
# relative to the current domain name
|
||||
api_url_external = "/api"
|
||||
|
||||
# Address used to make internal API requests to the backend
|
||||
api_url_internal = "https://pixeldrain.com/api"
|
||||
|
||||
api_url_external = "/api" # Used in the web browser
|
||||
api_url_internal = "http://127.0.0.1:8080" # Used for internal API requests to the pixeldrain server, not visible to users
|
||||
website_address = "https://pixeldrain.com"
|
||||
session_cookie_domain = ".pixeldrain.com"
|
||||
session_cookie_domain = ""
|
||||
resource_dir = "res"
|
||||
debug_mode = false
|
||||
|
||||
# Parse all the templates every time a request comes in
|
||||
debug_mode = true
|
||||
|
||||
# Create proxy listeners to forward all requests made to /api to
|
||||
# api_url_internal
|
||||
proxy_api_requests = true
|
||||
|
||||
# When this is true every request will return a maintainance HTML page
|
||||
maintenance_mode = false
|
||||
`
|
||||
|
||||
@@ -68,5 +82,6 @@ func Init(r *httprouter.Router, prefix string, setLogLevel bool) {
|
||||
webconf.SessionCookieDomain,
|
||||
webconf.MaintenanceMode,
|
||||
webconf.DebugMode,
|
||||
webconf.ProxyAPIRequests,
|
||||
)
|
||||
}
|
||||
|
@@ -1,42 +1,42 @@
|
||||
function Toolbar(viewer) {
|
||||
this.viewer = viewer
|
||||
this.visible = false
|
||||
this.sharebarVisible = false
|
||||
this.currentFile = null
|
||||
this.editWindow = null
|
||||
this.viewer = viewer
|
||||
this.visible = false
|
||||
this.sharebarVisible = false
|
||||
this.currentFile = null
|
||||
this.editWindow = null
|
||||
|
||||
this.views = 0
|
||||
this.downloads = 0
|
||||
this.statsWebsocket = null
|
||||
this.views = 0
|
||||
this.downloads = 0
|
||||
this.statsWebsocket = null
|
||||
|
||||
this.divToolbar = document.getElementById("toolbar")
|
||||
this.divFilePreview = document.getElementById("filepreview")
|
||||
this.downloadFrame = document.getElementById("download_frame")
|
||||
this.spanViews = document.getElementById("stat_views")
|
||||
this.spanDownloads = document.getElementById("stat_downloads")
|
||||
this.spanSize = document.getElementById("stat_size")
|
||||
this.divToolbar = document.getElementById("toolbar")
|
||||
this.divFilePreview = document.getElementById("filepreview")
|
||||
this.downloadFrame = document.getElementById("download_frame")
|
||||
this.spanViews = document.getElementById("stat_views")
|
||||
this.spanDownloads = document.getElementById("stat_downloads")
|
||||
this.spanSize = document.getElementById("stat_size")
|
||||
|
||||
this.btnToggleToolbar = document.getElementById("btn_toggle_toolbar")
|
||||
this.btnDownload = document.getElementById("btn_download")
|
||||
this.btnCopyLink = document.getElementById("btn_copy")
|
||||
this.spanCopyLink = document.querySelector("#btn_copy > span")
|
||||
this.btnShare = document.getElementById("btn_share")
|
||||
this.divSharebar = document.getElementById("sharebar")
|
||||
this.btnDownload = document.getElementById("btn_download")
|
||||
this.btnCopyLink = document.getElementById("btn_copy")
|
||||
this.spanCopyLink = document.querySelector("#btn_copy > span")
|
||||
this.btnShare = document.getElementById("btn_share")
|
||||
this.divSharebar = document.getElementById("sharebar")
|
||||
|
||||
this.btnToggleToolbar.addEventListener("click", () => { this.toggle() })
|
||||
this.btnDownload.addEventListener("click", () => { this.download() })
|
||||
this.btnCopyLink.addEventListener("click", () => { this.copyUrl() })
|
||||
this.btnShare.addEventListener("click", () => { this.toggleSharebar() })
|
||||
this.btnDownload.addEventListener("click", () => { this.download() })
|
||||
this.btnCopyLink.addEventListener("click", () => { this.copyUrl() })
|
||||
this.btnShare.addEventListener("click", () => { this.toggleSharebar() })
|
||||
}
|
||||
|
||||
Toolbar.prototype.setFile = function(file) {
|
||||
Toolbar.prototype.setFile = function (file) {
|
||||
this.currentFile = file
|
||||
this.spanSize.innerText = formatDataVolume(file.size, 3)
|
||||
|
||||
this.setStats()
|
||||
}
|
||||
|
||||
Toolbar.prototype.setStats = function() {
|
||||
Toolbar.prototype.setStats = function () {
|
||||
let size = this.currentFile.size
|
||||
|
||||
this.spanViews.innerText = "loading..."
|
||||
@@ -47,19 +47,19 @@ Toolbar.prototype.setStats = function() {
|
||||
}
|
||||
|
||||
this.statsWebsocket = new WebSocket(
|
||||
location.origin.replace(/^http/, 'ws')+"/api/file/"+this.currentFile.id+"/stats"
|
||||
location.origin.replace(/^http/, 'ws') + "/api/file/" + this.currentFile.id + "/stats"
|
||||
)
|
||||
this.statsWebsocket.onmessage = (msg) => {
|
||||
let j = JSON.parse(msg.data)
|
||||
console.debug("WS update", j)
|
||||
|
||||
this.views = j.views
|
||||
this.downloads = Math.round(j.bandwidth/size)
|
||||
this.downloads = Math.round(j.bandwidth / size)
|
||||
this.spanViews.innerText = formatThousands(this.views)
|
||||
this.spanDownloads.innerText = formatThousands(this.downloads)
|
||||
}
|
||||
this.statsWebsocket.onerror = (err) => {
|
||||
log.error("WS error", err)
|
||||
console.error("WS error", err)
|
||||
this.statsWebsocket.close()
|
||||
this.statsWebsocket = null
|
||||
|
||||
@@ -74,7 +74,7 @@ Toolbar.prototype.setStats = function() {
|
||||
}
|
||||
}
|
||||
|
||||
Toolbar.prototype.toggle = function() {
|
||||
Toolbar.prototype.toggle = function () {
|
||||
if (this.visible) {
|
||||
if (this.sharebarVisible) { this.toggleSharebar() }
|
||||
|
||||
@@ -90,7 +90,7 @@ Toolbar.prototype.toggle = function() {
|
||||
}
|
||||
}
|
||||
|
||||
Toolbar.prototype.toggleSharebar = function() {
|
||||
Toolbar.prototype.toggleSharebar = function () {
|
||||
if (navigator.share) {
|
||||
navigator.share({
|
||||
title: this.viewer.title,
|
||||
@@ -100,19 +100,19 @@ Toolbar.prototype.toggleSharebar = function() {
|
||||
return
|
||||
}
|
||||
|
||||
if(this.sharebarVisible){
|
||||
if (this.sharebarVisible) {
|
||||
this.divSharebar.style.left = "-8em"
|
||||
this.btnShare.classList.remove("button_highlight")
|
||||
this.sharebarVisible = false
|
||||
}else{
|
||||
} else {
|
||||
this.divSharebar.style.left = "8em"
|
||||
this.btnShare.classList.add("button_highlight")
|
||||
this.sharebarVisible = true
|
||||
}
|
||||
}
|
||||
|
||||
Toolbar.prototype.download = function() {
|
||||
if (captchaKey === "none" || captchaKey === ""){
|
||||
Toolbar.prototype.download = function () {
|
||||
if (captchaKey === "none" || captchaKey === "") {
|
||||
console.debug("Server doesn't support captcha, starting download")
|
||||
this.downloadFrame.src = this.currentFile.download_href
|
||||
return
|
||||
@@ -140,7 +140,7 @@ Toolbar.prototype.download = function() {
|
||||
// Set the callback function
|
||||
recaptchaCallback = token => {
|
||||
// Download the file using the recaptcha token
|
||||
this.downloadFrame.src = this.currentFile.download_href+"&recaptcha_response="+token
|
||||
this.downloadFrame.src = this.currentFile.download_href + "&recaptcha_response=" + token
|
||||
this.captchaModal.close()
|
||||
}
|
||||
|
||||
@@ -158,25 +158,25 @@ Toolbar.prototype.download = function() {
|
||||
console.debug("Showing rate limiting captcha")
|
||||
showCaptcha(
|
||||
"Rate limiting enabled!",
|
||||
"This file is using a suspicious amount of bandwidth relative "+
|
||||
"to its popularity. To continue downloading this file you "+
|
||||
"This file is using a suspicious amount of bandwidth relative " +
|
||||
"to its popularity. To continue downloading this file you " +
|
||||
"will have to prove that you're a human first.",
|
||||
)
|
||||
} else if (this.currentFile.availability === "virus_detected_captcha_required") {
|
||||
console.debug("Showing virus captcha")
|
||||
showCaptcha(
|
||||
"Malware warning!",
|
||||
"According to our scanning systems this file may contain a "+
|
||||
"virus of type '"+this.currentFile.availability_name+"'. You "+
|
||||
"can continue downloading this file at your own risk, but you "+
|
||||
"According to our scanning systems this file may contain a " +
|
||||
"virus of type '" + this.currentFile.availability_name + "'. You " +
|
||||
"can continue downloading this file at your own risk, but you " +
|
||||
"will have to prove that you're a human first.",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Toolbar.prototype.copyUrl = function() {
|
||||
if(copyText(window.location.href)) {
|
||||
Toolbar.prototype.copyUrl = function () {
|
||||
if (copyText(window.location.href)) {
|
||||
console.log('Text copied')
|
||||
this.spanCopyLink.innerText = "Copied!"
|
||||
this.btnCopyLink.classList.add("button_highlight")
|
||||
@@ -194,9 +194,9 @@ Toolbar.prototype.copyUrl = function() {
|
||||
}
|
||||
|
||||
// Called by the google recaptcha script
|
||||
let recaptchaElement = null
|
||||
let recaptchaElement = null
|
||||
let recaptchaCallback = null
|
||||
function loadCaptcha(){
|
||||
function loadCaptcha() {
|
||||
grecaptcha.render(recaptchaElement, {
|
||||
sitekey: captchaKey,
|
||||
theme: "dark",
|
||||
|
@@ -19,7 +19,7 @@ import (
|
||||
|
||||
func (wc *WebController) viewTokenOrBust() (t string) {
|
||||
var err error
|
||||
if t, err = wc.api.GetMiscViewToken(); err != nil {
|
||||
if t, err = wc.api.GetMiscViewToken(); err != nil && !wc.proxyAPIRequests {
|
||||
log.Error("Could not get viewtoken: %s", err)
|
||||
}
|
||||
return t
|
||||
|
@@ -6,6 +6,8 @@ import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -26,11 +28,11 @@ type WebController struct {
|
||||
|
||||
hostname string
|
||||
|
||||
apiURLInternal string
|
||||
apiURLExternal string
|
||||
websiteAddress string
|
||||
|
||||
apiURLInternal string
|
||||
apiURLExternal string
|
||||
websiteAddress string
|
||||
sessionCookieDomain string
|
||||
proxyAPIRequests bool
|
||||
|
||||
// page-specific variables
|
||||
captchaSiteKey string
|
||||
@@ -55,6 +57,7 @@ func New(
|
||||
sessionCookieDomain string,
|
||||
maintenanceMode bool,
|
||||
debugMode bool,
|
||||
proxyAPIRequests bool,
|
||||
) (wc *WebController) {
|
||||
var err error
|
||||
wc = &WebController{
|
||||
@@ -63,6 +66,7 @@ func New(
|
||||
apiURLExternal: apiURLExternal,
|
||||
websiteAddress: websiteAddress,
|
||||
sessionCookieDomain: sessionCookieDomain,
|
||||
proxyAPIRequests: proxyAPIRequests,
|
||||
httpClient: &http.Client{Timeout: time.Minute * 10},
|
||||
api: apiclient.New(apiURLInternal),
|
||||
}
|
||||
@@ -96,6 +100,23 @@ func New(
|
||||
return wc
|
||||
}
|
||||
|
||||
if proxyAPIRequests {
|
||||
proxPath := strings.TrimSuffix(apiURLInternal, "/api")
|
||||
proxURL, err := url.Parse(proxPath)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to parse reverse proxy URL '%s': %w", proxPath, err))
|
||||
}
|
||||
|
||||
var prox = httputil.NewSingleHostReverseProxy(proxURL)
|
||||
prox.Transport = wc.httpClient.Transport
|
||||
r.Handler("OPTIONS", "/api/*p", prox)
|
||||
r.Handler("POST", "/api/*p", prox)
|
||||
r.Handler("GET", "/api/*p", prox)
|
||||
r.Handler("PUT", "/api/*p", prox)
|
||||
r.Handler("PATCH", "/api/*p", prox)
|
||||
r.Handler("DELETE", "/api/*p", prox)
|
||||
}
|
||||
|
||||
r.NotFound = http.HandlerFunc(wc.serveNotFound)
|
||||
|
||||
// Request method shorthands. These help keep the array of handlers aligned
|
||||
|
Reference in New Issue
Block a user