binwiederhier пре 1 месец
родитељ
комит
5a1aa68ead

+ 5 - 78
server/config.go

@@ -182,7 +182,7 @@ type Config struct {
 	WebPushStartupQueries                string
 	WebPushExpiryDuration                time.Duration
 	WebPushExpiryWarningDuration         time.Duration
-	Version                              string // injected by App
+	Version                              string // Injected by App
 }
 
 // NewConfig instantiates a default new server config
@@ -279,86 +279,13 @@ func NewConfig() *Config {
 	}
 }
 
-// configHashData is a subset of Config fields used for computing the config hash.
-// It excludes sensitive fields (keys, passwords, tokens) and runtime-only fields.
-type configHashData struct {
-	BaseURL                              string
-	ListenHTTP                           string
-	ListenHTTPS                          string
-	ListenUnix                           string
-	CacheDuration                        time.Duration
-	AttachmentTotalSizeLimit             int64
-	AttachmentFileSizeLimit              int64
-	AttachmentExpiryDuration             time.Duration
-	KeepaliveInterval                    time.Duration
-	ManagerInterval                      time.Duration
-	DisallowedTopics                     []string
-	WebRoot                              string
-	MessageDelayMin                      time.Duration
-	MessageDelayMax                      time.Duration
-	MessageSizeLimit                     int
-	TotalTopicLimit                      int
-	VisitorSubscriptionLimit             int
-	VisitorAttachmentTotalSizeLimit      int64
-	VisitorAttachmentDailyBandwidthLimit int64
-	VisitorRequestLimitBurst             int
-	VisitorRequestLimitReplenish         time.Duration
-	VisitorMessageDailyLimit             int
-	VisitorEmailLimitBurst               int
-	VisitorEmailLimitReplenish           time.Duration
-	EnableSignup                         bool
-	EnableLogin                          bool
-	RequireLogin                         bool
-	EnableReservations                   bool
-	EnableMetrics                        bool
-	EnablePayments                       bool
-	EnableCalls                          bool
-	EnableEmails                         bool
-	EnableWebPush                        bool
-	BillingContact                       string
-	Version                              string
-}
-
 // Hash computes a SHA-256 hash of the configuration. This is used to detect
 // configuration changes for the web app version check feature.
 func (c *Config) Hash() string {
-	data := configHashData{
-		BaseURL:                              c.BaseURL,
-		ListenHTTP:                           c.ListenHTTP,
-		ListenHTTPS:                          c.ListenHTTPS,
-		ListenUnix:                           c.ListenUnix,
-		CacheDuration:                        c.CacheDuration,
-		AttachmentTotalSizeLimit:             c.AttachmentTotalSizeLimit,
-		AttachmentFileSizeLimit:              c.AttachmentFileSizeLimit,
-		AttachmentExpiryDuration:             c.AttachmentExpiryDuration,
-		KeepaliveInterval:                    c.KeepaliveInterval,
-		ManagerInterval:                      c.ManagerInterval,
-		DisallowedTopics:                     c.DisallowedTopics,
-		WebRoot:                              c.WebRoot,
-		MessageDelayMin:                      c.MessageDelayMin,
-		MessageDelayMax:                      c.MessageDelayMax,
-		MessageSizeLimit:                     c.MessageSizeLimit,
-		TotalTopicLimit:                      c.TotalTopicLimit,
-		VisitorSubscriptionLimit:             c.VisitorSubscriptionLimit,
-		VisitorAttachmentTotalSizeLimit:      c.VisitorAttachmentTotalSizeLimit,
-		VisitorAttachmentDailyBandwidthLimit: c.VisitorAttachmentDailyBandwidthLimit,
-		VisitorRequestLimitBurst:             c.VisitorRequestLimitBurst,
-		VisitorRequestLimitReplenish:         c.VisitorRequestLimitReplenish,
-		VisitorMessageDailyLimit:             c.VisitorMessageDailyLimit,
-		VisitorEmailLimitBurst:               c.VisitorEmailLimitBurst,
-		VisitorEmailLimitReplenish:           c.VisitorEmailLimitReplenish,
-		EnableSignup:                         c.EnableSignup,
-		EnableLogin:                          c.EnableLogin,
-		RequireLogin:                         c.RequireLogin,
-		EnableReservations:                   c.EnableReservations,
-		EnableMetrics:                        c.EnableMetrics,
-		EnablePayments:                       c.StripeSecretKey != "",
-		EnableCalls:                          c.TwilioAccount != "",
-		EnableEmails:                         c.SMTPSenderFrom != "",
-		EnableWebPush:                        c.WebPushPublicKey != "",
-		BillingContact:                       c.BillingContact,
-		Version:                              c.Version,
+	b, err := json.Marshal(c)
+	if err != nil {
+		fmt.Println(err)
 	}
-	b, _ := json.Marshal(data)
+	fmt.Println(string(b))
 	return fmt.Sprintf("%x", sha256.Sum256(b))
 }

+ 2 - 1
web/public/static/langs/en.json

@@ -5,7 +5,8 @@
   "common_back": "Back",
   "common_copy_to_clipboard": "Copy to clipboard",
   "common_refresh": "Refresh",
-  "version_update_available": "New ntfy version available. Please refresh the page.",
+  "version_update_available_title": "New version available",
+  "version_update_available_description": "The ntfy server has been updated. Please refresh the page.",
   "signup_title": "Create a ntfy account",
   "signup_form_username": "Username",
   "signup_form_password": "Password",

+ 1 - 1
web/src/app/VersionChecker.js

@@ -3,7 +3,7 @@
  * or configuration changes, prompting users to refresh the page.
  */
 
-const CHECK_INTERVAL = 5 * 60 * 1000; // 5 minutes
+const CHECK_INTERVAL = 30 * 1000; // 5 * 60 * 1000; // 5 minutes
 
 class VersionChecker {
   constructor() {

+ 3 - 49
web/src/components/App.jsx

@@ -1,18 +1,6 @@
 import * as React from "react";
-import { createContext, Suspense, useContext, useEffect, useState, useMemo, useCallback } from "react";
-import {
-  Box,
-  Toolbar,
-  CssBaseline,
-  Backdrop,
-  CircularProgress,
-  useMediaQuery,
-  ThemeProvider,
-  createTheme,
-  Snackbar,
-  Button,
-  Alert,
-} from "@mui/material";
+import { createContext, Suspense, useContext, useEffect, useState, useMemo } from "react";
+import { Box, Toolbar, CssBaseline, Backdrop, CircularProgress, useMediaQuery, ThemeProvider, createTheme } from "@mui/material";
 import { useLiveQuery } from "dexie-react-hooks";
 import { BrowserRouter, Outlet, Route, Routes, useParams } from "react-router-dom";
 import { useTranslation } from "react-i18next";
@@ -26,13 +14,7 @@ import userManager from "../app/UserManager";
 import { expandUrl, getKebabCaseLangStr } from "../app/utils";
 import ErrorBoundary from "./ErrorBoundary";
 import routes from "./routes";
-import {
-  useAccountListener,
-  useBackgroundProcesses,
-  useConnectionListeners,
-  useWebPushTopics,
-  useVersionChangeListener,
-} from "./hooks";
+import { useAccountListener, useBackgroundProcesses, useConnectionListeners, useWebPushTopics } from "./hooks";
 import PublishDialog from "./PublishDialog";
 import Messaging from "./Messaging";
 import Login from "./Login";
@@ -118,12 +100,10 @@ const updateTitle = (newNotificationsCount) => {
 };
 
 const Layout = () => {
-  const { t } = useTranslation();
   const params = useParams();
   const { account, setAccount } = useContext(AccountContext);
   const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false);
   const [sendDialogOpenMode, setSendDialogOpenMode] = useState("");
-  const [versionChanged, setVersionChanged] = useState(false);
   const users = useLiveQuery(() => userManager.all());
   const subscriptions = useLiveQuery(() => subscriptionManager.all());
   const webPushTopics = useWebPushTopics();
@@ -135,18 +115,9 @@ const Layout = () => {
       (config.base_url === s.baseUrl && params.topic === s.topic)
   );
 
-  const handleVersionChange = useCallback(() => {
-    setVersionChanged(true);
-  }, []);
-
-  const handleRefresh = useCallback(() => {
-    window.location.reload();
-  }, []);
-
   useConnectionListeners(account, subscriptions, users, webPushTopics);
   useAccountListener(setAccount);
   useBackgroundProcesses();
-  useVersionChangeListener(handleVersionChange);
   useEffect(() => updateTitle(newNotificationsCount), [newNotificationsCount]);
 
   return (
@@ -169,23 +140,6 @@ const Layout = () => {
         />
       </Main>
       <Messaging selected={selected} dialogOpenMode={sendDialogOpenMode} onDialogOpenModeChange={setSendDialogOpenMode} />
-      <Snackbar
-        open={versionChanged}
-        anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
-        sx={{ bottom: { xs: 70, md: 24 } }}
-      >
-        <Alert
-          severity="info"
-          variant="filled"
-          action={
-            <Button color="inherit" size="small" onClick={handleRefresh}>
-              {t("common_refresh")}
-            </Button>
-          }
-        >
-          {t("version_update_available")}
-        </Alert>
-      </Snackbar>
     </Box>
   );
 };

+ 27 - 2
web/src/components/Navigation.jsx

@@ -21,7 +21,7 @@ import {
   useTheme,
 } from "@mui/material";
 import * as React from "react";
-import { useContext, useState } from "react";
+import { useCallback, useContext, useState } from "react";
 import ChatBubbleOutlineIcon from "@mui/icons-material/ChatBubbleOutline";
 import Person from "@mui/icons-material/Person";
 import SettingsIcon from "@mui/icons-material/Settings";
@@ -44,7 +44,7 @@ import UpgradeDialog from "./UpgradeDialog";
 import { AccountContext } from "./App";
 import { PermissionDenyAll, PermissionRead, PermissionReadWrite, PermissionWrite } from "./ReserveIcons";
 import { SubscriptionPopup } from "./SubscriptionPopup";
-import { useNotificationPermissionListener } from "./hooks";
+import { useNotificationPermissionListener, useVersionChangeListener } from "./hooks";
 
 const navWidth = 280;
 
@@ -91,6 +91,13 @@ const NavList = (props) => {
   const { account } = useContext(AccountContext);
   const [subscribeDialogKey, setSubscribeDialogKey] = useState(0);
   const [subscribeDialogOpen, setSubscribeDialogOpen] = useState(false);
+  const [versionChanged, setVersionChanged] = useState(false);
+
+  const handleVersionChange = useCallback(() => {
+    setVersionChanged(true);
+  }, []);
+
+  useVersionChangeListener(handleVersionChange);
 
   const handleSubscribeReset = () => {
     setSubscribeDialogOpen(false);
@@ -119,6 +126,7 @@ const NavList = (props) => {
   const showNotificationContextNotSupportedBox = notifier.browserSupported() && !notifier.contextSupported(); // Only show if notifications are generally supported in the browser
 
   const alertVisible =
+    versionChanged ||
     showNotificationPermissionRequired ||
     showNotificationPermissionDenied ||
     showNotificationIOSInstallRequired ||
@@ -129,6 +137,7 @@ const NavList = (props) => {
     <>
       <Toolbar sx={{ display: { xs: "none", sm: "block" } }} />
       <List component="nav" sx={{ paddingTop: { xs: 0, sm: alertVisible ? 0 : "" } }}>
+        {versionChanged && <VersionUpdateBanner />}
         {showNotificationPermissionRequired && <NotificationPermissionRequired />}
         {showNotificationPermissionDenied && <NotificationPermissionDeniedAlert />}
         {showNotificationBrowserNotSupportedBox && <NotificationBrowserNotSupportedAlert />}
@@ -425,4 +434,20 @@ const NotificationContextNotSupportedAlert = () => {
   );
 };
 
+const VersionUpdateBanner = () => {
+  const { t } = useTranslation();
+  const handleRefresh = () => {
+    window.location.reload();
+  };
+  return (
+    <Alert severity="info" sx={{ paddingTop: 2 }}>
+      <AlertTitle>{t("version_update_available_title")}</AlertTitle>
+      <Typography gutterBottom>{t("version_update_available_description")}</Typography>
+      <Button sx={{ float: "right" }} color="inherit" size="small" onClick={handleRefresh}>
+        {t("common_refresh")}
+      </Button>
+    </Alert>
+  );
+};
+
 export default Navigation;