slightly better

This commit is contained in:
Curid 2022-06-04 16:10:26 +00:00
parent 86c132f9cd
commit f9b8d6ed1b
7 changed files with 47 additions and 27 deletions

View file

@ -8,7 +8,6 @@ import (
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"heckel.io/ntfy/log"
"io" "io"
"net" "net"
"net/http" "net/http"
@ -23,6 +22,8 @@ import (
"time" "time"
"unicode/utf8" "unicode/utf8"
"heckel.io/ntfy/log"
"github.com/emersion/go-smtp" "github.com/emersion/go-smtp"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
@ -76,6 +77,9 @@ var (
//go:embed "example.html" //go:embed "example.html"
exampleSource string exampleSource string
//go:embed site/app.html
appHTML string
//go:embed site //go:embed site
webFs embed.FS webFs embed.FS
webFsCached = &util.CachingEmbedFS{ModTime: time.Now(), FS: webFs} webFsCached = &util.CachingEmbedFS{ModTime: time.Now(), FS: webFs}
@ -317,10 +321,10 @@ func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request, v *visit
func (s *Server) handleHome(w http.ResponseWriter, r *http.Request, v *visitor) error { func (s *Server) handleHome(w http.ResponseWriter, r *http.Request, v *visitor) error {
if s.config.WebRootIsApp { if s.config.WebRootIsApp {
r.URL.Path = webAppIndex return s.handleRoot(w, r, v)
} else {
r.URL.Path = webHomeIndex
} }
r.URL.Path = webHomeIndex
return s.handleStatic(w, r, v) return s.handleStatic(w, r, v)
} }
@ -332,8 +336,7 @@ func (s *Server) handleTopic(w http.ResponseWriter, r *http.Request, v *visitor)
_, err := io.WriteString(w, `{"unifiedpush":{"version":1}}`+"\n") _, err := io.WriteString(w, `{"unifiedpush":{"version":1}}`+"\n")
return err return err
} }
r.URL.Path = webAppIndex return s.handleRoot(w, r, v)
return s.handleStatic(w, r, v)
} }
func (s *Server) handleEmpty(_ http.ResponseWriter, _ *http.Request, _ *visitor) error { func (s *Server) handleEmpty(_ http.ResponseWriter, _ *http.Request, _ *visitor) error {
@ -353,17 +356,12 @@ func (s *Server) handleExample(w http.ResponseWriter, _ *http.Request, _ *visito
} }
func (s *Server) handleWebConfig(w http.ResponseWriter, _ *http.Request, _ *visitor) error { func (s *Server) handleWebConfig(w http.ResponseWriter, _ *http.Request, _ *visitor) error {
appRoot := "/"
if !s.config.WebRootIsApp {
appRoot = "/app"
}
disallowedTopicsStr := `"` + strings.Join(disallowedTopics, `", "`) + `"` disallowedTopicsStr := `"` + strings.Join(disallowedTopics, `", "`) + `"`
w.Header().Set("Content-Type", "text/javascript") w.Header().Set("Content-Type", "text/javascript")
_, err := io.WriteString(w, fmt.Sprintf(`// Generated server configuration _, err := io.WriteString(w, fmt.Sprintf(`// Generated server configuration
var config = { var config = {
appRoot: "%s",
disallowedTopics: [%s] disallowedTopics: [%s]
};`, appRoot, disallowedTopicsStr)) };`, disallowedTopicsStr))
return err return err
} }
@ -380,6 +378,17 @@ func (s *Server) handleUserStats(w http.ResponseWriter, r *http.Request, v *visi
return nil return nil
} }
func (s *Server) handleRoot(w http.ResponseWriter, r *http.Request, _ *visitor) error {
// Does this need to be sanitized?
html := strings.ReplaceAll(appHTML, `currentPath="/"`, `currentPath="`+r.URL.Path+`"`)
handle := func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(html))
}
util.Gzip(http.HandlerFunc(handle)).ServeHTTP(w, r)
return nil
}
func (s *Server) handleStatic(w http.ResponseWriter, r *http.Request, _ *visitor) error { func (s *Server) handleStatic(w http.ResponseWriter, r *http.Request, _ *visitor) error {
r.URL.Path = webSiteDir + r.URL.Path r.URL.Path = webSiteDir + r.URL.Path
util.Gzip(http.FileServer(http.FS(webFsCached))).ServeHTTP(w, r) util.Gzip(http.FileServer(http.FS(webFsCached))).ServeHTTP(w, r)
@ -509,7 +518,7 @@ func (s *Server) forwardPollRequest(v *visitor, m *message) {
return return
} }
req.Header.Set("X-Poll-ID", m.ID) req.Header.Set("X-Poll-ID", m.ID)
var httpClient = &http.Client{ httpClient := &http.Client{
Timeout: time.Second * 10, Timeout: time.Second * 10,
} }
response, err := httpClient.Do(req) response, err := httpClient.Do(req)

View file

@ -1,6 +1,7 @@
{ {
"name": "ntfy", "name": "ntfy",
"version": "1.0.0", "version": "1.0.0",
"homepage": ".",
"private": true, "private": true,
"scripts": { "scripts": {
"start": "react-scripts start", "start": "react-scripts start",

View file

@ -4,6 +4,5 @@
// The actual config is dynamically generated server-side. // The actual config is dynamically generated server-side.
var config = { var config = {
appRoot: "/",
disallowedTopics: ["docs", "static", "file", "app", "settings"] disallowedTopics: ["docs", "static", "file", "app", "settings"]
}; };

View file

@ -1,9 +1,19 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<base>
<script>
// Calculate the root path.
const currentPath = "/" // Replaced by server.
const pathname = window.location.pathname
const lastIndex = pathname.lastIndexOf(currentPath)
var RootPath = pathname.slice(0, lastIndex+1)
console.log(`Root=${RootPath}`)
document.querySelector("base").href = RootPath;
</script>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>ntfy web</title> <title>ntfy web</title>
<!-- Mobile view --> <!-- Mobile view -->
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"> <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
@ -15,7 +25,7 @@
<meta name="apple-mobile-web-app-status-bar-style" content="#317f6f"> <meta name="apple-mobile-web-app-status-bar-style" content="#317f6f">
<!-- Favicon, see favicon.io --> <!-- Favicon, see favicon.io -->
<link rel="icon" type="image/png" href="%PUBLIC_URL%/static/img/favicon.png"> <link rel="icon" type="image/png" href="static/img/favicon.png">
<!-- Previews in Google, Slack, WhatsApp, etc. --> <!-- Previews in Google, Slack, WhatsApp, etc. -->
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
@ -23,14 +33,14 @@
<meta property="og:site_name" content="ntfy web" /> <meta property="og:site_name" content="ntfy web" />
<meta property="og:title" content="ntfy web" /> <meta property="og:title" content="ntfy web" />
<meta property="og:description" content="ntfy lets you send push notifications via scripts from any computer or phone, entirely without signup or cost. Made with ❤ by Philipp C. Heckel, Apache License 2.0, source at https://heckel.io/ntfy." /> <meta property="og:description" content="ntfy lets you send push notifications via scripts from any computer or phone, entirely without signup or cost. Made with ❤ by Philipp C. Heckel, Apache License 2.0, source at https://heckel.io/ntfy." />
<meta property="og:image" content="%PUBLIC_URL%/static/img/ntfy.png" /> <meta property="og:image" content="static/img/ntfy.png" />
<meta property="og:url" content="https://ntfy.sh" /> <meta property="og:url" content="https://ntfy.sh" />
<!-- Never index --> <!-- Never index -->
<meta name="robots" content="noindex, nofollow" /> <meta name="robots" content="noindex, nofollow" />
<!-- Fonts --> <!-- Fonts -->
<link rel="stylesheet" href="%PUBLIC_URL%/static/css/fonts.css" type="text/css"> <link rel="stylesheet" href="static/css/fonts.css" type="text/css">
</head> </head>
<body> <body>
<noscript> <noscript>
@ -38,6 +48,6 @@
or <a href="https://ntfy.sh/docs/subscribe/phone/">Android/iOS app</a> to subscribe. or <a href="https://ntfy.sh/docs/subscribe/phone/">Android/iOS app</a> to subscribe.
</noscript> </noscript>
<div id="root"></div> <div id="root"></div>
<script src="%PUBLIC_URL%/config.js"></script> <script src="config.js"></script>
</body> </body>
</html> </html>

View file

@ -122,7 +122,7 @@ const NavList = (props) => {
<ListItemIcon><SettingsIcon/></ListItemIcon> <ListItemIcon><SettingsIcon/></ListItemIcon>
<ListItemText primary={t("nav_button_settings")}/> <ListItemText primary={t("nav_button_settings")}/>
</ListItemButton> </ListItemButton>
<ListItemButton onClick={() => openUrl("/docs")}> <ListItemButton onClick={() => openUrl("docs")}>
<ListItemIcon><ArticleIcon/></ListItemIcon> <ListItemIcon><ArticleIcon/></ListItemIcon>
<ListItemText primary={t("nav_button_documentation")}/> <ListItemText primary={t("nav_button_documentation")}/>
</ListItemButton> </ListItemButton>

View file

@ -22,7 +22,7 @@ i18n
escapeValue: false, // not needed for react as it escapes by default escapeValue: false, // not needed for react as it escapes by default
}, },
backend: { backend: {
loadPath: '/static/langs/{{lng}}.json', loadPath: 'static/langs/{{lng}}.json',
} }
}); });

View file

@ -1,16 +1,17 @@
import config from "../app/config"; import config from "../app/config";
import {shortUrl} from "../app/utils"; import {shortUrl} from "../app/utils";
const root = RootPath; // Defined in `public/index.html`.
const routes = { const routes = {
root: config.appRoot, root: root,
settings: "/settings", settings: root+"settings",
subscription: "/:topic", subscription: root+":topic",
subscriptionExternal: "/:baseUrl/:topic", subscriptionExternal: root+":baseUrl/:topic",
forSubscription: (subscription) => { forSubscription: (subscription) => {
if (subscription.baseUrl !== window.location.origin) { if (subscription.baseUrl !== window.location.origin) {
return `/${shortUrl(subscription.baseUrl)}/${subscription.topic}`; return root+`${shortUrl(subscription.baseUrl)}/${subscription.topic}`;
} }
return `/${subscription.topic}`; return root+`${subscription.topic}`;
} }
}; };