binwiederhier 2 лет назад
Родитель
Сommit
6ad3b2e802

+ 12 - 7
cmd/serve.go

@@ -59,7 +59,7 @@ var flagsServe = append(
 	altsrc.NewDurationFlag(&cli.DurationFlag{Name: "keepalive-interval", Aliases: []string{"keepalive_interval", "k"}, EnvVars: []string{"NTFY_KEEPALIVE_INTERVAL"}, Value: server.DefaultKeepaliveInterval, Usage: "interval of keepalive messages"}),
 	altsrc.NewDurationFlag(&cli.DurationFlag{Name: "manager-interval", Aliases: []string{"manager_interval", "m"}, EnvVars: []string{"NTFY_MANAGER_INTERVAL"}, Value: server.DefaultManagerInterval, Usage: "interval of for message pruning and stats printing"}),
 	altsrc.NewStringSliceFlag(&cli.StringSliceFlag{Name: "disallowed-topics", Aliases: []string{"disallowed_topics"}, EnvVars: []string{"NTFY_DISALLOWED_TOPICS"}, Usage: "topics that are not allowed to be used"}),
-	altsrc.NewStringFlag(&cli.StringFlag{Name: "web-root", Aliases: []string{"web_root"}, EnvVars: []string{"NTFY_WEB_ROOT"}, Value: "app", Usage: "sets web root to landing page (home), web app (app) or disabled (disable)"}),
+	altsrc.NewStringFlag(&cli.StringFlag{Name: "web-root", Aliases: []string{"web_root"}, EnvVars: []string{"NTFY_WEB_ROOT"}, Value: "/", Usage: "sets root of the web app (e.g. /, or /app), or disables it (disable)"}),
 	altsrc.NewBoolFlag(&cli.BoolFlag{Name: "enable-signup", Aliases: []string{"enable_signup"}, EnvVars: []string{"NTFY_ENABLE_SIGNUP"}, Value: false, Usage: "allows users to sign up via the web app, or API"}),
 	altsrc.NewBoolFlag(&cli.BoolFlag{Name: "enable-login", Aliases: []string{"enable_login"}, EnvVars: []string{"NTFY_ENABLE_LOGIN"}, Value: false, Usage: "allows users to log in via the web app, or API"}),
 	altsrc.NewBoolFlag(&cli.BoolFlag{Name: "enable-reservations", Aliases: []string{"enable_reservations"}, EnvVars: []string{"NTFY_ENABLE_RESERVATIONS"}, Value: false, Usage: "allows users to reserve topics (if their tier allows it)"}),
@@ -195,8 +195,6 @@ func execServe(c *cli.Context) error {
 		return errors.New("if set, base-url must start with http:// or https://")
 	} else if baseURL != "" && strings.HasSuffix(baseURL, "/") {
 		return errors.New("if set, base-url must not end with a slash (/)")
-	} else if !util.Contains([]string{"app", "home", "disable"}, webRoot) {
-		return errors.New("if set, web-root must be 'home' or 'app'")
 	} else if upstreamBaseURL != "" && !strings.HasPrefix(upstreamBaseURL, "http://") && !strings.HasPrefix(upstreamBaseURL, "https://") {
 		return errors.New("if set, upstream-base-url must start with http:// or https://")
 	} else if upstreamBaseURL != "" && strings.HasSuffix(upstreamBaseURL, "/") {
@@ -213,8 +211,16 @@ func execServe(c *cli.Context) error {
 		return errors.New("if stripe-secret-key is set, stripe-webhook-key and base-url must also be set")
 	}
 
-	webRootIsApp := webRoot == "app"
-	enableWeb := webRoot != "disable"
+	// Backwards compatibility
+	if webRoot == "app" {
+		webRoot = "/"
+	} else if webRoot == "home" {
+		webRoot = "/app"
+	} else if webRoot == "disable" {
+		webRoot = ""
+	} else if !strings.HasPrefix(webRoot, "/") {
+		webRoot = "/" + webRoot
+	}
 
 	// Default auth permissions
 	authDefault, err := user.ParsePermission(authDefaultAccess)
@@ -293,7 +299,7 @@ func execServe(c *cli.Context) error {
 	conf.KeepaliveInterval = keepaliveInterval
 	conf.ManagerInterval = managerInterval
 	conf.DisallowedTopics = disallowedTopics
-	conf.WebRootIsApp = webRootIsApp
+	conf.WebRoot = webRoot
 	conf.UpstreamBaseURL = upstreamBaseURL
 	conf.SMTPSenderAddr = smtpSenderAddr
 	conf.SMTPSenderUser = smtpSenderUser
@@ -317,7 +323,6 @@ func execServe(c *cli.Context) error {
 	conf.StripeSecretKey = stripeSecretKey
 	conf.StripeWebhookKey = stripeWebhookKey
 	conf.BillingContact = billingContact
-	conf.EnableWeb = enableWeb
 	conf.EnableSignup = enableSignup
 	conf.EnableLogin = enableLogin
 	conf.EnableReservations = enableReservations

+ 1 - 1
docs/config.md

@@ -1255,7 +1255,7 @@ variable before running the `ntfy` command (e.g. `export NTFY_LISTEN_HTTP=:80`).
 | `visitor-request-limit-exempt-hosts`       | `NTFY_VISITOR_REQUEST_LIMIT_EXEMPT_HOSTS`       | *comma-separated host/IP list*                      | -                 | Rate limiting: List of hostnames and IPs to be exempt from request rate limiting                                                                                                                                                |
 | `visitor-subscription-limit`               | `NTFY_VISITOR_SUBSCRIPTION_LIMIT`               | *number*                                            | 30                | Rate limiting: Number of subscriptions per visitor (IP address)                                                                                                                                                                 |
 | `visitor-subscriber-rate-limiting`         | `NTFY_VISITOR_SUBSCRIBER_RATE_LIMITING`         | *bool*                                              | `false`           | Rate limiting: Enables subscriber-based rate limiting                                                                                                                                                                           |
-| `web-root`                                 | `NTFY_WEB_ROOT`                                 | `app`, `home` or `disable`                          | `app`             | Sets web root to landing page (home), web app (app) or disables the web app entirely (disable)                                                                                                                                  |
+| `web-root`                                 | `NTFY_WEB_ROOT`                                 | *path*, e.g. `/` or `/app`, or `disable`            | `/`               | Sets root of the web app (e.g. /, or /app), or disables it entirely (disable)                                                                                                                                                   |
 | `enable-signup`                            | `NTFY_ENABLE_SIGNUP`                            | *boolean* (`true` or `false`)                       | `false`           | Allows users to sign up via the web app, or API                                                                                                                                                                                 |
 | `enable-login`                             | `NTFY_ENABLE_LOGIN`                             | *boolean* (`true` or `false`)                       | `false`           | Allows users to log in via the web app, or API                                                                                                                                                                                  |
 | `enable-reservations`                      | `NTFY_ENABLE_RESERVATIONS`                      | *boolean* (`true` or `false`)                       | `false`           | Allows users to reserve topics (if their tier allows it)                                                                                                                                                                        |

+ 6 - 0
docs/releases.md

@@ -1178,6 +1178,12 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
 
 ## Not released yet
 
+## ntfy server v2.5.0 (UNRELEASED)
+
+**Bug fixes + maintenance:**
+
+* Removed old ntfy website from ntfy entirely (no ticket)
+
 ### ntfy Android app v1.16.1 (UNRELEASED)
 
 **Features:**

+ 2 - 4
server/config.go

@@ -92,7 +92,7 @@ type Config struct {
 	KeepaliveInterval                    time.Duration
 	ManagerInterval                      time.Duration
 	DisallowedTopics                     []string
-	WebRootIsApp                         bool
+	WebRoot                              string // empty to disable
 	DelayedSenderInterval                time.Duration
 	FirebaseKeepaliveInterval            time.Duration
 	FirebasePollInterval                 time.Duration
@@ -133,7 +133,6 @@ type Config struct {
 	StripeWebhookKey                     string
 	StripePriceCacheDuration             time.Duration
 	BillingContact                       string
-	EnableWeb                            bool
 	EnableSignup                         bool // Enable creation of accounts via API and UI
 	EnableLogin                          bool
 	EnableReservations                   bool // Allow users with role "user" to own/reserve topics
@@ -171,7 +170,7 @@ func NewConfig() *Config {
 		KeepaliveInterval:                    DefaultKeepaliveInterval,
 		ManagerInterval:                      DefaultManagerInterval,
 		DisallowedTopics:                     DefaultDisallowedTopics,
-		WebRootIsApp:                         false,
+		WebRoot:                              "/",
 		DelayedSenderInterval:                DefaultDelayedSenderInterval,
 		FirebaseKeepaliveInterval:            DefaultFirebaseKeepaliveInterval,
 		FirebasePollInterval:                 DefaultFirebasePollInterval,
@@ -209,7 +208,6 @@ func NewConfig() *Config {
 		StripeWebhookKey:                     "",
 		StripePriceCacheDuration:             DefaultStripePriceCacheDuration,
 		BillingContact:                       "",
-		EnableWeb:                            true,
 		EnableSignup:                         false,
 		EnableLogin:                          false,
 		EnableReservations:                   false,

+ 9 - 18
server/server.go

@@ -100,11 +100,10 @@ var (
 	urlRegex                                             = regexp.MustCompile(`^https?://`)
 
 	//go:embed site
-	webFs        embed.FS
-	webFsCached  = &util.CachingEmbedFS{ModTime: time.Now(), FS: webFs}
-	webSiteDir   = "/site"
-	webHomeIndex = "/home.html" // Landing page, only if "web-root: home"
-	webAppIndex  = "/app.html"  // React app
+	webFs       embed.FS
+	webFsCached = &util.CachingEmbedFS{ModTime: time.Now(), FS: webFs}
+	webSiteDir  = "/site"
+	webAppIndex = "/app.html" // React app
 
 	//go:embed docs
 	docsStaticFs     embed.FS
@@ -404,8 +403,8 @@ func (s *Server) handleError(w http.ResponseWriter, r *http.Request, v *visitor,
 }
 
 func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request, v *visitor) error {
-	if r.Method == http.MethodGet && r.URL.Path == "/" {
-		return s.ensureWebEnabled(s.handleHome)(w, r, v)
+	if r.Method == http.MethodGet && r.URL.Path == "/" && s.config.WebRoot == "/" {
+		return s.ensureWebEnabled(s.handleRoot)(w, r, v)
 	} else if r.Method == http.MethodHead && r.URL.Path == "/" {
 		return s.ensureWebEnabled(s.handleEmpty)(w, r, v)
 	} else if r.Method == http.MethodGet && r.URL.Path == apiHealthPath {
@@ -490,12 +489,8 @@ func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request, v *visit
 	return errHTTPNotFound
 }
 
-func (s *Server) handleHome(w http.ResponseWriter, r *http.Request, v *visitor) error {
-	if s.config.WebRootIsApp {
-		r.URL.Path = webAppIndex
-	} else {
-		r.URL.Path = webHomeIndex
-	}
+func (s *Server) handleRoot(w http.ResponseWriter, r *http.Request, v *visitor) error {
+	r.URL.Path = webAppIndex
 	return s.handleStatic(w, r, v)
 }
 
@@ -527,13 +522,9 @@ func (s *Server) handleHealth(w http.ResponseWriter, _ *http.Request, _ *visitor
 }
 
 func (s *Server) handleWebConfig(w http.ResponseWriter, _ *http.Request, _ *visitor) error {
-	appRoot := "/"
-	if !s.config.WebRootIsApp {
-		appRoot = "/app"
-	}
 	response := &apiConfigResponse{
 		BaseURL:            "", // Will translate to window.location.origin
-		AppRoot:            appRoot,
+		AppRoot:            s.config.WebRoot,
 		EnableLogin:        s.config.EnableLogin,
 		EnableSignup:       s.config.EnableSignup,
 		EnablePayments:     s.config.StripeSecretKey != "",

+ 6 - 4
server/server.yml

@@ -167,11 +167,13 @@
 #
 # disallowed-topics:
 
-# Defines if the root route (/) is pointing to the landing page (as on ntfy.sh) or the
-# web app. If you self-host, you don't want to change this.
-# Can be "app" (default), "home" or "disable" to disable the web app entirely.
+# Defines the root path of the web app, or disables the web app entirely.
 #
-# web-root: app
+# Can be any simple path, e.g. "/", "/app", or "/ntfy". For backwards-compatibility reasons,
+# the values "app" (maps to "/"), "home" (maps to "/app"), or "disable" (maps to "") to disable
+# the web app entirely.
+#
+# web-root: /
 
 # Various feature flags used to control the web app, and API access, mainly around user and
 # account management.

+ 1 - 1
server/server_middleware.go

@@ -51,7 +51,7 @@ func (s *Server) limitRequestsWithTopic(next handleFunc) handleFunc {
 
 func (s *Server) ensureWebEnabled(next handleFunc) handleFunc {
 	return func(w http.ResponseWriter, r *http.Request, v *visitor) error {
-		if !s.config.EnableWeb {
+		if s.config.WebRoot == "" {
 			return errHTTPNotFound
 		}
 		return next(w, r, v)

+ 2 - 9
server/server_test.go

@@ -220,10 +220,6 @@ func TestServer_StaticSites(t *testing.T) {
 	require.Equal(t, 200, rr.Code)
 	require.Contains(t, rr.Body.String(), `<meta name="robots" content="noindex, nofollow"/>`)
 
-	rr = request(t, s, "GET", "/static/css/home.css", "", nil)
-	require.Equal(t, 200, rr.Code)
-	require.Contains(t, rr.Body.String(), `/* general styling */`)
-
 	rr = request(t, s, "GET", "/docs", "", nil)
 	require.Equal(t, 301, rr.Code)
 
@@ -232,7 +228,7 @@ func TestServer_StaticSites(t *testing.T) {
 
 func TestServer_WebEnabled(t *testing.T) {
 	conf := newTestConfig(t)
-	conf.EnableWeb = false
+	conf.WebRoot = "" // Disable web app
 	s := newTestServer(t, conf)
 
 	rr := request(t, s, "GET", "/", "", nil)
@@ -245,7 +241,7 @@ func TestServer_WebEnabled(t *testing.T) {
 	require.Equal(t, 404, rr.Code)
 
 	conf2 := newTestConfig(t)
-	conf2.EnableWeb = true
+	conf2.WebRoot = "/"
 	s2 := newTestServer(t, conf2)
 
 	rr = request(t, s2, "GET", "/", "", nil)
@@ -253,9 +249,6 @@ func TestServer_WebEnabled(t *testing.T) {
 
 	rr = request(t, s2, "GET", "/config.js", "", nil)
 	require.Equal(t, 200, rr.Code)
-
-	rr = request(t, s2, "GET", "/static/css/home.css", "", nil)
-	require.Equal(t, 200, rr.Code)
 }
 
 func TestServer_PublishLargeMessage(t *testing.T) {

+ 0 - 182
web/public/home.html

@@ -1,182 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="UTF-8">
-
-    <title>ntfy.sh | Send push notifications to your phone via PUT/POST</title>
-    <link rel="stylesheet" href="static/css/home.css" type="text/css">
-
-    <!-- Mobile view -->
-    <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 name="HandheldFriendly" content="true">
-
-    <!-- Mobile browsers, background color -->
-    <meta name="theme-color" content="#317f6f">
-    <meta name="msapplication-navbutton-color" content="#317f6f">
-    <meta name="apple-mobile-web-app-status-bar-style" content="#317f6f">
-
-    <!-- Favicon, see favicon.io -->
-    <link rel="icon" type="image/png" href="static/img/favicon.png">
-
-    <!-- Previews in Google, Slack, WhatsApp, etc. -->
-    <meta property="og:type" content="website" />
-    <meta property="og:locale" content="en_US" />
-    <meta property="og:site_name" content="ntfy.sh" />
-    <meta property="og:title" content="ntfy.sh | Push notifications to your phone or desktop via PUT/POST" />
-    <meta property="og:description" content="ntfy is a simple HTTP-based pub-sub notification service. It allows you to send desktop notifications via scripts from any computer, 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="/static/img/ntfy.png" />
-    <meta property="og:url" content="https://ntfy.sh" />
-
-    <!-- Fonts -->
-    <link rel="stylesheet" href="static/css/fonts.css" type="text/css">
-</head>
-<body>
-
-<nav id="header">
-    <div id="headerBox">
-        <img id="logo" src="static/img/ntfy.png" alt="logo"/>
-        <div id="name">ntfy</div>
-        <ol>
-            <li><a href="app">Web app</a></li>
-            <li><a href="docs/subscribe/phone/">Android/iOS</a></li>
-            <li><a href="docs/">Docs</a></li>
-            <li><a href="docs/publish/">API</a></li>
-            <li><a href="https://github.com/binwiederhier/ntfy">GitHub</a></li>
-        </ol>
-    </div>
-</nav>
-<div id="main">
-    <h1>Send push notifications to your phone or desktop via PUT/POST</h1>
-    <p>
-        <b>ntfy</b> (pronounce: <i>notify</i>) is a simple HTTP-based <a href="https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern">pub-sub</a> notification service.
-        It allows you to send notifications to your phone or desktop via scripts from any computer,
-        entirely <b>without signup, cost or setup</b>. It's also <a href="https://github.com/binwiederhier/ntfy">open source</a> if you want to run your own.
-    </p>
-    <div id="screenshots">
-        <a href="static/img/screenshot-curl.png"><img src="static/img/screenshot-curl.png"/></a>
-        <a href="static/img/screenshot-web-detail.png"><img src="static/img/screenshot-web-detail.png"/></a>
-        <span class="nowrap">
-            <a href="static/img/screenshot-phone-main.jpg"><img src="static/img/screenshot-phone-main.jpg"/></a>
-            <a href="static/img/screenshot-phone-detail.jpg"><img src="static/img/screenshot-phone-detail.jpg"/></a>
-            <a href="static/img/screenshot-phone-notification.jpg"><img src="static/img/screenshot-phone-notification.jpg"/></a>
-        </span>
-    </div>
-
-    <h2 id="publish" class="anchor">Publishing messages</h2>
-    <p>
-        <a href="docs/publish/">Publishing messages</a> can be done via PUT or POST. Topics are created on the fly by subscribing or publishing to them.
-        Because there is no sign-up, <b>the topic is essentially a password</b>, so pick something that's not easily guessable.
-    </p>
-    <p class="smallMarginBottom">
-        Here's an example showing how to publish a message using a POST request (via <tt>curl -d</tt>):
-    </p>
-    <code>
-        curl -d "Backup successful 😀" <span class="ntfyUrl">ntfy.sh</span>/mytopic
-    </code>
-    <p class="smallMarginBottom">
-        There are <a href="docs/publish/">more features</a> related to publishing messages: You can set a
-        <a href="docs/publish/#message-priority">notification priority</a>, a <a href="docs/publish/#message-title">title</a>,
-        and <a href="docs/publish/#tags-emojis">tag messages</a>.
-        Here's an example using some of them together:
-    </p>
-    <code>
-        curl \<br/>
-        &nbsp;&nbsp;-H "Title: Unauthorized access detected" \<br/>
-        &nbsp;&nbsp;-H "Priority: urgent" \<br/>
-        &nbsp;&nbsp;-H "Tags: warning,skull" \<br/>
-        &nbsp;&nbsp;-d "Remote access to $(hostname) detected. Act right away." \<br/>
-        &nbsp;&nbsp;<span class="ntfyUrl">ntfy.sh</span>/mytopic
-    </code>
-    <p>
-        Here's what that looks like in the <a href="docs/subscribe/phone/">Android app</a>:
-    </p>
-    <figure>
-        <img src="static/img/screenshot-phone-popover.png" style="max-height: 200px"/>
-        <figcaption>Urgent notification with pop-over</figcaption>
-    </figure>
-
-    <h2 id="subscribe" class="anchor">Subscribe to a topic</h2>
-    <p>
-        You can create and subscribe to a topic either <a href="docs/subscribe/phone/">using your phone</a>,
-        in <a href="docs/subscribe/web/">this web UI</a>, or in your own app by <a href="docs/subscribe/api/">subscribing via the API</a>.
-    </p>
-
-    <h3 id="subscribe-phone" class="anchor">Subscribe from your phone</h3>
-    <p>
-        Simply get the app and start <a href="docs/publish/">publishing messages</a>. To learn more about the app,
-        <a href="docs/subscribe/phone/">check out the documentation</a>.
-    </p>
-    <p>
-        <a href="https://play.google.com/store/apps/details?id=io.heckel.ntfy"><img src="static/img/badge-googleplay.png"></a>
-        <a href="https://f-droid.org/en/packages/io.heckel.ntfy/"><img src="static/img/badge-fdroid.png"></a>
-        <a href="https://apps.apple.com/us/app/ntfy/id1625396347"><img src="static/img/badge-appstore.png"></a>
-    </p>
-    <p>
-        Here's a video showing the app in action:
-    </p>
-    <figure>
-        <video controls muted autoplay loop src="static/img/android-video-overview.mp4" style="max-width: 650px"></video>
-        <figcaption>Sending push notifications to your Android phone</figcaption>
-    </figure>
-
-    <h3 id="subscribe-web" class="anchor">Subscribe via web app</h3>
-    <p>
-        Subscribe to topics in the <a href="app">web app</a> and receive messages as <b>desktop notification</b>.
-        It is available at <b><a href="app"><span class="ntfyUrl">ntfy.sh</span>/app</a></b>.
-    </p>
-    <figure>
-        <a href="app"><img src="static/img/screenshot-web-detail.png" width="100%"/></a>
-        <figcaption>ntfy web app, available at <a href="app"><span class="ntfyUrl">ntfy.sh</span>/app</a></figcaption>
-    </figure>
-
-    <h3 id="subscribe-api" class="anchor">Subscribe using the API</h3>
-    <p>
-        There's a super simple API that you can use to integrate your own app. You can consume
-        a <a href="docs/subscribe/api/#subscribe-as-json-stream">JSON stream</a>,
-        an <a href="docs/subscribe/api/#subscribe-as-sse-stream">SSE/EventSource stream</a>,
-        a <a href="docs/subscribe/api/#subscribe-as-raw-stream">plain text stream</a>,
-        or <a href="docs/subscribe/api/#websockets">via WebSockets</a>.
-    </p>
-    <p class="smallMarginBottom">
-        Here's an example for JSON. The <b>connection stays open</b>, so you can retrieve messages as they come in:
-    </p>
-    <code>
-        $ curl -s <span class="ntfyUrl">ntfy.sh</span>/mytopic/json<br/>
-        {"id":"SLiKI64DOt","time":1635528757,"event":"open","topic":"mytopic"}<br/>
-        {"id":"hwQ2YpKdmg","time":1635528741,"event":"message","topic":"mytopic","message":"Hi!"}<br/>
-        {"id":"DGUDShMCsc","time":1635528787,"event":"keepalive","topic":"mytopic"}<br/>
-        ...
-    </code>
-    <p>
-        Here's a short video demonstrating it in action:
-    </p>
-    <figure>
-        <video controls muted autoplay loop src="static/img/android-video-subscribe-api.mp4" style="max-width: 650px"></video>
-        <figcaption>Subscribing to the JSON stream with <tt>curl</tt></figcaption>
-    </figure>
-
-    <h3 id="docs" class="anchor">Check out the docs!</h3>
-    <p>
-        ntfy has so many more features and you can learn about all of them <a href="docs/">in the documentation</a>
-        (I tried my very best to make it the best docs ever 😉, not sure if I succeeded, hehe).
-    </p>
-    <figure>
-        <a href="docs/"><img width="100%" src="static/img/screenshot-docs.png"/></a>
-        <figcaption>Check out the documentation</figcaption>
-    </figure>
-
-    <h3 id="free-software" class="anchor">100% open source &amp; forever free</h3>
-    <p>
-        I love free software, and I'm doing this because it's fun. I have no bad intentions, and I will
-        never monetize or sell your information. This service will always stay
-        <a href="https://github.com/binwiederhier/ntfy">free and open</a>.
-        You can read more in the <a href="docs/faq/">FAQs</a> and in the <a href="docs/privacy/">privacy policy</a>.
-    </p>
-
-    <center id="ironicCenterTagDontFreakOut"><i>Made with ❤️ by <a href="https://heckel.io">Philipp C. Heckel</a></i></center>
-</div>
-<div id="lightbox" class="lightbox"></div>
-<script src="static/js/home.js"></script>
-</body>
-</html>

+ 2 - 2
web/public/index.html

@@ -15,7 +15,7 @@
   <meta name="apple-mobile-web-app-status-bar-style" content="#317f6f">
 
   <!-- Favicon, see favicon.io -->
-  <link rel="icon" type="image/png" href="%PUBLIC_URL%/static/img/favicon.ico">
+  <link rel="icon" type="image/png" href="%PUBLIC_URL%/static/images/favicon.ico">
 
   <!-- Previews in Google, Slack, WhatsApp, etc. -->
   <meta property="og:type" content="website" />
@@ -23,7 +23,7 @@
   <meta property="og:site_name" 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. 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="%PUBLIC_URL%/static/images/ntfy.png" />
   <meta property="og:url" content="https://ntfy.sh" />
 
   <!-- Never index -->

+ 0 - 280
web/public/static/css/home.css

@@ -1,280 +0,0 @@
-/* general styling */
-
-html, body {
-    font-family: 'Roboto', sans-serif;
-    font-weight: 400;
-    font-size: 1.1em;
-    color: #444;
-    margin: 0;
-    padding: 0;
-}
-
-html {
-    /* prevent scrollbar from repositioning website:
-     * https://www.w3docs.com/snippets/css/how-to-prevent-scrollbar-from-repositioning-web-page.html */
-    overflow-y: scroll;
-}
-
-a, a:visited {
-    color: #338574;
-}
-
-a:hover {
-    text-decoration: none;
-    color: #317f6f;
-}
-
-h1 {
-    margin-top: 35px;
-    margin-bottom: 30px;
-    font-size: 2.5em;
-    word-wrap: break-word; /* For very long topics */
-    padding-right: 40px; /* For the X on the detail page */
-    font-weight: 300;
-    color: #666;
-}
-
-h2 {
-    margin-top: 30px;
-    margin-bottom: 5px;
-    font-size: 1.8em;
-    font-weight: 300;
-    color: #333;
-}
-
-h3 {
-    margin-top: 25px;
-    margin-bottom: 5px;
-    font-size: 1.3em;
-    font-weight: 300;
-    color: #333;
-}
-
-p {
-    margin-top: 10px;
-    margin-bottom: 20px;
-    line-height: 160%;
-    font-weight: 400;
-}
-
-p.smallMarginBottom {
-    margin-bottom: 10px;
-}
-
-b {
-    font-weight: 500;
-}
-
-tt {
-    background: #eee;
-    padding: 2px 7px;
-    border-radius: 3px;
-}
-
-code {
-    display: block;
-    background: #eee;
-    font-family: monospace;
-    padding: 20px;
-    border-radius: 3px;
-    margin-top: 10px;
-    margin-bottom: 20px;
-    overflow-x: auto;
-    white-space: nowrap;
-}
-
-/* Main page */
-
-#main {
-    max-width: 900px;
-    margin: 0 auto 50px auto;
-    padding: 0 10px;
-}
-
-#error {
-    color: darkred;
-    font-style: italic;
-}
-
-#ironicCenterTagDontFreakOut {
-    color: #666;
-}
-
-/* Anchors */
-
-.anchor .anchorLink {
-    color: #ccc;
-    text-decoration: none;
-    padding: 0 5px;
-    visibility: hidden;
-}
-
-.anchor:hover .anchorLink {
-    visibility: visible;
-}
-
-.anchor .anchorLink:hover {
-    color: #338574;
-    visibility: visible;
-}
-
-/* Figures */
-
-figure {
-    text-align: center;
-}
-
-figure img, figure video {
-    filter: drop-shadow(3px 3px 3px #ccc);
-    border-radius: 7px;
-    max-width: 100%;
-}
-
-figure video {
-    width: 100%;
-    max-height: 450px;
-}
-
-figcaption {
-    text-align: center;
-    font-style: italic;
-    padding-top: 10px;
-}
-
-/* Screenshots */
-
-#screenshots {
-    text-align: center;
-}
-
-#screenshots img {
-    height: 190px;
-    margin: 3px;
-    border-radius: 5px;
-    filter: drop-shadow(2px 2px 2px #ddd);
-}
-
-#screenshots .nowrap {
-    white-space: nowrap;
-}
-
-/* Lightbox; thanks to https://yossiabramov.com/blog/vanilla-js-lightbox */
-
-.lightbox {
-    opacity: 0;
-    visibility: hidden;
-    position: fixed;
-    left:0;
-    right: 0;
-    top: 0;
-    bottom: 0;
-    z-index: -1;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    transition: all 0.15s ease-in;
-}
-
-.lightbox.show {
-    background-color: rgba(0,0,0, 0.75);
-    opacity: 1;
-    visibility: visible;
-    z-index: 1000;
-}
-
-.lightbox img {
-    max-width: 90%;
-    max-height: 90%;
-    filter: drop-shadow(5px 5px 10px #222);
-    border-radius: 5px;
-}
-
-.lightbox .close-lightbox {
-    cursor: pointer;
-    position: absolute;
-    top: 30px;
-    right: 30px;
-    width: 20px;
-    height: 20px;
-}
-
-.lightbox .close-lightbox::after,
-.lightbox .close-lightbox::before {
-    content: '';
-    width: 3px;
-    height: 20px;
-    background-color: #ddd;
-    position: absolute;
-    border-radius: 5px;
-    transform: rotate(45deg);
-}
-
-.lightbox .close-lightbox::before {
-    transform: rotate(-45deg);
-}
-
-.lightbox .close-lightbox:hover::after,
-.lightbox .close-lightbox:hover::before {
-    background-color: #fff;
-}
-
-/* Header */
-
-#header {
-    background: #338574;
-    height: 130px;
-}
-
-#header #headerBox {
-    max-width: 900px;
-    margin: 0 auto;
-    padding: 0 10px;
-}
-
-#header #logo {
-    margin-top: 23px;
-    float: left;
-}
-
-#header #name {
-    float: left;
-    color: white;
-    font-size: 2.6em;
-    font-weight: 300;
-    margin: 35px 0 0 20px;
-}
-
-#header ol {
-    list-style-type: none;
-    float: right;
-    margin-top: 80px;
-}
-
-#header ol li {
-    display: inline-block;
-    margin: 0 10px;
-    font-weight: 400;
-}
-
-#header ol li a, nav ol li a:visited {
-    color: white;
-    text-decoration: none;
-}
-
-#header ol li a:hover {
-    text-decoration: underline;
-}
-
-li {
-    padding: 4px 0;
-    margin: 4px 0;
-    font-size: 0.9em;
-}
-
-
-/* Hide top menu SMALL SCREEN */
-@media only screen and (max-width: 780px) {
-    #header ol {
-        display: none;
-    }
-}

+ 0 - 0
web/public/static/img/favicon.ico → web/public/static/images/favicon.ico


+ 0 - 0
web/public/static/img/ntfy.png → web/public/static/images/ntfy.png


BIN
web/public/static/img/android-video-overview.mp4


BIN
web/public/static/img/android-video-subscribe-api.mp4


BIN
web/public/static/img/badge-appstore.png


BIN
web/public/static/img/badge-fdroid.png


BIN
web/public/static/img/badge-googleplay.png


BIN
web/public/static/img/screenshot-curl.png


BIN
web/public/static/img/screenshot-docs.png


BIN
web/public/static/img/screenshot-phone-add.jpg


BIN
web/public/static/img/screenshot-phone-detail.jpg


BIN
web/public/static/img/screenshot-phone-main.jpg


BIN
web/public/static/img/screenshot-phone-notification.jpg


BIN
web/public/static/img/screenshot-phone-popover.png


BIN
web/public/static/img/screenshot-web-detail.png


+ 0 - 84
web/public/static/js/home.js

@@ -1,84 +0,0 @@
-
-/* All the things */
-
-let currentUrl = window.location.hostname;
-if (window.location.port) {
-    currentUrl += ':' + window.location.port
-}
-
-/* Screenshots */
-const lightbox = document.getElementById("lightbox");
-
-const showScreenshotOverlay = (e, el, index) => {
-    lightbox.classList.add('show');
-    document.addEventListener('keydown', nextScreenshotKeyboardListener);
-    return showScreenshot(e, index);
-};
-
-const showScreenshot = (e, index) => {
-    const actualIndex = resolveScreenshotIndex(index);
-    lightbox.innerHTML = '<div class="close-lightbox"></div>' + screenshots[actualIndex].innerHTML;
-    lightbox.querySelector('img').onclick = (e) => { return showScreenshot(e,actualIndex+1); };
-    currentScreenshotIndex = actualIndex;
-    e.stopPropagation();
-    return false;
-};
-
-const nextScreenshot = (e) => {
-    return showScreenshot(e, currentScreenshotIndex+1);
-};
-
-const previousScreenshot = (e) => {
-    return showScreenshot(e, currentScreenshotIndex-1);
-};
-
-const resolveScreenshotIndex = (index) => {
-    if (index < 0) {
-        return screenshots.length - 1;
-    } else if (index > screenshots.length - 1) {
-        return 0;
-    }
-    return index;
-};
-
-const hideScreenshotOverlay = (e) => {
-    lightbox.classList.remove('show');
-    document.removeEventListener('keydown', nextScreenshotKeyboardListener);
-};
-
-const nextScreenshotKeyboardListener = (e) => {
-    switch (e.keyCode) {
-        case 37:
-            previousScreenshot(e);
-            break;
-        case 39:
-            nextScreenshot(e);
-            break;
-    }
-};
-
-let currentScreenshotIndex = 0;
-const screenshots = [...document.querySelectorAll("#screenshots a")];
-screenshots.forEach((el, index) => {
-    el.onclick = (e) => { return showScreenshotOverlay(e, el, index); };
-});
-
-lightbox.onclick = hideScreenshotOverlay;
-
-// Add anchor links
-document.querySelectorAll('.anchor').forEach((el) => {
-    if (el.hasAttribute('id')) {
-        const id = el.getAttribute('id');
-        const anchor = document.createElement('a');
-        anchor.innerHTML = `<a href="#${id}" class="anchorLink">#</a>`;
-        el.appendChild(anchor);
-    }
-});
-
-// Change ntfy.sh url and protocol to match self-hosted one
-document.querySelectorAll('.ntfyUrl').forEach((el) => {
-    el.innerHTML = currentUrl;
-});
-document.querySelectorAll('.ntfyProtocol').forEach((el) => {
-    el.innerHTML = window.location.protocol + "//";
-});