Merge pull request #1434 from binwiederhier/require-login
Require login
This commit is contained in:
@@ -63,6 +63,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-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-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: "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-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: "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"}),
|
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"}),
|
||||||
@@ -171,6 +172,7 @@ func execServe(c *cli.Context) error {
|
|||||||
webRoot := c.String("web-root")
|
webRoot := c.String("web-root")
|
||||||
enableSignup := c.Bool("enable-signup")
|
enableSignup := c.Bool("enable-signup")
|
||||||
enableLogin := c.Bool("enable-login")
|
enableLogin := c.Bool("enable-login")
|
||||||
|
requireLogin := c.Bool("require-login")
|
||||||
enableReservations := c.Bool("enable-reservations")
|
enableReservations := c.Bool("enable-reservations")
|
||||||
upstreamBaseURL := c.String("upstream-base-url")
|
upstreamBaseURL := c.String("upstream-base-url")
|
||||||
upstreamAccessToken := c.String("upstream-access-token")
|
upstreamAccessToken := c.String("upstream-access-token")
|
||||||
@@ -318,10 +320,12 @@ func execServe(c *cli.Context) error {
|
|||||||
return errors.New("if upstream-base-url is set, base-url must also be set")
|
return errors.New("if upstream-base-url is set, base-url must also be set")
|
||||||
} else if upstreamBaseURL != "" && baseURL != "" && baseURL == upstreamBaseURL {
|
} else if upstreamBaseURL != "" && baseURL != "" && baseURL == upstreamBaseURL {
|
||||||
return errors.New("base-url and upstream-base-url cannot be identical, you'll likely want to set upstream-base-url to https://ntfy.sh, see https://ntfy.sh/docs/config/#ios-instant-notifications")
|
return errors.New("base-url and upstream-base-url cannot be identical, you'll likely want to set upstream-base-url to https://ntfy.sh, see https://ntfy.sh/docs/config/#ios-instant-notifications")
|
||||||
} else if authFile == "" && (enableSignup || enableLogin || enableReservations || stripeSecretKey != "") {
|
} else if authFile == "" && (enableSignup || enableLogin || requireLogin || enableReservations || stripeSecretKey != "") {
|
||||||
return errors.New("cannot set enable-signup, enable-login, enable-reserve-topics, or stripe-secret-key if auth-file is not set")
|
return errors.New("cannot set enable-signup, enable-login, require-login, enable-reserve-topics, or stripe-secret-key if auth-file is not set")
|
||||||
} else if enableSignup && !enableLogin {
|
} else if enableSignup && !enableLogin {
|
||||||
return errors.New("cannot set enable-signup without also setting enable-login")
|
return errors.New("cannot set enable-signup without also setting enable-login")
|
||||||
|
} else if requireLogin && !enableLogin {
|
||||||
|
return errors.New("cannot set require-login without also setting enable-login")
|
||||||
} else if !payments.Available && (stripeSecretKey != "" || stripeWebhookKey != "") {
|
} else if !payments.Available && (stripeSecretKey != "" || stripeWebhookKey != "") {
|
||||||
return errors.New("cannot set stripe-secret-key or stripe-webhook-key, support for payments is not available in this build (nopayments)")
|
return errors.New("cannot set stripe-secret-key or stripe-webhook-key, support for payments is not available in this build (nopayments)")
|
||||||
} else if stripeSecretKey != "" && (stripeWebhookKey == "" || baseURL == "") {
|
} else if stripeSecretKey != "" && (stripeWebhookKey == "" || baseURL == "") {
|
||||||
@@ -475,6 +479,7 @@ func execServe(c *cli.Context) error {
|
|||||||
conf.BillingContact = billingContact
|
conf.BillingContact = billingContact
|
||||||
conf.EnableSignup = enableSignup
|
conf.EnableSignup = enableSignup
|
||||||
conf.EnableLogin = enableLogin
|
conf.EnableLogin = enableLogin
|
||||||
|
conf.RequireLogin = requireLogin
|
||||||
conf.EnableReservations = enableReservations
|
conf.EnableReservations = enableReservations
|
||||||
conf.EnableMetrics = enableMetrics
|
conf.EnableMetrics = enableMetrics
|
||||||
conf.MetricsListenHTTP = metricsListenHTTP
|
conf.MetricsListenHTTP = metricsListenHTTP
|
||||||
|
|||||||
@@ -1698,6 +1698,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-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-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) |
|
| `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-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 |
|
| `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 |
|
| `billing-contact` | `NTFY_BILLING_CONTACT` | *email address* or *website* | - | Payments: Email or website displayed in Upgrade dialog as a billing contact |
|
||||||
|
|||||||
@@ -1470,6 +1470,10 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
|
|||||||
|
|
||||||
### ntfy server v2.15.0 (UNRELEASED)
|
### ntfy server v2.15.0 (UNRELEASED)
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
|
||||||
|
* Add `require-login` flag to redirect to login page if not logged in ([#1434](https://github.com/binwiederhier/ntfy/pull/1434)/[#238](https://github.com/binwiederhier/ntfy/issues/238)/[#1329](https://github.com/binwiederhier/ntfy/pull/1329), thanks to [@theatischbein](https://github.com/theatischbein) for implementing most of this)
|
||||||
|
|
||||||
**Bug fixes + maintenance:**
|
**Bug fixes + maintenance:**
|
||||||
|
|
||||||
* Add mutex around message cache writes to avoid `database locked` errors ([#1397](https://github.com/binwiederhier/ntfy/pull/1397), [#1391](https://github.com/binwiederhier/ntfy/issues/1391), thanks to [@timofej673](https://github.com/timofej673))
|
* Add mutex around message cache writes to avoid `database locked` errors ([#1397](https://github.com/binwiederhier/ntfy/pull/1397), [#1391](https://github.com/binwiederhier/ntfy/issues/1391), thanks to [@timofej673](https://github.com/timofej673))
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ type Config struct {
|
|||||||
BillingContact string
|
BillingContact string
|
||||||
EnableSignup bool // Enable creation of accounts via API and UI
|
EnableSignup bool // Enable creation of accounts via API and UI
|
||||||
EnableLogin bool
|
EnableLogin bool
|
||||||
|
RequireLogin bool
|
||||||
EnableReservations bool // Allow users with role "user" to own/reserve topics
|
EnableReservations bool // Allow users with role "user" to own/reserve topics
|
||||||
EnableMetrics bool
|
EnableMetrics bool
|
||||||
AccessControlAllowOrigin string // CORS header field to restrict access from web clients
|
AccessControlAllowOrigin string // CORS header field to restrict access from web clients
|
||||||
@@ -256,6 +257,7 @@ func NewConfig() *Config {
|
|||||||
EnableSignup: false,
|
EnableSignup: false,
|
||||||
EnableLogin: false,
|
EnableLogin: false,
|
||||||
EnableReservations: false,
|
EnableReservations: false,
|
||||||
|
RequireLogin: false,
|
||||||
AccessControlAllowOrigin: "*",
|
AccessControlAllowOrigin: "*",
|
||||||
Version: "",
|
Version: "",
|
||||||
WebPushPrivateKey: "",
|
WebPushPrivateKey: "",
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
"heckel.io/ntfy/v2/payments"
|
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -33,7 +31,9 @@ import (
|
|||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
"heckel.io/ntfy/v2/log"
|
"heckel.io/ntfy/v2/log"
|
||||||
|
"heckel.io/ntfy/v2/payments"
|
||||||
"heckel.io/ntfy/v2/user"
|
"heckel.io/ntfy/v2/user"
|
||||||
"heckel.io/ntfy/v2/util"
|
"heckel.io/ntfy/v2/util"
|
||||||
"heckel.io/ntfy/v2/util/sprig"
|
"heckel.io/ntfy/v2/util/sprig"
|
||||||
@@ -600,6 +600,7 @@ func (s *Server) handleWebConfig(w http.ResponseWriter, _ *http.Request, _ *visi
|
|||||||
BaseURL: "", // Will translate to window.location.origin
|
BaseURL: "", // Will translate to window.location.origin
|
||||||
AppRoot: s.config.WebRoot,
|
AppRoot: s.config.WebRoot,
|
||||||
EnableLogin: s.config.EnableLogin,
|
EnableLogin: s.config.EnableLogin,
|
||||||
|
RequireLogin: s.config.RequireLogin,
|
||||||
EnableSignup: s.config.EnableSignup,
|
EnableSignup: s.config.EnableSignup,
|
||||||
EnablePayments: s.config.StripeSecretKey != "",
|
EnablePayments: s.config.StripeSecretKey != "",
|
||||||
EnableCalls: s.config.TwilioAccount != "",
|
EnableCalls: s.config.TwilioAccount != "",
|
||||||
|
|||||||
@@ -258,9 +258,11 @@
|
|||||||
#
|
#
|
||||||
# - enable-signup allows users to sign up via the web app, or API
|
# - 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-login allows users to log in via the web app, or API
|
||||||
|
# - require-login redirects users to the login page if they are not logged in (disallows web app access without login)
|
||||||
# - enable-reservations allows users to reserve topics (if their tier allows it)
|
# - enable-reservations allows users to reserve topics (if their tier allows it)
|
||||||
#
|
#
|
||||||
# enable-signup: false
|
# enable-signup: false
|
||||||
|
# require-login: false
|
||||||
# enable-login: false
|
# enable-login: false
|
||||||
# enable-reservations: false
|
# enable-reservations: false
|
||||||
|
|
||||||
|
|||||||
@@ -449,6 +449,7 @@ type apiConfigResponse struct {
|
|||||||
BaseURL string `json:"base_url"`
|
BaseURL string `json:"base_url"`
|
||||||
AppRoot string `json:"app_root"`
|
AppRoot string `json:"app_root"`
|
||||||
EnableLogin bool `json:"enable_login"`
|
EnableLogin bool `json:"enable_login"`
|
||||||
|
RequireLogin bool `json:"require_login"`
|
||||||
EnableSignup bool `json:"enable_signup"`
|
EnableSignup bool `json:"enable_signup"`
|
||||||
EnablePayments bool `json:"enable_payments"`
|
EnablePayments bool `json:"enable_payments"`
|
||||||
EnableCalls bool `json:"enable_calls"`
|
EnableCalls bool `json:"enable_calls"`
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ var config = {
|
|||||||
base_url: window.location.origin, // Change to test against a different server
|
base_url: window.location.origin, // Change to test against a different server
|
||||||
app_root: "/",
|
app_root: "/",
|
||||||
enable_login: true,
|
enable_login: true,
|
||||||
|
require_login: true,
|
||||||
enable_signup: true,
|
enable_signup: true,
|
||||||
enable_payments: false,
|
enable_payments: false,
|
||||||
enable_reservations: true,
|
enable_reservations: true,
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export const maybeWithBearerAuth = (headers, token) => {
|
|||||||
|
|
||||||
export const withBasicAuth = (headers, username, password) => ({
|
export const withBasicAuth = (headers, username, password) => ({
|
||||||
...headers,
|
...headers,
|
||||||
Authorization: basicAuth(username, password)
|
Authorization: basicAuth(username, password),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const maybeWithAuth = (headers, user) => {
|
export const maybeWithAuth = (headers, user) => {
|
||||||
@@ -142,7 +142,7 @@ export const getKebabCaseLangStr = (language) => language.replace(/_/g, "-");
|
|||||||
export const formatShortDateTime = (timestamp, language) =>
|
export const formatShortDateTime = (timestamp, language) =>
|
||||||
new Intl.DateTimeFormat(getKebabCaseLangStr(language), {
|
new Intl.DateTimeFormat(getKebabCaseLangStr(language), {
|
||||||
dateStyle: "short",
|
dateStyle: "short",
|
||||||
timeStyle: "short"
|
timeStyle: "short",
|
||||||
}).format(new Date(timestamp * 1000));
|
}).format(new Date(timestamp * 1000));
|
||||||
|
|
||||||
export const formatShortDate = (timestamp, language) =>
|
export const formatShortDate = (timestamp, language) =>
|
||||||
@@ -181,32 +181,32 @@ export const openUrl = (url) => {
|
|||||||
export const sounds = {
|
export const sounds = {
|
||||||
ding: {
|
ding: {
|
||||||
file: ding,
|
file: ding,
|
||||||
label: "Ding"
|
label: "Ding",
|
||||||
},
|
},
|
||||||
juntos: {
|
juntos: {
|
||||||
file: juntos,
|
file: juntos,
|
||||||
label: "Juntos"
|
label: "Juntos",
|
||||||
},
|
},
|
||||||
pristine: {
|
pristine: {
|
||||||
file: pristine,
|
file: pristine,
|
||||||
label: "Pristine"
|
label: "Pristine",
|
||||||
},
|
},
|
||||||
dadum: {
|
dadum: {
|
||||||
file: dadum,
|
file: dadum,
|
||||||
label: "Dadum"
|
label: "Dadum",
|
||||||
},
|
},
|
||||||
pop: {
|
pop: {
|
||||||
file: pop,
|
file: pop,
|
||||||
label: "Pop"
|
label: "Pop",
|
||||||
},
|
},
|
||||||
"pop-swoosh": {
|
"pop-swoosh": {
|
||||||
file: popSwoosh,
|
file: popSwoosh,
|
||||||
label: "Pop swoosh"
|
label: "Pop swoosh",
|
||||||
},
|
},
|
||||||
beep: {
|
beep: {
|
||||||
file: beep,
|
file: beep,
|
||||||
label: "Beep"
|
label: "Beep",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const playSound = async (id) => {
|
export const playSound = async (id) => {
|
||||||
@@ -219,7 +219,7 @@ export const playSound = async (id) => {
|
|||||||
export async function* fetchLinesIterator(fileURL, headers) {
|
export async function* fetchLinesIterator(fileURL, headers) {
|
||||||
const utf8Decoder = new TextDecoder("utf-8");
|
const utf8Decoder = new TextDecoder("utf-8");
|
||||||
const response = await fetch(fileURL, {
|
const response = await fetch(fileURL, {
|
||||||
headers
|
headers,
|
||||||
});
|
});
|
||||||
const reader = response.body.getReader();
|
const reader = response.body.getReader();
|
||||||
let { value: chunk, done: readerDone } = await reader.read();
|
let { value: chunk, done: readerDone } = await reader.read();
|
||||||
@@ -228,7 +228,7 @@ export async function* fetchLinesIterator(fileURL, headers) {
|
|||||||
const re = /\n|\r|\r\n/gm;
|
const re = /\n|\r|\r\n/gm;
|
||||||
let startIndex = 0;
|
let startIndex = 0;
|
||||||
|
|
||||||
for (; ;) {
|
for (;;) {
|
||||||
const result = re.exec(chunk);
|
const result = re.exec(chunk);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
if (readerDone) {
|
if (readerDone) {
|
||||||
@@ -277,17 +277,17 @@ export const urlB64ToUint8Array = (base64String) => {
|
|||||||
export const copyToClipboard = (text) => {
|
export const copyToClipboard = (text) => {
|
||||||
if (navigator.clipboard && window.isSecureContext) {
|
if (navigator.clipboard && window.isSecureContext) {
|
||||||
return navigator.clipboard.writeText(text);
|
return navigator.clipboard.writeText(text);
|
||||||
} else {
|
|
||||||
const textarea = document.createElement("textarea");
|
|
||||||
textarea.value = text;
|
|
||||||
textarea.setAttribute("readonly", ""); // Avoid mobile keyboards from popping up
|
|
||||||
textarea.style.position = "fixed"; // Avoid scroll jump
|
|
||||||
textarea.style.left = "-9999px";
|
|
||||||
document.body.appendChild(textarea);
|
|
||||||
textarea.focus();
|
|
||||||
textarea.select();
|
|
||||||
document.execCommand("copy");
|
|
||||||
document.body.removeChild(textarea);
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
}
|
||||||
|
// Fallback to the older method if clipboard API is not supported (or on HTTP)
|
||||||
|
const textarea = document.createElement("textarea");
|
||||||
|
textarea.value = text;
|
||||||
|
textarea.setAttribute("readonly", ""); // Avoid mobile keyboards from popping up
|
||||||
|
textarea.style.position = "fixed"; // Avoid scroll jump
|
||||||
|
textarea.style.left = "-9999px";
|
||||||
|
document.body.appendChild(textarea);
|
||||||
|
textarea.focus();
|
||||||
|
textarea.select();
|
||||||
|
document.execCommand("copy");
|
||||||
|
document.body.removeChild(textarea);
|
||||||
|
return Promise.resolve();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import Account from "./Account";
|
|||||||
import initI18n from "../app/i18n"; // Translations!
|
import initI18n from "../app/i18n"; // Translations!
|
||||||
import prefs, { THEME } from "../app/Prefs";
|
import prefs, { THEME } from "../app/Prefs";
|
||||||
import RTLCacheProvider from "./RTLCacheProvider";
|
import RTLCacheProvider from "./RTLCacheProvider";
|
||||||
|
import session from "../app/Session";
|
||||||
|
|
||||||
initI18n();
|
initI18n();
|
||||||
|
|
||||||
@@ -45,7 +46,6 @@ const darkModeEnabled = (prefersDarkMode, themePreference) => {
|
|||||||
const App = () => {
|
const App = () => {
|
||||||
const { i18n } = useTranslation();
|
const { i18n } = useTranslation();
|
||||||
const languageDir = i18n.dir();
|
const languageDir = i18n.dir();
|
||||||
|
|
||||||
const [account, setAccount] = useState(null);
|
const [account, setAccount] = useState(null);
|
||||||
const accountMemo = useMemo(() => ({ account, setAccount }), [account, setAccount]);
|
const accountMemo = useMemo(() => ({ account, setAccount }), [account, setAccount]);
|
||||||
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
|
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
|
||||||
@@ -60,6 +60,12 @@ const App = () => {
|
|||||||
document.dir = languageDir;
|
document.dir = languageDir;
|
||||||
}, [i18n.language, languageDir]);
|
}, [i18n.language, languageDir]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!session.exists() && config.require_login && window.location.pathname !== routes.login) {
|
||||||
|
window.location.href = routes.login;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<Loader />}>
|
<Suspense fallback={<Loader />}>
|
||||||
<RTLCacheProvider>
|
<RTLCacheProvider>
|
||||||
|
|||||||
@@ -28,7 +28,13 @@ import { useRemark } from "react-remark";
|
|||||||
import styled from "@emotion/styled";
|
import styled from "@emotion/styled";
|
||||||
import {
|
import {
|
||||||
copyToClipboard,
|
copyToClipboard,
|
||||||
formatBytes, formatShortDateTime, maybeActionErrors, openUrl, shortUrl, topicShortUrl, unmatchedTags
|
formatBytes,
|
||||||
|
formatShortDateTime,
|
||||||
|
maybeActionErrors,
|
||||||
|
openUrl,
|
||||||
|
shortUrl,
|
||||||
|
topicShortUrl,
|
||||||
|
unmatchedTags,
|
||||||
} from "../app/utils";
|
} from "../app/utils";
|
||||||
import { formatMessage, formatTitle, isImage } from "../app/notificationUtils";
|
import { formatMessage, formatTitle, isImage } from "../app/notificationUtils";
|
||||||
import { LightboxBackdrop, Paragraph, VerticallyCenteredContainer } from "./styles";
|
import { LightboxBackdrop, Paragraph, VerticallyCenteredContainer } from "./styles";
|
||||||
|
|||||||
Reference in New Issue
Block a user