From ab4b5637540dcb9fd803b4ac2614865052748edf Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sat, 9 Sep 2023 02:29:30 -0700 Subject: [PATCH] Implement rudimentary Bahamut Mode (#381) Bahamut Mode lets me make sure people aren't doing naughty things behind closed doors. --- components/Header/index.module.scss | 11 +++++++++++ components/Header/index.tsx | 27 ++++++++++++++++++-------- components/auth/AccountModal/index.tsx | 18 +++++++++++++++++ components/auth/LoginModal/index.tsx | 5 +++++ components/auth/SignupModal/index.tsx | 4 ++++ pages/_app.tsx | 4 +++- public/locales/en/common.json | 8 ++++++-- public/locales/ja/common.json | 8 ++++++-- types/AccountCookie.d.ts | 1 + types/User.d.ts | 1 + utils/accountState.tsx | 2 ++ utils/localId.tsx | 7 ++++--- utils/userToken.tsx | 26 ++++++++++++++++++------- 13 files changed, 99 insertions(+), 23 deletions(-) diff --git a/components/Header/index.module.scss b/components/Header/index.module.scss index 309ba5e0..7d6a6fbe 100644 --- a/components/Header/index.module.scss +++ b/components/Header/index.module.scss @@ -1,3 +1,14 @@ +.bahamut { + background: #2b4683; + color: white; + text-align: center; + font-weight: $bold; + padding: $unit; + border-radius: $full-corner; + margin-bottom: $unit; + width: 100%; +} + .header { display: flex; flex-direction: row; diff --git a/components/Header/index.tsx b/components/Header/index.tsx index cf714466..2bc75fa3 100644 --- a/components/Header/index.tsx +++ b/components/Header/index.tsx @@ -172,6 +172,12 @@ const Header = () => { gender={accountState.account.user.gender} language={accountState.account.user.language} theme={accountState.account.user.theme} + role={accountState.account.user.role} + bahamutMode={ + accountState.account.user.role === 9 + ? accountState.account.user.bahamut + : false + } onOpenChange={setSettingsModalOpen} /> )} @@ -375,14 +381,19 @@ const Header = () => { ) return ( - + <> + {accountState.account.user?.bahamut && ( +
Bahamut Mode is active
+ )} + + ) } diff --git a/components/auth/AccountModal/index.tsx b/components/auth/AccountModal/index.tsx index 1cfa9cb1..0dc5db23 100644 --- a/components/auth/AccountModal/index.tsx +++ b/components/auth/AccountModal/index.tsx @@ -18,6 +18,7 @@ import { accountState } from '~utils/accountState' import { pictureData } from '~utils/pictureData' import styles from './index.module.scss' +import SwitchTableField from '~components/common/SwitchTableField' interface Props { open: boolean @@ -27,6 +28,8 @@ interface Props { language?: string theme?: string private?: boolean + role?: number + bahamutMode?: boolean onOpenChange?: (open: boolean) => void } @@ -53,6 +56,7 @@ const AccountModal = React.forwardRef( const [language, setLanguage] = useState(props.language || '') const [gender, setGender] = useState(props.gender || 0) const [theme, setTheme] = useState(props.theme || 'system') + const [bahamutMode, setBahamutMode] = useState(props.bahamutMode || false) // Setup const [pictureOpen, setPictureOpen] = useState(false) @@ -135,6 +139,7 @@ const AccountModal = React.forwardRef( gender: user.gender, language: user.language, theme: user.theme, + bahamut: bahamutMode, } const expiresAt = new Date() @@ -145,6 +150,7 @@ const AccountModal = React.forwardRef( id: user.id, username: user.username, granblueId: '', + role: user.role, avatar: { picture: user.avatar.picture, element: user.avatar.element, @@ -152,11 +158,13 @@ const AccountModal = React.forwardRef( language: user.language, theme: user.theme, gender: user.gender, + bahamut: bahamutMode, } setOpen(false) if (props.onOpenChange) props.onOpenChange(false) changeLanguage(router, user.language) + if (props.bahamutMode != bahamutMode) router.reload() }) } } @@ -265,6 +273,15 @@ const AccountModal = React.forwardRef( ) + const adminField = () => ( + setBahamutMode(value)} + /> + ) + useEffect(() => { setMounted(true) }, []) @@ -293,6 +310,7 @@ const AccountModal = React.forwardRef( {genderField()} {languageField()} {themeField()} + {props.role === 9 && adminField()} { const cookieObj: AccountCookie = { userId: resp.user.id, username: resp.user.username, + role: resp.user.role, token: resp.access_token, } @@ -169,6 +171,7 @@ const LoginModal = (props: Props) => { language: user.language, gender: user.gender, theme: user.theme, + bahamut: false, }, { path: '/', expires: expiresAt } ) @@ -178,6 +181,7 @@ const LoginModal = (props: Props) => { id: user.id, username: user.username, granblueId: '', + role: user.role, avatar: { picture: user.avatar.picture, element: user.avatar.element, @@ -185,6 +189,7 @@ const LoginModal = (props: Props) => { gender: user.gender, language: user.language, theme: user.theme, + bahamut: false, } console.log('Authorizing account...') diff --git a/components/auth/SignupModal/index.tsx b/components/auth/SignupModal/index.tsx index 7b57c82d..d9c62279 100644 --- a/components/auth/SignupModal/index.tsx +++ b/components/auth/SignupModal/index.tsx @@ -97,6 +97,7 @@ const SignupModal = (props: Props) => { const cookieObj: AccountCookie = { userId: resp.id, username: resp.username, + role: resp.role, token: resp.token, } @@ -130,6 +131,7 @@ const SignupModal = (props: Props) => { language: user.language, gender: user.gender, theme: user.theme, + bahamut: false, }, { path: '/', expires: expiresAt } ) @@ -139,6 +141,7 @@ const SignupModal = (props: Props) => { id: user.id, username: user.username, granblueId: '', + role: user.role, avatar: { picture: user.avatar.picture, element: user.avatar.element, @@ -146,6 +149,7 @@ const SignupModal = (props: Props) => { gender: user.gender, language: user.language, theme: user.theme, + bahamut: false, } console.log('Authorizing account...') diff --git a/pages/_app.tsx b/pages/_app.tsx index 1762afa9..e978aaf1 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -65,11 +65,11 @@ function MyApp({ Component, pageProps }: AppProps) { setHeaders() if (cookieData.account && cookieData.account.token) { console.log(`Logged in as user "${cookieData.account.username}"`) - accountState.account.authorized = true accountState.account.user = { id: cookieData.account.userId, username: cookieData.account.username, + role: cookieData.account.role, granblueId: '', avatar: { picture: cookieData.user.avatar.picture, @@ -78,6 +78,7 @@ function MyApp({ Component, pageProps }: AppProps) { gender: cookieData.user.gender, language: cookieData.user.language, theme: cookieData.user.theme, + bahamut: cookieData.user.bahamut, } } else { console.log(`You are not currently logged in.`) @@ -101,6 +102,7 @@ function MyApp({ Component, pageProps }: AppProps) { const cookieObj = { userId: localUserId, + role: 1, username: undefined, token: undefined, } diff --git a/public/locales/en/common.json b/public/locales/en/common.json index c480b5c4..734698e0 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -120,7 +120,10 @@ "validation": { "guidebooks": "You cannot equip more than one of each Sephira Guidebook" }, - "unauthorized": "You don't have permission to perform that action" + "unauthorized": { + "title": "Unauthorized", + "description": "You don't have permission to perform that action" + } }, "filters": { "name": "Filter", @@ -349,7 +352,8 @@ "language": "Language", "gender": "Main Character", "private": "Private", - "theme": "Theme" + "theme": "Theme", + "admin": "Bahamut Mode" }, "descriptions": { "picture": "Displayed next to your name", diff --git a/public/locales/ja/common.json b/public/locales/ja/common.json index c036d01a..435a8a19 100644 --- a/public/locales/ja/common.json +++ b/public/locales/ja/common.json @@ -120,7 +120,10 @@ "validation": { "guidebooks": "セフィラ導本を複数個装備することはできません" }, - "unauthorized": "行ったアクションを実行する権限がありません" + "unauthorized": { + "title": "権限がありません", + "description": "行ったアクションを実行する権限がありません" + } }, "filters": { "name": "フィルター", @@ -348,7 +351,8 @@ "language": "言語", "gender": "主人公", "private": "プライベート", - "theme": "表示" + "theme": "表示", + "admin": "バハムートモード" }, "descriptions": { "picture": "名前の隣に表示する", diff --git a/types/AccountCookie.d.ts b/types/AccountCookie.d.ts index 444f2e4a..747e6b7d 100644 --- a/types/AccountCookie.d.ts +++ b/types/AccountCookie.d.ts @@ -2,4 +2,5 @@ interface AccountCookie { userId: string username: string token: string + role: number } diff --git a/types/User.d.ts b/types/User.d.ts index b79e76f2..c98cf947 100644 --- a/types/User.d.ts +++ b/types/User.d.ts @@ -7,4 +7,5 @@ interface User { element: string } gender: number + role: number } diff --git a/utils/accountState.tsx b/utils/accountState.tsx index ffe3cb60..946f2d4e 100644 --- a/utils/accountState.tsx +++ b/utils/accountState.tsx @@ -4,6 +4,7 @@ export type UserState = { id: string granblueId: string username: string + role: number avatar: { picture: string element: string @@ -11,6 +12,7 @@ export type UserState = { gender: number language: string theme: string + bahamut: boolean } interface AccountState { diff --git a/utils/localId.tsx b/utils/localId.tsx index a1cfcdba..0fc6a141 100644 --- a/utils/localId.tsx +++ b/utils/localId.tsx @@ -1,4 +1,4 @@ -import { accountCookie } from './userToken' +import { retrieveCookie } from './userToken' import { v4 as uuidv4 } from 'uuid' import { setCookie } from 'cookies-next' import type { NextApiRequest, NextApiResponse } from 'next' @@ -8,7 +8,7 @@ export const createLocalId = ( res: NextApiResponse | undefined = undefined ) => { // If there is no account entry in cookies, create a UUID and store it - if (!accountCookie(req, res)) { + if (!retrieveCookie('account', req, res)) { const uuid = uuidv4() const expiresAt = new Date() expiresAt.setDate(expiresAt.getDate() + 60) @@ -16,6 +16,7 @@ export const createLocalId = ( const cookieObj = { userId: uuid, username: undefined, + role: 1, token: undefined, } @@ -31,7 +32,7 @@ export const createLocalId = ( } export const getLocalId = () => { - const cookie = accountCookie() + const cookie = retrieveCookie('account') if (cookie) { const parsed = JSON.parse(cookie as string) if (parsed && !parsed.token) diff --git a/utils/userToken.tsx b/utils/userToken.tsx index aebd920e..5ee12594 100644 --- a/utils/userToken.tsx +++ b/utils/userToken.tsx @@ -3,12 +3,13 @@ import ls, { get, set } from 'local-storage' import { getCookie } from 'cookies-next' import type { NextApiRequest, NextApiResponse } from 'next' -export const accountCookie = ( +export const retrieveCookie = ( + name: string, req: NextApiRequest | undefined = undefined, res: NextApiResponse | undefined = undefined ) => { const options = req && res ? { req, res } : {} - const cookie = getCookie('account', options) + const cookie = getCookie(name, options) return cookie ? cookie : undefined } @@ -16,13 +17,24 @@ export const setHeaders = ( req: NextApiRequest | undefined = undefined, res: NextApiResponse | undefined = undefined ) => { - const cookie = accountCookie(req, res) - if (cookie) { - const parsed = JSON.parse(cookie as string) - if (parsed.token) - axios.defaults.headers.common['Authorization'] = `Bearer ${parsed.token}` + const accountCookie = retrieveCookie('account', req, res) + const userCookie = retrieveCookie('user', req, res) + + if (accountCookie && userCookie) { + const account = JSON.parse(accountCookie as string) + const user = JSON.parse(userCookie as string) + + if (account.token) + axios.defaults.headers.common['Authorization'] = `Bearer ${account.token}` + + if (account.role === 9 && user.bahamut) + axios.defaults.headers.common['X-Admin-Mode'] = 'true' + else { + delete axios.defaults.headers.common['X-Admin-Mode'] + } } else { delete axios.defaults.headers.common['Authorization'] + delete axios.defaults.headers.common['X-Admin-Mode'] } }