diff --git a/cmd/serve.go b/cmd/serve.go index 62e0a14a..47c9baf4 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -62,6 +62,7 @@ var flagsServe = append( 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)"}), + altsrc.NewBoolFlag(&cli.BoolFlag{Name: "require-login", Aliases: []string{"require_login"}, EnvVars: []string{"NTFY_REQUIRE_LOGIN"}, Value: false, Usage: "all actions via the web app requires a login"}), altsrc.NewStringFlag(&cli.StringFlag{Name: "upstream-base-url", Aliases: []string{"upstream_base_url"}, EnvVars: []string{"NTFY_UPSTREAM_BASE_URL"}, Value: "", Usage: "forward poll request to an upstream server, this is needed for iOS push notifications for self-hosted servers"}), altsrc.NewStringFlag(&cli.StringFlag{Name: "upstream-access-token", Aliases: []string{"upstream_access_token"}, EnvVars: []string{"NTFY_UPSTREAM_ACCESS_TOKEN"}, Value: "", Usage: "access token to use for the upstream server; needed only if upstream rate limits are exceeded or upstream server requires auth"}), altsrc.NewStringFlag(&cli.StringFlag{Name: "smtp-sender-addr", Aliases: []string{"smtp_sender_addr"}, EnvVars: []string{"NTFY_SMTP_SENDER_ADDR"}, Usage: "SMTP server address (host:port) for outgoing emails"}), diff --git a/docs/config.md b/docs/config.md index 9479301a..6d67e57f 100644 --- a/docs/config.md +++ b/docs/config.md @@ -1424,6 +1424,7 @@ variable before running the `ntfy` command (e.g. `export NTFY_LISTEN_HTTP=:80`). | `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) | +| `require-login` | `NTFY_REQUIRE_LOGIN` | *boolean* (`true` or `false`) | `false` | All actions via the web app require a login | | `stripe-secret-key` | `NTFY_STRIPE_SECRET_KEY` | *string* | - | Payments: Key used for the Stripe API communication, this enables payments | | `stripe-webhook-key` | `NTFY_STRIPE_WEBHOOK_KEY` | *string* | - | Payments: Key required to validate the authenticity of incoming webhooks from Stripe | | `billing-contact` | `NTFY_BILLING_CONTACT` | *email address* or *website* | - | Payments: Email or website displayed in Upgrade dialog as a billing contact | diff --git a/server/config.go b/server/config.go index 7267ce9d..1204910e 100644 --- a/server/config.go +++ b/server/config.go @@ -240,6 +240,7 @@ func NewConfig() *Config { EnableSignup: false, EnableLogin: false, EnableReservations: false, + RequireLogin: false, AccessControlAllowOrigin: "*", Version: "", WebPushPrivateKey: "", diff --git a/server/server.go b/server/server.go index ee2da76a..c922387a 100644 --- a/server/server.go +++ b/server/server.go @@ -583,6 +583,7 @@ func (s *Server) handleWebConfig(w http.ResponseWriter, _ *http.Request, _ *visi EnableCalls: s.config.TwilioAccount != "", EnableEmails: s.config.SMTPSenderFrom != "", EnableReservations: s.config.EnableReservations, + ReuqireLogin: s.config.RequireLogin, EnableWebPush: s.config.WebPushPublicKey != "", BillingContact: s.config.BillingContact, WebPushPublicKey: s.config.WebPushPublicKey, diff --git a/server/server.yml b/server/server.yml index 7329d37e..ded53c5e 100644 --- a/server/server.yml +++ b/server/server.yml @@ -214,10 +214,12 @@ # - enable-signup allows users to sign up via the web app, or API # - enable-login allows users to log in via the web app, or API # - enable-reservations allows users to reserve topics (if their tier allows it) +# - require-login all user actions via the web app require a login # # enable-signup: false # enable-login: false # enable-reservations: false +# require-login: false # Server URL of a Firebase/APNS-connected ntfy server (likely "https://ntfy.sh"). # diff --git a/server/types.go b/server/types.go index fb08fb05..2565faa6 100644 --- a/server/types.go +++ b/server/types.go @@ -401,6 +401,7 @@ type apiConfigResponse struct { EnableEmails bool `json:"enable_emails"` EnableReservations bool `json:"enable_reservations"` EnableWebPush bool `json:"enable_web_push"` + RequireLogin bool `json:"require_login"` BillingContact string `json:"billing_contact"` WebPushPublicKey string `json:"web_push_public_key"` DisallowedTopics []string `json:"disallowed_topics"` diff --git a/web/public/static/langs/en.json b/web/public/static/langs/en.json index 3ad04ea7..e0bc1085 100644 --- a/web/public/static/langs/en.json +++ b/web/public/static/langs/en.json @@ -97,6 +97,8 @@ "notifications_none_for_any_description": "To send notifications to a topic, simply PUT or POST to the topic URL. Here's an example using one of your topics.", "notifications_no_subscriptions_title": "It looks like you don't have any subscriptions yet.", "notifications_no_subscriptions_description": "Click the \"{{linktext}}\" link to create or subscribe to a topic. After that, you can send messages via PUT or POST and you'll receive notifications here.", + "notifications_no_subscriptions_login_title": "This page requires a Login.", + "notifications_no_subscriptions_login_description": "Click \"{{linktext}}\" to login into your account.", "notifications_example": "Example", "notifications_more_details": "For more information, check out the website or documentation.", "display_name_dialog_title": "Change display name", diff --git a/web/src/components/Navigation.jsx b/web/src/components/Navigation.jsx index 7e30931a..0c4da2e5 100644 --- a/web/src/components/Navigation.jsx +++ b/web/src/components/Navigation.jsx @@ -135,7 +135,7 @@ const NavList = (props) => { {showNotificationContextNotSupportedBox && } {showNotificationIOSInstallRequired && } {alertVisible && } - {!showSubscriptionsList && ( + {!showSubscriptionsList && (session.exists() || !config.require_login) && ( navigate(routes.app)} selected={location.pathname === config.app_root}> @@ -164,30 +164,36 @@ const NavList = (props) => { )} + {session.exists() || !config.require_login && ( navigate(routes.settings)} selected={location.pathname === routes.settings}> + )} openUrl("/docs")}> + {session.exists() || !config.require_login && ( props.onPublishMessageClick()}> + )} + {session.exists() || !config.require_login && ( setSubscribeDialogOpen(true)}> + )} {showUpgradeBanner && ( // The text background gradient didn't seem to do well with switching between light/dark mode, // So adding a `key` forces React to replace the entire component when the theme changes diff --git a/web/src/components/Notifications.jsx b/web/src/components/Notifications.jsx index 0b8b2e7d..69b5978e 100644 --- a/web/src/components/Notifications.jsx +++ b/web/src/components/Notifications.jsx @@ -37,6 +37,7 @@ import priority5 from "../img/priority-5.svg"; import logoOutline from "../img/ntfy-outline.svg"; import AttachmentIcon from "./AttachmentIcon"; import { useAutoSubscribe } from "./hooks"; +import session from "../app/Session"; const priorityFiles = { 1: priority1, @@ -634,12 +635,16 @@ const NoSubscriptions = () => { {t("action_bar_logo_alt")}
- {t("notifications_no_subscriptions_title")} + {!session.exists() && !config.require_login && t("notifications_no_subscriptions_title")} + {!session.exists() && config.require_login && t("notifications_no_subscriptions_login_title")}
- {t("notifications_no_subscriptions_description", { + {!session.exists() && !config.require_login && t("notifications_no_subscriptions_description", { linktext: t("nav_button_subscribe"), })} + {!session.exists() && config.require_login && t("notifications_no_subscriptions_login_description", { + linktext: t("action_bar_sign_in"), + })} diff --git a/web/src/components/Preferences.jsx b/web/src/components/Preferences.jsx index 6770f282..d0767a2a 100644 --- a/web/src/components/Preferences.jsx +++ b/web/src/components/Preferences.jsx @@ -65,16 +65,22 @@ const maybeUpdateAccountSettings = async (payload) => { } }; -const Preferences = () => ( - - - - - - - - -); +const Preferences = () => { + if (!session.exists() or !config.requireLogin) { + window.location.href = routes.app; + return <>; + } + return ( + + + + + + + + + ); +}; const Notifications = () => { const { t } = useTranslation();