mirror of
https://github.com/binwiederhier/ntfy.git
synced 2024-12-27 04:03:14 +01:00
Redirect UI if unauthorized API response
This commit is contained in:
parent
1b39ba70cb
commit
3aac1b2715
11 changed files with 148 additions and 77 deletions
|
@ -42,7 +42,6 @@ import (
|
||||||
expire tokens
|
expire tokens
|
||||||
auto-refresh tokens from UI
|
auto-refresh tokens from UI
|
||||||
reserve topics
|
reserve topics
|
||||||
handle invalid session token
|
|
||||||
purge accounts that were not logged into in X
|
purge accounts that were not logged into in X
|
||||||
sync subscription display name
|
sync subscription display name
|
||||||
reset daily limits for users
|
reset daily limits for users
|
||||||
|
|
|
@ -126,7 +126,7 @@ class Api {
|
||||||
headers: maybeWithBasicAuth({}, user)
|
headers: maybeWithBasicAuth({}, user)
|
||||||
});
|
});
|
||||||
if (response.status === 401 || response.status === 403) {
|
if (response.status === 401 || response.status === 403) {
|
||||||
return false;
|
throw new UnauthorizedError();
|
||||||
} else if (response.status !== 200) {
|
} else if (response.status !== 200) {
|
||||||
throw new Error(`Unexpected server response ${response.status}`);
|
throw new Error(`Unexpected server response ${response.status}`);
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,9 @@ class Api {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
headers: maybeWithBearerAuth({}, token)
|
headers: maybeWithBearerAuth({}, token)
|
||||||
});
|
});
|
||||||
if (response.status !== 200) {
|
if (response.status === 401 || response.status === 403) {
|
||||||
|
throw new UnauthorizedError();
|
||||||
|
} else if (response.status !== 200) {
|
||||||
throw new Error(`Unexpected server response ${response.status}`);
|
throw new Error(`Unexpected server response ${response.status}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,7 +177,9 @@ class Api {
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
headers: maybeWithBearerAuth({}, token)
|
headers: maybeWithBearerAuth({}, token)
|
||||||
});
|
});
|
||||||
if (response.status !== 200) {
|
if (response.status === 401 || response.status === 403) {
|
||||||
|
throw new UnauthorizedError();
|
||||||
|
} else if (response.status !== 200) {
|
||||||
throw new Error(`Unexpected server response ${response.status}`);
|
throw new Error(`Unexpected server response ${response.status}`);
|
||||||
}
|
}
|
||||||
const account = await response.json();
|
const account = await response.json();
|
||||||
|
@ -190,7 +194,9 @@ class Api {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
headers: maybeWithBearerAuth({}, token)
|
headers: maybeWithBearerAuth({}, token)
|
||||||
});
|
});
|
||||||
if (response.status !== 200) {
|
if (response.status === 401 || response.status === 403) {
|
||||||
|
throw new UnauthorizedError();
|
||||||
|
} else if (response.status !== 200) {
|
||||||
throw new Error(`Unexpected server response ${response.status}`);
|
throw new Error(`Unexpected server response ${response.status}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,7 +211,9 @@ class Api {
|
||||||
password: password
|
password: password
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
if (response.status !== 200) {
|
if (response.status === 401 || response.status === 403) {
|
||||||
|
throw new UnauthorizedError();
|
||||||
|
} else if (response.status !== 200) {
|
||||||
throw new Error(`Unexpected server response ${response.status}`);
|
throw new Error(`Unexpected server response ${response.status}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,7 +227,9 @@ class Api {
|
||||||
headers: maybeWithBearerAuth({}, token),
|
headers: maybeWithBearerAuth({}, token),
|
||||||
body: body
|
body: body
|
||||||
});
|
});
|
||||||
if (response.status !== 200) {
|
if (response.status === 401 || response.status === 403) {
|
||||||
|
throw new UnauthorizedError();
|
||||||
|
} else if (response.status !== 200) {
|
||||||
throw new Error(`Unexpected server response ${response.status}`);
|
throw new Error(`Unexpected server response ${response.status}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -233,7 +243,9 @@ class Api {
|
||||||
headers: maybeWithBearerAuth({}, token),
|
headers: maybeWithBearerAuth({}, token),
|
||||||
body: body
|
body: body
|
||||||
});
|
});
|
||||||
if (response.status !== 200) {
|
if (response.status === 401 || response.status === 403) {
|
||||||
|
throw new UnauthorizedError();
|
||||||
|
} else if (response.status !== 200) {
|
||||||
throw new Error(`Unexpected server response ${response.status}`);
|
throw new Error(`Unexpected server response ${response.status}`);
|
||||||
}
|
}
|
||||||
const subscription = await response.json();
|
const subscription = await response.json();
|
||||||
|
@ -248,7 +260,9 @@ class Api {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
headers: maybeWithBearerAuth({}, token)
|
headers: maybeWithBearerAuth({}, token)
|
||||||
});
|
});
|
||||||
if (response.status !== 200) {
|
if (response.status === 401 || response.status === 403) {
|
||||||
|
throw new UnauthorizedError();
|
||||||
|
} else if (response.status !== 200) {
|
||||||
throw new Error(`Unexpected server response ${response.status}`);
|
throw new Error(`Unexpected server response ${response.status}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,13 +270,21 @@ class Api {
|
||||||
|
|
||||||
export class UsernameTakenError extends Error {
|
export class UsernameTakenError extends Error {
|
||||||
constructor(username) {
|
constructor(username) {
|
||||||
super();
|
super("Username taken");
|
||||||
this.username = username;
|
this.username = username;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AccountCreateLimitReachedError extends Error {
|
export class AccountCreateLimitReachedError extends Error {
|
||||||
// Nothing
|
constructor() {
|
||||||
|
super("Account creation limit reached");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UnauthorizedError extends Error {
|
||||||
|
constructor() {
|
||||||
|
super("Unauthorized");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const api = new Api();
|
const api = new Api();
|
||||||
|
|
|
@ -16,7 +16,7 @@ import DialogTitle from "@mui/material/DialogTitle";
|
||||||
import DialogContent from "@mui/material/DialogContent";
|
import DialogContent from "@mui/material/DialogContent";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import DialogActions from "@mui/material/DialogActions";
|
import DialogActions from "@mui/material/DialogActions";
|
||||||
import api from "../app/Api";
|
import api, {UnauthorizedError} from "../app/Api";
|
||||||
import routes from "./routes";
|
import routes from "./routes";
|
||||||
import IconButton from "@mui/material/IconButton";
|
import IconButton from "@mui/material/IconButton";
|
||||||
import {useNavigate, useOutletContext} from "react-router-dom";
|
import {useNavigate, useOutletContext} from "react-router-dom";
|
||||||
|
@ -152,6 +152,10 @@ const ChangePassword = () => {
|
||||||
console.debug(`[Account] Password changed`);
|
console.debug(`[Account] Password changed`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`[Account] Error changing password`, e);
|
console.log(`[Account] Error changing password`, e);
|
||||||
|
if ((e instanceof UnauthorizedError)) {
|
||||||
|
session.reset();
|
||||||
|
window.location.href = routes.login;
|
||||||
|
}
|
||||||
// TODO show error
|
// TODO show error
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -238,6 +242,10 @@ const DeleteAccount = () => {
|
||||||
window.location.href = routes.app;
|
window.location.href = routes.app;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`[Account] Error deleting account`, e);
|
console.log(`[Account] Error deleting account`, e);
|
||||||
|
if ((e instanceof UnauthorizedError)) {
|
||||||
|
session.reset();
|
||||||
|
window.location.href = routes.login;
|
||||||
|
}
|
||||||
// TODO show error
|
// TODO show error
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,7 +18,7 @@ import MenuList from '@mui/material/MenuList';
|
||||||
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
||||||
import NotificationsIcon from '@mui/icons-material/Notifications';
|
import NotificationsIcon from '@mui/icons-material/Notifications';
|
||||||
import NotificationsOffIcon from '@mui/icons-material/NotificationsOff';
|
import NotificationsOffIcon from '@mui/icons-material/NotificationsOff';
|
||||||
import api from "../app/Api";
|
import api, {UnauthorizedError} from "../app/Api";
|
||||||
import routes from "./routes";
|
import routes from "./routes";
|
||||||
import subscriptionManager from "../app/SubscriptionManager";
|
import subscriptionManager from "../app/SubscriptionManager";
|
||||||
import logo from "../img/ntfy.svg";
|
import logo from "../img/ntfy.svg";
|
||||||
|
@ -118,7 +118,15 @@ const SettingsIcons = (props) => {
|
||||||
handleClose(event);
|
handleClose(event);
|
||||||
await subscriptionManager.remove(props.subscription.id);
|
await subscriptionManager.remove(props.subscription.id);
|
||||||
if (session.exists() && props.subscription.remoteId) {
|
if (session.exists() && props.subscription.remoteId) {
|
||||||
await api.deleteAccountSubscription(config.baseUrl, session.token(), props.subscription.remoteId);
|
try {
|
||||||
|
await api.deleteAccountSubscription(config.baseUrl, session.token(), props.subscription.remoteId);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`[ActionBar] Error unsubscribing`, e);
|
||||||
|
if ((e instanceof UnauthorizedError)) {
|
||||||
|
session.reset();
|
||||||
|
window.location.href = routes.login;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const newSelected = await subscriptionManager.first(); // May be undefined
|
const newSelected = await subscriptionManager.first(); // May be undefined
|
||||||
if (newSelected) {
|
if (newSelected) {
|
||||||
|
|
|
@ -26,7 +26,7 @@ import {Backdrop, CircularProgress} from "@mui/material";
|
||||||
import Home from "./Home";
|
import Home from "./Home";
|
||||||
import Login from "./Login";
|
import Login from "./Login";
|
||||||
import i18n from "i18next";
|
import i18n from "i18next";
|
||||||
import api from "../app/Api";
|
import api, {UnauthorizedError} from "../app/Api";
|
||||||
import prefs from "../app/Prefs";
|
import prefs from "../app/Prefs";
|
||||||
import session from "../app/Session";
|
import session from "../app/Session";
|
||||||
import Pricing from "./Pricing";
|
import Pricing from "./Pricing";
|
||||||
|
@ -96,8 +96,12 @@ const Layout = () => {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const acc = await api.getAccount(config.baseUrl, session.token());
|
// TODO this should not live here
|
||||||
if (acc) {
|
try {
|
||||||
|
if (!session.token()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const acc = await api.getAccount(config.baseUrl, session.token());
|
||||||
setAccount(acc);
|
setAccount(acc);
|
||||||
if (acc.language) {
|
if (acc.language) {
|
||||||
await i18n.changeLanguage(acc.language);
|
await i18n.changeLanguage(acc.language);
|
||||||
|
@ -116,6 +120,12 @@ const Layout = () => {
|
||||||
if (acc.subscriptions) {
|
if (acc.subscriptions) {
|
||||||
await subscriptionManager.syncFromRemote(acc.subscriptions);
|
await subscriptionManager.syncFromRemote(acc.subscriptions);
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`[App] Error fetching account`, e);
|
||||||
|
if ((e instanceof UnauthorizedError)) {
|
||||||
|
session.reset();
|
||||||
|
window.location.href = routes.login;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
|
@ -4,7 +4,7 @@ import WarningAmberIcon from '@mui/icons-material/WarningAmber';
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import api from "../app/Api";
|
import api, {UnauthorizedError} from "../app/Api";
|
||||||
import routes from "./routes";
|
import routes from "./routes";
|
||||||
import session from "../app/Session";
|
import session from "../app/Session";
|
||||||
import {NavLink} from "react-router-dom";
|
import {NavLink} from "react-router-dom";
|
||||||
|
@ -22,17 +22,14 @@ const Login = () => {
|
||||||
const user = { username, password };
|
const user = { username, password };
|
||||||
try {
|
try {
|
||||||
const token = await api.login(config.baseUrl, user);
|
const token = await api.login(config.baseUrl, user);
|
||||||
if (token) {
|
console.log(`[Login] User auth for user ${user.username} successful, token is ${token}`);
|
||||||
console.log(`[Login] User auth for user ${user.username} successful, token is ${token}`);
|
session.store(user.username, token);
|
||||||
session.store(user.username, token);
|
window.location.href = routes.app;
|
||||||
window.location.href = routes.app;
|
|
||||||
} else {
|
|
||||||
console.log(`[Login] User auth for user ${user.username} failed, access denied`);
|
|
||||||
setError(t("Login failed: Invalid username or password"));
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`[Login] User auth for user ${user.username} failed`, e);
|
console.log(`[Login] User auth for user ${user.username} failed`, e);
|
||||||
if (e && e.message) {
|
if ((e instanceof UnauthorizedError)) {
|
||||||
|
setError(t("Login failed: Invalid username or password"));
|
||||||
|
} else if (e.message) {
|
||||||
setError(e.message);
|
setError(e.message);
|
||||||
} else {
|
} else {
|
||||||
setError(t("Unknown error. Check logs for details."))
|
setError(t("Unknown error. Check logs for details."))
|
||||||
|
|
|
@ -34,8 +34,9 @@ import DialogActions from "@mui/material/DialogActions";
|
||||||
import userManager from "../app/UserManager";
|
import userManager from "../app/UserManager";
|
||||||
import {playSound, shuffle, sounds, validTopic, validUrl} from "../app/utils";
|
import {playSound, shuffle, sounds, validTopic, validUrl} from "../app/utils";
|
||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import api from "../app/Api";
|
import api, {UnauthorizedError} from "../app/Api";
|
||||||
import session from "../app/Session";
|
import session from "../app/Session";
|
||||||
|
import routes from "./routes";
|
||||||
|
|
||||||
const Preferences = () => {
|
const Preferences = () => {
|
||||||
return (
|
return (
|
||||||
|
@ -72,13 +73,11 @@ const Sound = () => {
|
||||||
const sound = useLiveQuery(async () => prefs.sound());
|
const sound = useLiveQuery(async () => prefs.sound());
|
||||||
const handleChange = async (ev) => {
|
const handleChange = async (ev) => {
|
||||||
await prefs.setSound(ev.target.value);
|
await prefs.setSound(ev.target.value);
|
||||||
if (session.exists()) {
|
await maybeUpdateAccountSettings({
|
||||||
await api.updateAccountSettings(config.baseUrl, session.token(), {
|
notification: {
|
||||||
notification: {
|
sound: ev.target.value
|
||||||
sound: ev.target.value
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!sound) {
|
if (!sound) {
|
||||||
return null; // While loading
|
return null; // While loading
|
||||||
|
@ -112,13 +111,11 @@ const MinPriority = () => {
|
||||||
const minPriority = useLiveQuery(async () => prefs.minPriority());
|
const minPriority = useLiveQuery(async () => prefs.minPriority());
|
||||||
const handleChange = async (ev) => {
|
const handleChange = async (ev) => {
|
||||||
await prefs.setMinPriority(ev.target.value);
|
await prefs.setMinPriority(ev.target.value);
|
||||||
if (session.exists()) {
|
await maybeUpdateAccountSettings({
|
||||||
await api.updateAccountSettings(config.baseUrl, session.token(), {
|
notification: {
|
||||||
notification: {
|
min_priority: ev.target.value
|
||||||
min_priority: ev.target.value
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!minPriority) {
|
if (!minPriority) {
|
||||||
return null; // While loading
|
return null; // While loading
|
||||||
|
@ -162,13 +159,11 @@ const DeleteAfter = () => {
|
||||||
const deleteAfter = useLiveQuery(async () => prefs.deleteAfter());
|
const deleteAfter = useLiveQuery(async () => prefs.deleteAfter());
|
||||||
const handleChange = async (ev) => {
|
const handleChange = async (ev) => {
|
||||||
await prefs.setDeleteAfter(ev.target.value);
|
await prefs.setDeleteAfter(ev.target.value);
|
||||||
if (session.exists()) {
|
await maybeUpdateAccountSettings({
|
||||||
await api.updateAccountSettings(config.baseUrl, session.token(), {
|
notification: {
|
||||||
notification: {
|
delete_after: ev.target.value
|
||||||
delete_after: ev.target.value
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (deleteAfter === null || deleteAfter === undefined) { // !deleteAfter will not work with "0"
|
if (deleteAfter === null || deleteAfter === undefined) { // !deleteAfter will not work with "0"
|
||||||
return null; // While loading
|
return null; // While loading
|
||||||
|
@ -466,11 +461,9 @@ const Language = () => {
|
||||||
|
|
||||||
const handleChange = async (ev) => {
|
const handleChange = async (ev) => {
|
||||||
await i18n.changeLanguage(ev.target.value);
|
await i18n.changeLanguage(ev.target.value);
|
||||||
if (session.exists()) {
|
await maybeUpdateAccountSettings({
|
||||||
await api.updateAccountSettings(config.baseUrl, session.token(), {
|
language: ev.target.value
|
||||||
language: ev.target.value
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Remember: Flags are not languages. Don't put flags next to the language in the list.
|
// Remember: Flags are not languages. Don't put flags next to the language in the list.
|
||||||
|
@ -670,4 +663,19 @@ const AccessControlDialog = (props) => {
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const maybeUpdateAccountSettings = async (payload) => {
|
||||||
|
if (!session.exists()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await api.updateAccountSettings(config.baseUrl, session.token(), payload);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`[Preferences] Error updating account settings`, e);
|
||||||
|
if ((e instanceof UnauthorizedError)) {
|
||||||
|
session.reset();
|
||||||
|
window.location.href = routes.login;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export default Preferences;
|
export default Preferences;
|
||||||
|
|
|
@ -22,11 +22,12 @@ import {basicAuth, formatBytes, maybeWithBasicAuth, topicShortUrl, topicUrl, val
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import AttachmentIcon from "./AttachmentIcon";
|
import AttachmentIcon from "./AttachmentIcon";
|
||||||
import DialogFooter from "./DialogFooter";
|
import DialogFooter from "./DialogFooter";
|
||||||
import api from "../app/Api";
|
import api, {UnauthorizedError} from "../app/Api";
|
||||||
import userManager from "../app/UserManager";
|
import userManager from "../app/UserManager";
|
||||||
import EmojiPicker from "./EmojiPicker";
|
import EmojiPicker from "./EmojiPicker";
|
||||||
import {Trans, useTranslation} from "react-i18next";
|
import {Trans, useTranslation} from "react-i18next";
|
||||||
import session from "../app/Session";
|
import session from "../app/Session";
|
||||||
|
import routes from "./routes";
|
||||||
|
|
||||||
const PublishDialog = (props) => {
|
const PublishDialog = (props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@ -178,7 +179,12 @@ const PublishDialog = (props) => {
|
||||||
setAttachFileError("");
|
setAttachFileError("");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`[PublishDialog] Retrieving attachment limits failed`, e);
|
console.log(`[PublishDialog] Retrieving attachment limits failed`, e);
|
||||||
setAttachFileError(""); // Reset error (rely on server-side checking)
|
if ((e instanceof UnauthorizedError)) {
|
||||||
|
session.reset();
|
||||||
|
window.location.href = routes.login;
|
||||||
|
} else {
|
||||||
|
setAttachFileError(""); // Reset error (rely on server-side checking)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import * as React from 'react';
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import api, {AccountCreateLimitReachedError, UsernameTakenError} from "../app/Api";
|
import api, {AccountCreateLimitReachedError, UnauthorizedError, UsernameTakenError} from "../app/Api";
|
||||||
import routes from "./routes";
|
import routes from "./routes";
|
||||||
import session from "../app/Session";
|
import session from "../app/Session";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
|
@ -24,14 +24,9 @@ const Signup = () => {
|
||||||
try {
|
try {
|
||||||
await api.createAccount(config.baseUrl, user.username, user.password);
|
await api.createAccount(config.baseUrl, user.username, user.password);
|
||||||
const token = await api.login(config.baseUrl, user);
|
const token = await api.login(config.baseUrl, user);
|
||||||
if (token) {
|
console.log(`[Signup] User signup for user ${user.username} successful, token is ${token}`);
|
||||||
console.log(`[Signup] User signup for user ${user.username} successful, token is ${token}`);
|
session.store(user.username, token);
|
||||||
session.store(user.username, token);
|
window.location.href = routes.app;
|
||||||
window.location.href = routes.app;
|
|
||||||
} else {
|
|
||||||
console.log(`[Signup] Signup for user ${user.username} failed, access denied`);
|
|
||||||
setError(t("Login failed: Invalid username or password"));
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`[Signup] Signup for user ${user.username} failed`, e);
|
console.log(`[Signup] Signup for user ${user.username} failed`, e);
|
||||||
if ((e instanceof UsernameTakenError)) {
|
if ((e instanceof UsernameTakenError)) {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import DialogContentText from '@mui/material/DialogContentText';
|
||||||
import DialogTitle from '@mui/material/DialogTitle';
|
import DialogTitle from '@mui/material/DialogTitle';
|
||||||
import {Autocomplete, Checkbox, FormControlLabel, useMediaQuery} from "@mui/material";
|
import {Autocomplete, Checkbox, FormControlLabel, useMediaQuery} from "@mui/material";
|
||||||
import theme from "./theme";
|
import theme from "./theme";
|
||||||
import api from "../app/Api";
|
import api, {UnauthorizedError} from "../app/Api";
|
||||||
import {randomAlphanumericString, topicUrl, validTopic, validUrl} from "../app/utils";
|
import {randomAlphanumericString, topicUrl, validTopic, validUrl} from "../app/utils";
|
||||||
import userManager from "../app/UserManager";
|
import userManager from "../app/UserManager";
|
||||||
import subscriptionManager from "../app/SubscriptionManager";
|
import subscriptionManager from "../app/SubscriptionManager";
|
||||||
|
@ -16,6 +16,7 @@ import poller from "../app/Poller";
|
||||||
import DialogFooter from "./DialogFooter";
|
import DialogFooter from "./DialogFooter";
|
||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import session from "../app/Session";
|
import session from "../app/Session";
|
||||||
|
import routes from "./routes";
|
||||||
|
|
||||||
const publicBaseUrl = "https://ntfy.sh";
|
const publicBaseUrl = "https://ntfy.sh";
|
||||||
|
|
||||||
|
@ -25,14 +26,23 @@ const SubscribeDialog = (props) => {
|
||||||
const [showLoginPage, setShowLoginPage] = useState(false);
|
const [showLoginPage, setShowLoginPage] = useState(false);
|
||||||
const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
|
const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
|
||||||
const handleSuccess = async () => {
|
const handleSuccess = async () => {
|
||||||
|
console.log(`[SubscribeDialog] Subscribing to topic ${topic}`);
|
||||||
const actualBaseUrl = (baseUrl) ? baseUrl : config.baseUrl;
|
const actualBaseUrl = (baseUrl) ? baseUrl : config.baseUrl;
|
||||||
const subscription = await subscriptionManager.add(actualBaseUrl, topic);
|
const subscription = await subscriptionManager.add(actualBaseUrl, topic);
|
||||||
if (session.exists()) {
|
if (session.exists()) {
|
||||||
const remoteSubscription = await api.addAccountSubscription(config.baseUrl, session.token(), {
|
try {
|
||||||
base_url: actualBaseUrl,
|
const remoteSubscription = await api.addAccountSubscription(config.baseUrl, session.token(), {
|
||||||
topic: topic
|
base_url: actualBaseUrl,
|
||||||
});
|
topic: topic
|
||||||
await subscriptionManager.setRemoteId(subscription.id, remoteSubscription.id);
|
});
|
||||||
|
await subscriptionManager.setRemoteId(subscription.id, remoteSubscription.id);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`[SubscribeDialog] Subscribing to topic ${topic} failed`, e);
|
||||||
|
if ((e instanceof UnauthorizedError)) {
|
||||||
|
session.reset();
|
||||||
|
window.location.href = routes.login;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
poller.pollInBackground(subscription); // Dangle!
|
poller.pollInBackground(subscription); // Dangle!
|
||||||
props.onSuccess(subscription);
|
props.onSuccess(subscription);
|
||||||
|
|
|
@ -8,7 +8,7 @@ import connectionManager from "../app/ConnectionManager";
|
||||||
import poller from "../app/Poller";
|
import poller from "../app/Poller";
|
||||||
import pruner from "../app/Pruner";
|
import pruner from "../app/Pruner";
|
||||||
import session from "../app/Session";
|
import session from "../app/Session";
|
||||||
import api from "../app/Api";
|
import api, {UnauthorizedError} from "../app/Api";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wire connectionManager and subscriptionManager so that subscriptions are updated when the connection
|
* Wire connectionManager and subscriptionManager so that subscriptions are updated when the connection
|
||||||
|
@ -64,11 +64,19 @@ export const useAutoSubscribe = (subscriptions, selected) => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const subscription = await subscriptionManager.add(baseUrl, params.topic);
|
const subscription = await subscriptionManager.add(baseUrl, params.topic);
|
||||||
if (session.exists()) {
|
if (session.exists()) {
|
||||||
const remoteSubscription = await api.addAccountSubscription(config.baseUrl, session.token(), {
|
try {
|
||||||
base_url: baseUrl,
|
const remoteSubscription = await api.addAccountSubscription(config.baseUrl, session.token(), {
|
||||||
topic: params.topic
|
base_url: baseUrl,
|
||||||
});
|
topic: params.topic
|
||||||
await subscriptionManager.setRemoteId(subscription.id, remoteSubscription.id);
|
});
|
||||||
|
await subscriptionManager.setRemoteId(subscription.id, remoteSubscription.id);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`[App] Auto-subscribing failed`, e);
|
||||||
|
if ((e instanceof UnauthorizedError)) {
|
||||||
|
session.reset();
|
||||||
|
window.location.href = routes.login;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
poller.pollInBackground(subscription); // Dangle!
|
poller.pollInBackground(subscription); // Dangle!
|
||||||
})();
|
})();
|
||||||
|
|
Loading…
Reference in a new issue