diff --git a/init/init.go b/init/init.go index 12d3c8d..894d204 100644 --- a/init/init.go +++ b/init/init.go @@ -11,18 +11,6 @@ import ( "github.com/julienschmidt/httprouter" ) -// PixelWebConfig contains the Pixeldrain Web UI configuration -type PixelWebConfig struct { - APIURLExternal string `toml:"api_url_external"` - APIURLInternal string `toml:"api_url_internal"` - WebsiteAddress string `toml:"website_address"` - 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 @@ -33,6 +21,11 @@ api_url_external = "/api" # Address used to make internal API requests to the backend api_url_internal = "https://pixeldrain.com/api" +# When connecting to the API over a Unix domain socket you should enter the +# socket path here. api_url_internal needs to be correct too, as the API path +# prefix will be derived from there +api_socket_path = "" + website_address = "https://pixeldrain.com" session_cookie_domain = "" resource_dir = "res" @@ -56,12 +49,12 @@ func Init(r *httprouter.Router, prefix string, setLogLevel bool) { // Seed the RNG rand.Seed(time.Now().UnixNano()) - var webconf = &PixelWebConfig{} + var conf webcontroller.Config var _, err = config.New( DefaultConfig, "", "pdwebconf.toml", - webconf, + &conf, true, ) if err != nil { @@ -69,20 +62,9 @@ func Init(r *httprouter.Router, prefix string, setLogLevel bool) { os.Exit(1) } - if !webconf.DebugMode && setLogLevel { + if !conf.DebugMode && setLogLevel { log.SetLogLevel(log.LevelInfo) } - webcontroller.New( - r, - prefix, - webconf.ResourceDir, - webconf.APIURLInternal, - webconf.APIURLExternal, - webconf.WebsiteAddress, - webconf.SessionCookieDomain, - webconf.MaintenanceMode, - webconf.DebugMode, - webconf.ProxyAPIRequests, - ) + webcontroller.New(r, prefix, conf) } diff --git a/webcontroller/file_viewer.go b/webcontroller/file_viewer.go index 8a0172d..ae34f01 100644 --- a/webcontroller/file_viewer.go +++ b/webcontroller/file_viewer.go @@ -14,7 +14,7 @@ import ( func (wc *WebController) viewTokenOrBust() (t string) { var err error - if t, err = wc.api.GetMiscViewToken(); err != nil && !wc.proxyAPIRequests { + if t, err = wc.api.GetMiscViewToken(); err != nil && !wc.config.ProxyAPIRequests { log.Error("Could not get viewtoken: %s", err) } return t diff --git a/webcontroller/opengraph.go b/webcontroller/opengraph.go index 72d9462..d680d58 100644 --- a/webcontroller/opengraph.go +++ b/webcontroller/opengraph.go @@ -25,7 +25,7 @@ func (wc *WebController) metadataFromFile(f pixelapi.FileInfo) (og ogData) { og.addProp("og:title", f.Name) og.addProp("og:site_name", "pixeldrain") og.addProp("og:description", "This file has been shared with you on pixeldrain") - og.addProp("og:url", wc.websiteAddress+"/u/"+f.ID) + og.addProp("og:url", wc.config.WebsiteAddress+"/u/"+f.ID) og.addProp("description", "This file has been shared with you on pixeldrain") og.addName("description", "This file has been shared with you on pixeldrain") og.addName("keywords", "pixeldrain,shared,sharing,upload,file,free") @@ -35,39 +35,39 @@ func (wc *WebController) metadataFromFile(f pixelapi.FileInfo) (og ogData) { if strings.HasPrefix(f.MimeType, "image") { og.addProp("og:type", "article") - og.addProp("og:image", wc.websiteAddress+"/api/file/"+f.ID) - og.addProp("og:image:url", wc.websiteAddress+"/api/file/"+f.ID) - og.addProp("og:image:secure_url", wc.websiteAddress+"/api/file/"+f.ID) + og.addProp("og:image", wc.config.WebsiteAddress+"/api/file/"+f.ID) + og.addProp("og:image:url", wc.config.WebsiteAddress+"/api/file/"+f.ID) + og.addProp("og:image:secure_url", wc.config.WebsiteAddress+"/api/file/"+f.ID) og.addProp("og:image:type", f.MimeType) og.addName("twitter:card", "summary_large_image") - og.addName("twitter:image", wc.websiteAddress+"/api/file/"+f.ID) - og.addLink("image_src", wc.websiteAddress+"/api/file/"+f.ID) + og.addName("twitter:image", wc.config.WebsiteAddress+"/api/file/"+f.ID) + og.addLink("image_src", wc.config.WebsiteAddress+"/api/file/"+f.ID) } else if strings.HasPrefix(f.MimeType, "video") { og.addProp("og:type", "video.other") - og.addProp("og:image", wc.websiteAddress+"/api/file/"+f.ID+"/thumbnail") - og.addProp("og:video", wc.websiteAddress+"/api/file/"+f.ID) - og.addProp("og:video:url", wc.websiteAddress+"/api/file/"+f.ID) - og.addProp("og:video:secure_url", wc.websiteAddress+"/api/file/"+f.ID) + og.addProp("og:image", wc.config.WebsiteAddress+"/api/file/"+f.ID+"/thumbnail") + og.addProp("og:video", wc.config.WebsiteAddress+"/api/file/"+f.ID) + og.addProp("og:video:url", wc.config.WebsiteAddress+"/api/file/"+f.ID) + og.addProp("og:video:secure_url", wc.config.WebsiteAddress+"/api/file/"+f.ID) og.addProp("og:video:type", f.MimeType) og.addName("twitter:card", "player") - og.addName("twitter:image", wc.websiteAddress+"/api/file/"+f.ID+"/thumbnail") - og.addName("twitter:player", wc.websiteAddress+"/api/file/"+f.ID) - og.addName("twitter:player:stream", wc.websiteAddress+"/api/file/"+f.ID) + og.addName("twitter:image", wc.config.WebsiteAddress+"/api/file/"+f.ID+"/thumbnail") + og.addName("twitter:player", wc.config.WebsiteAddress+"/api/file/"+f.ID) + og.addName("twitter:player:stream", wc.config.WebsiteAddress+"/api/file/"+f.ID) og.addName("twitter:player:stream:content_type", f.MimeType) - og.addLink("image_src", wc.websiteAddress+"/api/file/"+f.ID+"/thumbnail") + og.addLink("image_src", wc.config.WebsiteAddress+"/api/file/"+f.ID+"/thumbnail") } else if strings.HasPrefix(f.MimeType, "audio") { og.addProp("og:type", "music.song") - og.addProp("og:image", wc.websiteAddress+"/api/file/"+f.ID+"/thumbnail") - og.addProp("og:audio", wc.websiteAddress+"/api/file/"+f.ID) - og.addProp("og:audio:secure_url", wc.websiteAddress+"/api/file/"+f.ID) + og.addProp("og:image", wc.config.WebsiteAddress+"/api/file/"+f.ID+"/thumbnail") + og.addProp("og:audio", wc.config.WebsiteAddress+"/api/file/"+f.ID) + og.addProp("og:audio:secure_url", wc.config.WebsiteAddress+"/api/file/"+f.ID) og.addProp("og:audio:type", f.MimeType) - og.addLink("image_src", wc.websiteAddress+"/api/file/"+f.ID+"/thumbnail") + og.addLink("image_src", wc.config.WebsiteAddress+"/api/file/"+f.ID+"/thumbnail") } else { og.addProp("og:type", "website") - og.addProp("og:image", wc.websiteAddress+"/api/file/"+f.ID+"/thumbnail") - og.addLink("image_src", wc.websiteAddress+"/api/file/"+f.ID+"/thumbnail") + og.addProp("og:image", wc.config.WebsiteAddress+"/api/file/"+f.ID+"/thumbnail") + og.addLink("image_src", wc.config.WebsiteAddress+"/api/file/"+f.ID+"/thumbnail") } return og } @@ -78,15 +78,15 @@ func (wc *WebController) metadataFromList(l pixelapi.ListInfo) (og ogData) { og.addProp("og:description", "A collection of files on pixeldrain") og.addProp("description", "A collection of files on pixeldrain") og.addName("description", "A collection of files on pixeldrain") - og.addProp("og:url", wc.websiteAddress+"/l/"+l.ID) + og.addProp("og:url", wc.config.WebsiteAddress+"/l/"+l.ID) og.addName("twitter:title", l.Title) og.addName("twitter:site", "@Fornax96") og.addName("twitter:domain", "pixeldrain.com") if l.FileCount > 0 { - og.addProp("og:image", wc.websiteAddress+"/api/file/"+l.Files[0].ID+"/thumbnail") - og.addProp("og:image:url", wc.websiteAddress+"/api/file/"+l.Files[0].ID+"/thumbnail") - og.addName("twitter:image", wc.websiteAddress+"/api/file/"+l.Files[0].ID+"/thumbnail") - og.addLink("image_src", wc.websiteAddress+"/api/file/"+l.Files[0].ID+"/thumbnail") + og.addProp("og:image", wc.config.WebsiteAddress+"/api/file/"+l.Files[0].ID+"/thumbnail") + og.addProp("og:image:url", wc.config.WebsiteAddress+"/api/file/"+l.Files[0].ID+"/thumbnail") + og.addName("twitter:image", wc.config.WebsiteAddress+"/api/file/"+l.Files[0].ID+"/thumbnail") + og.addLink("image_src", wc.config.WebsiteAddress+"/api/file/"+l.Files[0].ID+"/thumbnail") } return og } diff --git a/webcontroller/templates.go b/webcontroller/templates.go index 830ad4f..3a034d8 100644 --- a/webcontroller/templates.go +++ b/webcontroller/templates.go @@ -44,7 +44,7 @@ func (wc *WebController) newTemplateData(w http.ResponseWriter, r *http.Request) tpm: wc.templates, Authenticated: false, UserAgent: r.UserAgent(), - APIEndpoint: template.URL(wc.apiURLExternal), + APIEndpoint: template.URL(wc.config.APIURLExternal), // Use the user's IP address for making requests PixelAPI: wc.api.RealIP(util.RemoteAddress(r)).RealAgent(r.UserAgent()), @@ -74,7 +74,7 @@ func (wc *WebController) newTemplateData(w http.ResponseWriter, r *http.Request) Value: "", Path: "/", Expires: time.Unix(0, 0), - Domain: wc.sessionCookieDomain, + Domain: wc.config.SessionCookieDomain, }) http.SetCookie(w, &http.Cookie{ Name: "pd_auth_key", diff --git a/webcontroller/user_account.go b/webcontroller/user_account.go index 091fbf2..a89d5dd 100644 --- a/webcontroller/user_account.go +++ b/webcontroller/user_account.go @@ -205,7 +205,7 @@ func (wc *WebController) loginForm(td *TemplateData, r *http.Request) (f Form) { Value: session.AuthKey.String(), Path: "/", Expires: time.Now().AddDate(50, 0, 0), - Domain: wc.sessionCookieDomain, + Domain: wc.config.SessionCookieDomain, // Strict means the Cookie will only be sent when the user // reaches a page by a link from the same domain. Lax means any diff --git a/webcontroller/web_controller.go b/webcontroller/web_controller.go index 6cac20a..9aadd52 100644 --- a/webcontroller/web_controller.go +++ b/webcontroller/web_controller.go @@ -18,21 +18,27 @@ import ( blackfriday "github.com/russross/blackfriday/v2" ) +type Config struct { + APIURLExternal string `toml:"api_url_external"` + APIURLInternal string `toml:"api_url_internal"` + APISocketPath string `toml:"api_socket_path"` + WebsiteAddress string `toml:"website_address"` + 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"` +} + // WebController controls how requests are handled and makes sure they have // proper context when running type WebController struct { templates *TemplateManager + config Config - resourceDir string - + // Server hostname, displayed in the footer of every web page hostname string - apiURLInternal string - apiURLExternal string - websiteAddress string - sessionCookieDomain string - proxyAPIRequests bool - // page-specific variables captchaSiteKey string @@ -46,30 +52,19 @@ type WebController struct { // 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, - resourceDir string, - apiURLInternal string, - apiURLExternal string, - websiteAddress string, - sessionCookieDomain string, - maintenanceMode bool, - debugMode bool, - proxyAPIRequests bool, -) (wc *WebController) { +func New(r *httprouter.Router, prefix string, conf Config) (wc *WebController) { var err error wc = &WebController{ - resourceDir: resourceDir, - apiURLInternal: apiURLInternal, - apiURLExternal: apiURLExternal, - websiteAddress: websiteAddress, - sessionCookieDomain: sessionCookieDomain, - proxyAPIRequests: proxyAPIRequests, - httpClient: &http.Client{Timeout: time.Minute * 10}, - api: pixelapi.New(apiURLInternal), + config: conf, + httpClient: &http.Client{Timeout: time.Minute * 10}, + api: pixelapi.New(conf.APIURLInternal), } - wc.templates = NewTemplateManager(resourceDir, apiURLExternal, debugMode) + + if conf.APISocketPath != "" { + wc.api = wc.api.UnixSocketPath(conf.APISocketPath) + } + + wc.templates = NewTemplateManager(conf.ResourceDir, conf.APIURLExternal, conf.DebugMode) wc.templates.ParseTemplates(false) if wc.hostname, err = os.Hostname(); err != nil { @@ -77,7 +72,7 @@ func New( } // Serve static files - var fs = http.FileServer(http.Dir(resourceDir + "/static")) + var fs = http.FileServer(http.Dir(conf.ResourceDir + "/static")) var resourceHandler = func(w http.ResponseWriter, r *http.Request, p httprouter.Params) { // Cache resources for a year w.Header().Set("Cache-Control", "public, max-age=31536000") @@ -93,7 +88,7 @@ func New( r.GET(prefix+"/robots.txt" /* */, wc.serveFile("/robots.txt")) r.GET(prefix+"/ads.txt" /* */, wc.serveFile("/ads.txt")) - if maintenanceMode { + 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)) @@ -101,10 +96,10 @@ func New( return wc } - if proxyAPIRequests { - remoteURL, err := url.Parse(strings.TrimSuffix(apiURLInternal, "/api")) + if conf.ProxyAPIRequests { + remoteURL, err := url.Parse(strings.TrimSuffix(conf.APIURLInternal, "/api")) if err != nil { - panic(fmt.Errorf("failed to parse reverse proxy URL '%s': %w", apiURLInternal, err)) + panic(fmt.Errorf("failed to parse reverse proxy URL '%s': %w", conf.APIURLInternal, err)) } log.Info("Starting API proxy to %s", remoteURL) @@ -315,7 +310,7 @@ func (wc *WebController) serveFile(path string) httprouter.Handle { r *http.Request, p httprouter.Params, ) { - http.ServeFile(w, r, wc.resourceDir+"/static"+path) + http.ServeFile(w, r, wc.config.ResourceDir+"/static"+path) } }