Split out AccountApi
This commit is contained in:
parent
d4c7ad4beb
commit
276301dc87
11 changed files with 251 additions and 222 deletions
205
web/src/app/AccountApi.js
Normal file
205
web/src/app/AccountApi.js
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
import {
|
||||||
|
accountPasswordUrl,
|
||||||
|
accountSettingsUrl,
|
||||||
|
accountSubscriptionSingleUrl,
|
||||||
|
accountSubscriptionUrl,
|
||||||
|
accountTokenUrl,
|
||||||
|
accountUrl,
|
||||||
|
fetchLinesIterator,
|
||||||
|
maybeWithBasicAuth,
|
||||||
|
maybeWithBearerAuth,
|
||||||
|
topicShortUrl,
|
||||||
|
topicUrl,
|
||||||
|
topicUrlAuth,
|
||||||
|
topicUrlJsonPoll,
|
||||||
|
topicUrlJsonPollWithSince
|
||||||
|
} from "./utils";
|
||||||
|
import userManager from "./UserManager";
|
||||||
|
import session from "./Session";
|
||||||
|
|
||||||
|
class AccountApi {
|
||||||
|
async login(user) {
|
||||||
|
const url = accountTokenUrl(config.baseUrl);
|
||||||
|
console.log(`[Api] Checking auth for ${url}`);
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: "POST",
|
||||||
|
headers: maybeWithBasicAuth({}, user)
|
||||||
|
});
|
||||||
|
if (response.status === 401 || response.status === 403) {
|
||||||
|
throw new UnauthorizedError();
|
||||||
|
} else if (response.status !== 200) {
|
||||||
|
throw new Error(`Unexpected server response ${response.status}`);
|
||||||
|
}
|
||||||
|
const json = await response.json();
|
||||||
|
if (!json.token) {
|
||||||
|
throw new Error(`Unexpected server response: Cannot find token`);
|
||||||
|
}
|
||||||
|
return json.token;
|
||||||
|
}
|
||||||
|
|
||||||
|
async logout(token) {
|
||||||
|
const url = accountTokenUrl(config.baseUrl);
|
||||||
|
console.log(`[Api] Logging out from ${url} using token ${token}`);
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: maybeWithBearerAuth({}, token)
|
||||||
|
});
|
||||||
|
if (response.status === 401 || response.status === 403) {
|
||||||
|
throw new UnauthorizedError();
|
||||||
|
} else if (response.status !== 200) {
|
||||||
|
throw new Error(`Unexpected server response ${response.status}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(username, password) {
|
||||||
|
const url = accountUrl(config.baseUrl);
|
||||||
|
const body = JSON.stringify({
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
});
|
||||||
|
console.log(`[Api] Creating user account ${url}`);
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: "POST",
|
||||||
|
body: body
|
||||||
|
});
|
||||||
|
if (response.status === 409) {
|
||||||
|
throw new UsernameTakenError(username);
|
||||||
|
} else if (response.status === 429) {
|
||||||
|
throw new AccountCreateLimitReachedError();
|
||||||
|
} else if (response.status !== 200) {
|
||||||
|
throw new Error(`Unexpected server response ${response.status}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async get() {
|
||||||
|
const url = accountUrl(config.baseUrl);
|
||||||
|
console.log(`[Api] Fetching user account ${url}`);
|
||||||
|
const response = await fetch(url, {
|
||||||
|
headers: maybeWithBearerAuth({}, session.token())
|
||||||
|
});
|
||||||
|
if (response.status === 401 || response.status === 403) {
|
||||||
|
throw new UnauthorizedError();
|
||||||
|
} else if (response.status !== 200) {
|
||||||
|
throw new Error(`Unexpected server response ${response.status}`);
|
||||||
|
}
|
||||||
|
const account = await response.json();
|
||||||
|
console.log(`[Api] Account`, account);
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete() {
|
||||||
|
const url = accountUrl(config.baseUrl);
|
||||||
|
console.log(`[Api] Deleting user account ${url}`);
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: maybeWithBearerAuth({}, session.token())
|
||||||
|
});
|
||||||
|
if (response.status === 401 || response.status === 403) {
|
||||||
|
throw new UnauthorizedError();
|
||||||
|
} else if (response.status !== 200) {
|
||||||
|
throw new Error(`Unexpected server response ${response.status}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async changePassword(newPassword) {
|
||||||
|
const url = accountPasswordUrl(config.baseUrl);
|
||||||
|
console.log(`[Api] Changing account password ${url}`);
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: "POST",
|
||||||
|
headers: maybeWithBearerAuth({}, session.token()),
|
||||||
|
body: JSON.stringify({
|
||||||
|
password: newPassword
|
||||||
|
})
|
||||||
|
});
|
||||||
|
if (response.status === 401 || response.status === 403) {
|
||||||
|
throw new UnauthorizedError();
|
||||||
|
} else if (response.status !== 200) {
|
||||||
|
throw new Error(`Unexpected server response ${response.status}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async extendToken() {
|
||||||
|
const url = accountTokenUrl(config.baseUrl);
|
||||||
|
console.log(`[Api] Extending user access token ${url}`);
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: "PATCH",
|
||||||
|
headers: maybeWithBearerAuth({}, session.token())
|
||||||
|
});
|
||||||
|
if (response.status === 401 || response.status === 403) {
|
||||||
|
throw new UnauthorizedError();
|
||||||
|
} else if (response.status !== 200) {
|
||||||
|
throw new Error(`Unexpected server response ${response.status}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateSettings(payload) {
|
||||||
|
const url = accountSettingsUrl(config.baseUrl);
|
||||||
|
const body = JSON.stringify(payload);
|
||||||
|
console.log(`[Api] Updating user account ${url}: ${body}`);
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: "PATCH",
|
||||||
|
headers: maybeWithBearerAuth({}, session.token()),
|
||||||
|
body: body
|
||||||
|
});
|
||||||
|
if (response.status === 401 || response.status === 403) {
|
||||||
|
throw new UnauthorizedError();
|
||||||
|
} else if (response.status !== 200) {
|
||||||
|
throw new Error(`Unexpected server response ${response.status}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async addSubscription(payload) {
|
||||||
|
const url = accountSubscriptionUrl(config.baseUrl);
|
||||||
|
const body = JSON.stringify(payload);
|
||||||
|
console.log(`[Api] Adding user subscription ${url}: ${body}`);
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: "POST",
|
||||||
|
headers: maybeWithBearerAuth({}, session.token()),
|
||||||
|
body: body
|
||||||
|
});
|
||||||
|
if (response.status === 401 || response.status === 403) {
|
||||||
|
throw new UnauthorizedError();
|
||||||
|
} else if (response.status !== 200) {
|
||||||
|
throw new Error(`Unexpected server response ${response.status}`);
|
||||||
|
}
|
||||||
|
const subscription = await response.json();
|
||||||
|
console.log(`[Api] Subscription`, subscription);
|
||||||
|
return subscription;
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteSubscription(remoteId) {
|
||||||
|
const url = accountSubscriptionSingleUrl(config.baseUrl, remoteId);
|
||||||
|
console.log(`[Api] Removing user subscription ${url}`);
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: maybeWithBearerAuth({}, session.token())
|
||||||
|
});
|
||||||
|
if (response.status === 401 || response.status === 403) {
|
||||||
|
throw new UnauthorizedError();
|
||||||
|
} else if (response.status !== 200) {
|
||||||
|
throw new Error(`Unexpected server response ${response.status}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UsernameTakenError extends Error {
|
||||||
|
constructor(username) {
|
||||||
|
super("Username taken");
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AccountCreateLimitReachedError extends Error {
|
||||||
|
constructor() {
|
||||||
|
super("Account creation limit reached");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UnauthorizedError extends Error {
|
||||||
|
constructor() {
|
||||||
|
super("Unauthorized");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const accountApi = new AccountApi();
|
||||||
|
export default accountApi;
|
|
@ -122,188 +122,6 @@ class Api {
|
||||||
}
|
}
|
||||||
throw new Error(`Unexpected server response ${response.status}`);
|
throw new Error(`Unexpected server response ${response.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async login(baseUrl, user) {
|
|
||||||
const url = accountTokenUrl(baseUrl);
|
|
||||||
console.log(`[Api] Checking auth for ${url}`);
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: "POST",
|
|
||||||
headers: maybeWithBasicAuth({}, user)
|
|
||||||
});
|
|
||||||
if (response.status === 401 || response.status === 403) {
|
|
||||||
throw new UnauthorizedError();
|
|
||||||
} else if (response.status !== 200) {
|
|
||||||
throw new Error(`Unexpected server response ${response.status}`);
|
|
||||||
}
|
|
||||||
const json = await response.json();
|
|
||||||
if (!json.token) {
|
|
||||||
throw new Error(`Unexpected server response: Cannot find token`);
|
|
||||||
}
|
|
||||||
return json.token;
|
|
||||||
}
|
|
||||||
|
|
||||||
async logout(baseUrl, token) {
|
|
||||||
const url = accountTokenUrl(baseUrl);
|
|
||||||
console.log(`[Api] Logging out from ${url} using token ${token}`);
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: "DELETE",
|
|
||||||
headers: maybeWithBearerAuth({}, token)
|
|
||||||
});
|
|
||||||
if (response.status === 401 || response.status === 403) {
|
|
||||||
throw new UnauthorizedError();
|
|
||||||
} else if (response.status !== 200) {
|
|
||||||
throw new Error(`Unexpected server response ${response.status}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async createAccount(baseUrl, username, password) {
|
|
||||||
const url = accountUrl(baseUrl);
|
|
||||||
const body = JSON.stringify({
|
|
||||||
username: username,
|
|
||||||
password: password
|
|
||||||
});
|
|
||||||
console.log(`[Api] Creating user account ${url}`);
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: "POST",
|
|
||||||
body: body
|
|
||||||
});
|
|
||||||
if (response.status === 409) {
|
|
||||||
throw new UsernameTakenError(username);
|
|
||||||
} else if (response.status === 429) {
|
|
||||||
throw new AccountCreateLimitReachedError();
|
|
||||||
} else if (response.status !== 200) {
|
|
||||||
throw new Error(`Unexpected server response ${response.status}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getAccount(baseUrl, token) {
|
|
||||||
const url = accountUrl(baseUrl);
|
|
||||||
console.log(`[Api] Fetching user account ${url}`);
|
|
||||||
const response = await fetch(url, {
|
|
||||||
headers: maybeWithBearerAuth({}, token)
|
|
||||||
});
|
|
||||||
if (response.status === 401 || response.status === 403) {
|
|
||||||
throw new UnauthorizedError();
|
|
||||||
} else if (response.status !== 200) {
|
|
||||||
throw new Error(`Unexpected server response ${response.status}`);
|
|
||||||
}
|
|
||||||
const account = await response.json();
|
|
||||||
console.log(`[Api] Account`, account);
|
|
||||||
return account;
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteAccount(baseUrl, token) {
|
|
||||||
const url = accountUrl(baseUrl);
|
|
||||||
console.log(`[Api] Deleting user account ${url}`);
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: "DELETE",
|
|
||||||
headers: maybeWithBearerAuth({}, token)
|
|
||||||
});
|
|
||||||
if (response.status === 401 || response.status === 403) {
|
|
||||||
throw new UnauthorizedError();
|
|
||||||
} else if (response.status !== 200) {
|
|
||||||
throw new Error(`Unexpected server response ${response.status}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async changePassword(baseUrl, token, password) {
|
|
||||||
const url = accountPasswordUrl(baseUrl);
|
|
||||||
console.log(`[Api] Changing account password ${url}`);
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: "POST",
|
|
||||||
headers: maybeWithBearerAuth({}, token),
|
|
||||||
body: JSON.stringify({
|
|
||||||
password: password
|
|
||||||
})
|
|
||||||
});
|
|
||||||
if (response.status === 401 || response.status === 403) {
|
|
||||||
throw new UnauthorizedError();
|
|
||||||
} else if (response.status !== 200) {
|
|
||||||
throw new Error(`Unexpected server response ${response.status}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async extendToken(baseUrl, token) {
|
|
||||||
const url = accountTokenUrl(baseUrl);
|
|
||||||
console.log(`[Api] Extending user access token ${url}`);
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: "PATCH",
|
|
||||||
headers: maybeWithBearerAuth({}, token)
|
|
||||||
});
|
|
||||||
if (response.status === 401 || response.status === 403) {
|
|
||||||
throw new UnauthorizedError();
|
|
||||||
} else if (response.status !== 200) {
|
|
||||||
throw new Error(`Unexpected server response ${response.status}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateAccountSettings(baseUrl, token, payload) {
|
|
||||||
const url = accountSettingsUrl(baseUrl);
|
|
||||||
const body = JSON.stringify(payload);
|
|
||||||
console.log(`[Api] Updating user account ${url}: ${body}`);
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: "PATCH",
|
|
||||||
headers: maybeWithBearerAuth({}, token),
|
|
||||||
body: body
|
|
||||||
});
|
|
||||||
if (response.status === 401 || response.status === 403) {
|
|
||||||
throw new UnauthorizedError();
|
|
||||||
} else if (response.status !== 200) {
|
|
||||||
throw new Error(`Unexpected server response ${response.status}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async addAccountSubscription(baseUrl, token, payload) {
|
|
||||||
const url = accountSubscriptionUrl(baseUrl);
|
|
||||||
const body = JSON.stringify(payload);
|
|
||||||
console.log(`[Api] Adding user subscription ${url}: ${body}`);
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: "POST",
|
|
||||||
headers: maybeWithBearerAuth({}, token),
|
|
||||||
body: body
|
|
||||||
});
|
|
||||||
if (response.status === 401 || response.status === 403) {
|
|
||||||
throw new UnauthorizedError();
|
|
||||||
} else if (response.status !== 200) {
|
|
||||||
throw new Error(`Unexpected server response ${response.status}`);
|
|
||||||
}
|
|
||||||
const subscription = await response.json();
|
|
||||||
console.log(`[Api] Subscription`, subscription);
|
|
||||||
return subscription;
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteAccountSubscription(baseUrl, token, remoteId) {
|
|
||||||
const url = accountSubscriptionSingleUrl(baseUrl, remoteId);
|
|
||||||
console.log(`[Api] Removing user subscription ${url}`);
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: "DELETE",
|
|
||||||
headers: maybeWithBearerAuth({}, token)
|
|
||||||
});
|
|
||||||
if (response.status === 401 || response.status === 403) {
|
|
||||||
throw new UnauthorizedError();
|
|
||||||
} else if (response.status !== 200) {
|
|
||||||
throw new Error(`Unexpected server response ${response.status}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class UsernameTakenError extends Error {
|
|
||||||
constructor(username) {
|
|
||||||
super("Username taken");
|
|
||||||
this.username = username;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AccountCreateLimitReachedError extends Error {
|
|
||||||
constructor() {
|
|
||||||
super("Account creation limit reached");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class UnauthorizedError extends Error {
|
|
||||||
constructor() {
|
|
||||||
super("Unauthorized");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const api = new Api();
|
const api = new Api();
|
||||||
|
|
|
@ -16,11 +16,11 @@ 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, {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 {useOutletContext} from "react-router-dom";
|
||||||
import {formatBytes} from "../app/utils";
|
import {formatBytes} from "../app/utils";
|
||||||
|
import accountApi, {UnauthorizedError} from "../app/AccountApi";
|
||||||
|
|
||||||
const Account = () => {
|
const Account = () => {
|
||||||
if (!session.exists()) {
|
if (!session.exists()) {
|
||||||
|
@ -147,7 +147,7 @@ const ChangePassword = () => {
|
||||||
};
|
};
|
||||||
const handleDialogSubmit = async (newPassword) => {
|
const handleDialogSubmit = async (newPassword) => {
|
||||||
try {
|
try {
|
||||||
await api.changePassword(config.baseUrl, session.token(), newPassword);
|
await accountApi.changePassword(newPassword);
|
||||||
setDialogOpen(false);
|
setDialogOpen(false);
|
||||||
console.debug(`[Account] Password changed`);
|
console.debug(`[Account] Password changed`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -234,7 +234,7 @@ const DeleteAccount = () => {
|
||||||
};
|
};
|
||||||
const handleDialogSubmit = async (newPassword) => {
|
const handleDialogSubmit = async (newPassword) => {
|
||||||
try {
|
try {
|
||||||
await api.deleteAccount(config.baseUrl, session.token());
|
await accountApi.delete();
|
||||||
setDialogOpen(false);
|
setDialogOpen(false);
|
||||||
console.debug(`[Account] Account deleted`);
|
console.debug(`[Account] Account deleted`);
|
||||||
// TODO delete local storage
|
// TODO delete local storage
|
||||||
|
|
|
@ -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, {UnauthorizedError} from "../app/Api";
|
import api 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";
|
||||||
|
@ -31,6 +31,7 @@ import Button from "@mui/material/Button";
|
||||||
import Divider from "@mui/material/Divider";
|
import Divider from "@mui/material/Divider";
|
||||||
import {Logout, Person, Settings} from "@mui/icons-material";
|
import {Logout, Person, Settings} from "@mui/icons-material";
|
||||||
import ListItemIcon from "@mui/material/ListItemIcon";
|
import ListItemIcon from "@mui/material/ListItemIcon";
|
||||||
|
import accountApi, {UnauthorizedError} from "../app/AccountApi";
|
||||||
|
|
||||||
const ActionBar = (props) => {
|
const ActionBar = (props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@ -119,7 +120,7 @@ const SettingsIcons = (props) => {
|
||||||
await subscriptionManager.remove(props.subscription.id);
|
await subscriptionManager.remove(props.subscription.id);
|
||||||
if (session.exists() && props.subscription.remoteId) {
|
if (session.exists() && props.subscription.remoteId) {
|
||||||
try {
|
try {
|
||||||
await api.deleteAccountSubscription(config.baseUrl, session.token(), props.subscription.remoteId);
|
await accountApi.deleteSubscription(props.subscription.remoteId);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`[ActionBar] Error unsubscribing`, e);
|
console.log(`[ActionBar] Error unsubscribing`, e);
|
||||||
if ((e instanceof UnauthorizedError)) {
|
if ((e instanceof UnauthorizedError)) {
|
||||||
|
@ -268,9 +269,12 @@ const ProfileIcon = (props) => {
|
||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
};
|
};
|
||||||
const handleLogout = async () => {
|
const handleLogout = async () => {
|
||||||
await api.logout(config.baseUrl, session.token());
|
try {
|
||||||
|
await accountApi.logout();
|
||||||
|
} finally {
|
||||||
session.reset();
|
session.reset();
|
||||||
window.location.href = routes.app;
|
window.location.href = routes.app;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -26,13 +26,13 @@ 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, {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";
|
||||||
import Signup from "./Signup";
|
import Signup from "./Signup";
|
||||||
import Account from "./Account";
|
import Account from "./Account";
|
||||||
import ResetPassword from "./ResetPassword";
|
import ResetPassword from "./ResetPassword";
|
||||||
|
import accountApi, {UnauthorizedError} from "../app/AccountApi";
|
||||||
|
|
||||||
// TODO races when two tabs are open
|
// TODO races when two tabs are open
|
||||||
// TODO investigate service workers
|
// TODO investigate service workers
|
||||||
|
@ -101,24 +101,24 @@ const Layout = () => {
|
||||||
if (!session.token()) {
|
if (!session.token()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const acc = await api.getAccount(config.baseUrl, session.token());
|
const remoteAccount = await accountApi.get();
|
||||||
setAccount(acc);
|
setAccount(remoteAccount);
|
||||||
if (acc.language) {
|
if (remoteAccount.language) {
|
||||||
await i18n.changeLanguage(acc.language);
|
await i18n.changeLanguage(remoteAccount.language);
|
||||||
}
|
}
|
||||||
if (acc.notification) {
|
if (remoteAccount.notification) {
|
||||||
if (acc.notification.sound) {
|
if (remoteAccount.notification.sound) {
|
||||||
await prefs.setSound(acc.notification.sound);
|
await prefs.setSound(remoteAccount.notification.sound);
|
||||||
}
|
}
|
||||||
if (acc.notification.delete_after) {
|
if (remoteAccount.notification.delete_after) {
|
||||||
await prefs.setDeleteAfter(acc.notification.delete_after);
|
await prefs.setDeleteAfter(remoteAccount.notification.delete_after);
|
||||||
}
|
}
|
||||||
if (acc.notification.min_priority) {
|
if (remoteAccount.notification.min_priority) {
|
||||||
await prefs.setMinPriority(acc.notification.min_priority);
|
await prefs.setMinPriority(remoteAccount.notification.min_priority);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (acc.subscriptions) {
|
if (remoteAccount.subscriptions) {
|
||||||
await subscriptionManager.syncFromRemote(acc.subscriptions);
|
await subscriptionManager.syncFromRemote(remoteAccount.subscriptions);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`[App] Error fetching account`, e);
|
console.log(`[App] Error fetching account`, e);
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import {useState} from 'react';
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import WarningAmberIcon from '@mui/icons-material/WarningAmber';
|
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, {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";
|
||||||
import AvatarBox from "./AvatarBox";
|
import AvatarBox from "./AvatarBox";
|
||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import {useState} from "react";
|
import accountApi, {UnauthorizedError} from "../app/AccountApi";
|
||||||
|
|
||||||
const Login = () => {
|
const Login = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@ -21,7 +21,7 @@ const Login = () => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const user = { username, password };
|
const user = { username, password };
|
||||||
try {
|
try {
|
||||||
const token = await api.login(config.baseUrl, user);
|
const token = await accountApi.login(user);
|
||||||
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;
|
||||||
|
|
|
@ -34,9 +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, {UnauthorizedError} from "../app/Api";
|
|
||||||
import session from "../app/Session";
|
import session from "../app/Session";
|
||||||
import routes from "./routes";
|
import routes from "./routes";
|
||||||
|
import accountApi, {UnauthorizedError} from "../app/AccountApi";
|
||||||
|
|
||||||
const Preferences = () => {
|
const Preferences = () => {
|
||||||
return (
|
return (
|
||||||
|
@ -668,7 +668,7 @@ const maybeUpdateAccountSettings = async (payload) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await api.updateAccountSettings(config.baseUrl, session.token(), payload);
|
await accountApi.updateSettings(payload);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`[Preferences] Error updating account settings`, e);
|
console.log(`[Preferences] Error updating account settings`, e);
|
||||||
if ((e instanceof UnauthorizedError)) {
|
if ((e instanceof UnauthorizedError)) {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {useEffect, useRef, useState} from 'react';
|
import {useEffect, useRef, useState} from 'react';
|
||||||
import {NotificationItem} from "./Notifications";
|
|
||||||
import theme from "./theme";
|
import theme from "./theme";
|
||||||
import {Checkbox, Chip, FormControl, FormControlLabel, InputLabel, Link, Select, useMediaQuery} from "@mui/material";
|
import {Checkbox, Chip, FormControl, FormControlLabel, InputLabel, Link, Select, useMediaQuery} from "@mui/material";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
|
@ -18,16 +17,17 @@ import IconButton from "@mui/material/IconButton";
|
||||||
import InsertEmoticonIcon from '@mui/icons-material/InsertEmoticon';
|
import InsertEmoticonIcon from '@mui/icons-material/InsertEmoticon';
|
||||||
import {Close} from "@mui/icons-material";
|
import {Close} from "@mui/icons-material";
|
||||||
import MenuItem from "@mui/material/MenuItem";
|
import MenuItem from "@mui/material/MenuItem";
|
||||||
import {basicAuth, formatBytes, maybeWithBasicAuth, topicShortUrl, topicUrl, validTopic, validUrl} from "../app/utils";
|
import {formatBytes, maybeWithBasicAuth, topicShortUrl, topicUrl, validTopic, validUrl} from "../app/utils";
|
||||||
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, {UnauthorizedError} from "../app/Api";
|
import api 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";
|
import routes from "./routes";
|
||||||
|
import accountApi, {UnauthorizedError} from "../app/AccountApi";
|
||||||
|
|
||||||
const PublishDialog = (props) => {
|
const PublishDialog = (props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@ -161,7 +161,7 @@ const PublishDialog = (props) => {
|
||||||
|
|
||||||
const checkAttachmentLimits = async (file) => {
|
const checkAttachmentLimits = async (file) => {
|
||||||
try {
|
try {
|
||||||
const account = await api.getAccount(baseUrl, session.token());
|
const account = await accountApi.get();
|
||||||
const fileSizeLimit = account.limits.attachment_file_size ?? 0;
|
const fileSizeLimit = account.limits.attachment_file_size ?? 0;
|
||||||
const remainingBytes = account.stats.attachment_total_size_remaining;
|
const remainingBytes = account.stats.attachment_total_size_remaining;
|
||||||
const fileSizeLimitReached = fileSizeLimit > 0 && file.size > fileSizeLimit;
|
const fileSizeLimitReached = fileSizeLimit > 0 && file.size > fileSizeLimit;
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import {useState} 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, 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";
|
||||||
import {NavLink} from "react-router-dom";
|
import {NavLink} from "react-router-dom";
|
||||||
import AvatarBox from "./AvatarBox";
|
import AvatarBox from "./AvatarBox";
|
||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import {useState} from "react";
|
|
||||||
import WarningAmberIcon from "@mui/icons-material/WarningAmber";
|
import WarningAmberIcon from "@mui/icons-material/WarningAmber";
|
||||||
|
import accountApi, {AccountCreateLimitReachedError, UsernameTakenError} from "../app/AccountApi";
|
||||||
|
|
||||||
const Signup = () => {
|
const Signup = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@ -22,8 +22,8 @@ const Signup = () => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const user = { username, password };
|
const user = { username, password };
|
||||||
try {
|
try {
|
||||||
await api.createAccount(config.baseUrl, user.username, user.password);
|
await accountApi.create(user.username, user.password);
|
||||||
const token = await api.login(config.baseUrl, user);
|
const token = await accountApi.login(user);
|
||||||
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;
|
||||||
|
|
|
@ -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, {UnauthorizedError} from "../app/Api";
|
import api 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";
|
||||||
|
@ -17,6 +17,7 @@ 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";
|
import routes from "./routes";
|
||||||
|
import accountApi, {UnauthorizedError} from "../app/AccountApi";
|
||||||
|
|
||||||
const publicBaseUrl = "https://ntfy.sh";
|
const publicBaseUrl = "https://ntfy.sh";
|
||||||
|
|
||||||
|
@ -31,7 +32,7 @@ const SubscribeDialog = (props) => {
|
||||||
const subscription = await subscriptionManager.add(actualBaseUrl, topic);
|
const subscription = await subscriptionManager.add(actualBaseUrl, topic);
|
||||||
if (session.exists()) {
|
if (session.exists()) {
|
||||||
try {
|
try {
|
||||||
const remoteSubscription = await api.addAccountSubscription(config.baseUrl, session.token(), {
|
const remoteSubscription = await accountApi.addSubscription({
|
||||||
base_url: actualBaseUrl,
|
base_url: actualBaseUrl,
|
||||||
topic: topic
|
topic: topic
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,7 +8,8 @@ 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, {UnauthorizedError} from "../app/Api";
|
import {UnauthorizedError} from "../app/AccountApi";
|
||||||
|
import accountApi from "../app/AccountApi";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wire connectionManager and subscriptionManager so that subscriptions are updated when the connection
|
* Wire connectionManager and subscriptionManager so that subscriptions are updated when the connection
|
||||||
|
@ -65,7 +66,7 @@ export const useAutoSubscribe = (subscriptions, selected) => {
|
||||||
const subscription = await subscriptionManager.add(baseUrl, params.topic);
|
const subscription = await subscriptionManager.add(baseUrl, params.topic);
|
||||||
if (session.exists()) {
|
if (session.exists()) {
|
||||||
try {
|
try {
|
||||||
const remoteSubscription = await api.addAccountSubscription(config.baseUrl, session.token(), {
|
const remoteSubscription = await accountApi.addSubscription({
|
||||||
base_url: baseUrl,
|
base_url: baseUrl,
|
||||||
topic: params.topic
|
topic: params.topic
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue