From 7ca9afad5739c61e1674ba25d245f57927d14a92 Mon Sep 17 00:00:00 2001 From: binwiederhier Date: Wed, 28 Dec 2022 15:51:09 -0500 Subject: [PATCH] Account API endpoint fixes --- server/file_cache.go | 1 - server/server.go | 11 ++++------- user/manager.go | 3 +++ user/manager_test.go | 1 + web/src/app/AccountApi.js | 6 +++--- web/src/components/Account.js | 4 ++-- web/src/components/ActionBar.js | 2 ++ 7 files changed, 15 insertions(+), 13 deletions(-) diff --git a/server/file_cache.go b/server/file_cache.go index 9eae7ea..ebaeb76 100644 --- a/server/file_cache.go +++ b/server/file_cache.go @@ -22,7 +22,6 @@ type fileCache struct { dir string totalSizeCurrent int64 totalSizeLimit int64 - fileSizeLimit int64 mu sync.Mutex } diff --git a/server/server.go b/server/server.go index 2747f0b..6b62b0e 100644 --- a/server/server.go +++ b/server/server.go @@ -50,14 +50,11 @@ import ( - figure out what settings are "web" or "phone" UI: - Subscription dotmenu dropdown: Move to nav bar, or make same as profile dropdown - - "Logout and delete local storage" option - - Delete local storage when deleting account Pages: - Home - Password reset - Pricing - change email - - Polishing: aria-label for everything Tests: @@ -345,6 +342,8 @@ func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request, v *visit return s.handleHealth(w, r, v) } else if r.Method == http.MethodGet && r.URL.Path == webConfigPath { return s.ensureWebEnabled(s.handleWebConfig)(w, r, v) + } else if r.Method == http.MethodPost && r.URL.Path == accountTokenPath { + return s.ensureAccountsEnabled(s.handleAccountTokenIssue)(w, r, v) } else if r.Method == http.MethodPost && r.URL.Path == accountPath { return s.ensureAccountsEnabled(s.handleAccountCreate)(w, r, v) } else if r.Method == http.MethodGet && r.URL.Path == accountPath { @@ -353,8 +352,6 @@ func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request, v *visit return s.ensureWithAccount(s.handleAccountDelete)(w, r, v) } else if r.Method == http.MethodPost && r.URL.Path == accountPasswordPath { return s.ensureWithAccount(s.handleAccountPasswordChange)(w, r, v) - } else if r.Method == http.MethodPost && r.URL.Path == accountTokenPath { - return s.ensureWithAccount(s.handleAccountTokenIssue)(w, r, v) } else if r.Method == http.MethodPatch && r.URL.Path == accountTokenPath { return s.ensureWithAccount(s.handleAccountTokenExtend)(w, r, v) } else if r.Method == http.MethodDelete && r.URL.Path == accountTokenPath { @@ -1408,7 +1405,7 @@ func (s *Server) ensureWebEnabled(next handleFunc) handleFunc { func (s *Server) ensureAccountsEnabled(next handleFunc) handleFunc { return func(w http.ResponseWriter, r *http.Request, v *visitor) error { - if s.userManager != nil { + if s.userManager == nil { return errHTTPNotFound } return next(w, r, v) @@ -1417,7 +1414,7 @@ func (s *Server) ensureAccountsEnabled(next handleFunc) handleFunc { func (s *Server) ensureWithAccount(next handleFunc) handleFunc { return s.ensureAccountsEnabled(func(w http.ResponseWriter, r *http.Request, v *visitor) error { - if v.user != nil { + if v.user == nil { return errHTTPNotFound } return next(w, r, v) diff --git a/user/manager.go b/user/manager.go index 7a5e46a..6731e4e 100644 --- a/user/manager.go +++ b/user/manager.go @@ -243,6 +243,7 @@ func (a *Manager) RemoveExpiredTokens() error { return nil } +// ChangeSettings persists the user settings func (a *Manager) ChangeSettings(user *User) error { settings, err := json.Marshal(user.Prefs) if err != nil { @@ -254,6 +255,8 @@ func (a *Manager) ChangeSettings(user *User) error { return nil } +// EnqueueStats adds the user to a queue which writes out user stats (messages, emails, ..) in +// batches at a regular interval func (a *Manager) EnqueueStats(user *User) { a.mu.Lock() defer a.mu.Unlock() diff --git a/user/manager_test.go b/user/manager_test.go index 1e55bcf..91557b6 100644 --- a/user/manager_test.go +++ b/user/manager_test.go @@ -244,6 +244,7 @@ func TestManager_Token_Valid(t *testing.T) { // Create token for user token, err := a.CreateToken(u) + require.Nil(t, err) require.NotEmpty(t, token.Value) require.True(t, time.Now().Add(71*time.Hour).Unix() < token.Expires.Unix()) diff --git a/web/src/app/AccountApi.js b/web/src/app/AccountApi.js index 70622a8..2b057d9 100644 --- a/web/src/app/AccountApi.js +++ b/web/src/app/AccountApi.js @@ -45,12 +45,12 @@ class AccountApi { return json.token; } - async logout(token) { + async logout() { const url = accountTokenUrl(config.baseUrl); - console.log(`[AccountApi] Logging out from ${url} using token ${token}`); + console.log(`[AccountApi] Logging out from ${url} using token ${session.token()}`); const response = await fetch(url, { method: "DELETE", - headers: withBearerAuth({}, token) + headers: withBearerAuth({}, session.token()) }); if (response.status === 401 || response.status === 403) { throw new UnauthorizedError(); diff --git a/web/src/components/Account.js b/web/src/components/Account.js index 4490c32..890ee4f 100644 --- a/web/src/components/Account.js +++ b/web/src/components/Account.js @@ -57,7 +57,7 @@ const Stats = () => { const { t } = useTranslation(); const { account } = useOutletContext(); if (!account) { - return <>; // TODO loading + return <>; } const accountType = account.plan.code ?? "none"; const normalize = (value, max) => (value / max * 100); @@ -234,9 +234,9 @@ const DeleteAccount = () => { const handleDialogSubmit = async (newPassword) => { try { await accountApi.delete(); + await db.delete(); setDialogOpen(false); console.debug(`[Account] Account deleted`); - // TODO delete local storage session.resetAndRedirect(routes.app); } catch (e) { console.log(`[Account] Error deleting account`, e); diff --git a/web/src/components/ActionBar.js b/web/src/components/ActionBar.js index 0b6b0a9..6364ff4 100644 --- a/web/src/components/ActionBar.js +++ b/web/src/components/ActionBar.js @@ -8,6 +8,7 @@ import * as React from "react"; import {useEffect, useRef, useState} from "react"; import Box from "@mui/material/Box"; import {formatShortDateTime, shuffle, topicDisplayName} from "../app/utils"; +import db from "../app/db"; import {useLocation, useNavigate} from "react-router-dom"; import ClickAwayListener from '@mui/material/ClickAwayListener'; import Grow from '@mui/material/Grow'; @@ -270,6 +271,7 @@ const ProfileIcon = (props) => { const handleLogout = async () => { try { await accountApi.logout(); + await db.delete(); } finally { session.resetAndRedirect(routes.app); }