Implement rudimentary Bahamut Mode (#381)
Bahamut Mode lets me make sure people aren't doing naughty things behind closed doors.
This commit is contained in:
parent
9bb5e721ff
commit
ab4b563754
13 changed files with 99 additions and 23 deletions
|
|
@ -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 {
|
.header {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
|
||||||
|
|
@ -172,6 +172,12 @@ const Header = () => {
|
||||||
gender={accountState.account.user.gender}
|
gender={accountState.account.user.gender}
|
||||||
language={accountState.account.user.language}
|
language={accountState.account.user.language}
|
||||||
theme={accountState.account.user.theme}
|
theme={accountState.account.user.theme}
|
||||||
|
role={accountState.account.user.role}
|
||||||
|
bahamutMode={
|
||||||
|
accountState.account.user.role === 9
|
||||||
|
? accountState.account.user.bahamut
|
||||||
|
: false
|
||||||
|
}
|
||||||
onOpenChange={setSettingsModalOpen}
|
onOpenChange={setSettingsModalOpen}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
@ -375,14 +381,19 @@ const Header = () => {
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className={styles.header}>
|
<>
|
||||||
{left}
|
{accountState.account.user?.bahamut && (
|
||||||
{right}
|
<div className={styles.bahamut}>Bahamut Mode is active</div>
|
||||||
{logoutConfirmationAlert}
|
)}
|
||||||
{settingsModal}
|
<nav className={styles.header}>
|
||||||
{loginModal}
|
{left}
|
||||||
{signupModal}
|
{right}
|
||||||
</nav>
|
{logoutConfirmationAlert}
|
||||||
|
{settingsModal}
|
||||||
|
{loginModal}
|
||||||
|
{signupModal}
|
||||||
|
</nav>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import { accountState } from '~utils/accountState'
|
||||||
import { pictureData } from '~utils/pictureData'
|
import { pictureData } from '~utils/pictureData'
|
||||||
|
|
||||||
import styles from './index.module.scss'
|
import styles from './index.module.scss'
|
||||||
|
import SwitchTableField from '~components/common/SwitchTableField'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
open: boolean
|
open: boolean
|
||||||
|
|
@ -27,6 +28,8 @@ interface Props {
|
||||||
language?: string
|
language?: string
|
||||||
theme?: string
|
theme?: string
|
||||||
private?: boolean
|
private?: boolean
|
||||||
|
role?: number
|
||||||
|
bahamutMode?: boolean
|
||||||
onOpenChange?: (open: boolean) => void
|
onOpenChange?: (open: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,6 +56,7 @@ const AccountModal = React.forwardRef<HTMLDivElement, Props>(
|
||||||
const [language, setLanguage] = useState(props.language || '')
|
const [language, setLanguage] = useState(props.language || '')
|
||||||
const [gender, setGender] = useState(props.gender || 0)
|
const [gender, setGender] = useState(props.gender || 0)
|
||||||
const [theme, setTheme] = useState(props.theme || 'system')
|
const [theme, setTheme] = useState(props.theme || 'system')
|
||||||
|
const [bahamutMode, setBahamutMode] = useState(props.bahamutMode || false)
|
||||||
|
|
||||||
// Setup
|
// Setup
|
||||||
const [pictureOpen, setPictureOpen] = useState(false)
|
const [pictureOpen, setPictureOpen] = useState(false)
|
||||||
|
|
@ -135,6 +139,7 @@ const AccountModal = React.forwardRef<HTMLDivElement, Props>(
|
||||||
gender: user.gender,
|
gender: user.gender,
|
||||||
language: user.language,
|
language: user.language,
|
||||||
theme: user.theme,
|
theme: user.theme,
|
||||||
|
bahamut: bahamutMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
const expiresAt = new Date()
|
const expiresAt = new Date()
|
||||||
|
|
@ -145,6 +150,7 @@ const AccountModal = React.forwardRef<HTMLDivElement, Props>(
|
||||||
id: user.id,
|
id: user.id,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
granblueId: '',
|
granblueId: '',
|
||||||
|
role: user.role,
|
||||||
avatar: {
|
avatar: {
|
||||||
picture: user.avatar.picture,
|
picture: user.avatar.picture,
|
||||||
element: user.avatar.element,
|
element: user.avatar.element,
|
||||||
|
|
@ -152,11 +158,13 @@ const AccountModal = React.forwardRef<HTMLDivElement, Props>(
|
||||||
language: user.language,
|
language: user.language,
|
||||||
theme: user.theme,
|
theme: user.theme,
|
||||||
gender: user.gender,
|
gender: user.gender,
|
||||||
|
bahamut: bahamutMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
if (props.onOpenChange) props.onOpenChange(false)
|
if (props.onOpenChange) props.onOpenChange(false)
|
||||||
changeLanguage(router, user.language)
|
changeLanguage(router, user.language)
|
||||||
|
if (props.bahamutMode != bahamutMode) router.reload()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -265,6 +273,15 @@ const AccountModal = React.forwardRef<HTMLDivElement, Props>(
|
||||||
</SelectTableField>
|
</SelectTableField>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const adminField = () => (
|
||||||
|
<SwitchTableField
|
||||||
|
name="admin"
|
||||||
|
label={t('modals.settings.labels.admin')}
|
||||||
|
value={props.bahamutMode}
|
||||||
|
onValueChange={(value: boolean) => setBahamutMode(value)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMounted(true)
|
setMounted(true)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
@ -293,6 +310,7 @@ const AccountModal = React.forwardRef<HTMLDivElement, Props>(
|
||||||
{genderField()}
|
{genderField()}
|
||||||
{languageField()}
|
{languageField()}
|
||||||
{themeField()}
|
{themeField()}
|
||||||
|
{props.role === 9 && adminField()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DialogFooter
|
<DialogFooter
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import DialogFooter from '~components/common/DialogFooter'
|
||||||
import DialogContent from '~components/common/DialogContent'
|
import DialogContent from '~components/common/DialogContent'
|
||||||
|
|
||||||
import styles from './index.module.scss'
|
import styles from './index.module.scss'
|
||||||
|
import { userAgent } from 'next/server'
|
||||||
|
|
||||||
interface ErrorMap {
|
interface ErrorMap {
|
||||||
[index: string]: string
|
[index: string]: string
|
||||||
|
|
@ -140,6 +141,7 @@ const LoginModal = (props: Props) => {
|
||||||
const cookieObj: AccountCookie = {
|
const cookieObj: AccountCookie = {
|
||||||
userId: resp.user.id,
|
userId: resp.user.id,
|
||||||
username: resp.user.username,
|
username: resp.user.username,
|
||||||
|
role: resp.user.role,
|
||||||
token: resp.access_token,
|
token: resp.access_token,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -169,6 +171,7 @@ const LoginModal = (props: Props) => {
|
||||||
language: user.language,
|
language: user.language,
|
||||||
gender: user.gender,
|
gender: user.gender,
|
||||||
theme: user.theme,
|
theme: user.theme,
|
||||||
|
bahamut: false,
|
||||||
},
|
},
|
||||||
{ path: '/', expires: expiresAt }
|
{ path: '/', expires: expiresAt }
|
||||||
)
|
)
|
||||||
|
|
@ -178,6 +181,7 @@ const LoginModal = (props: Props) => {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
granblueId: '',
|
granblueId: '',
|
||||||
|
role: user.role,
|
||||||
avatar: {
|
avatar: {
|
||||||
picture: user.avatar.picture,
|
picture: user.avatar.picture,
|
||||||
element: user.avatar.element,
|
element: user.avatar.element,
|
||||||
|
|
@ -185,6 +189,7 @@ const LoginModal = (props: Props) => {
|
||||||
gender: user.gender,
|
gender: user.gender,
|
||||||
language: user.language,
|
language: user.language,
|
||||||
theme: user.theme,
|
theme: user.theme,
|
||||||
|
bahamut: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Authorizing account...')
|
console.log('Authorizing account...')
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,7 @@ const SignupModal = (props: Props) => {
|
||||||
const cookieObj: AccountCookie = {
|
const cookieObj: AccountCookie = {
|
||||||
userId: resp.id,
|
userId: resp.id,
|
||||||
username: resp.username,
|
username: resp.username,
|
||||||
|
role: resp.role,
|
||||||
token: resp.token,
|
token: resp.token,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,6 +131,7 @@ const SignupModal = (props: Props) => {
|
||||||
language: user.language,
|
language: user.language,
|
||||||
gender: user.gender,
|
gender: user.gender,
|
||||||
theme: user.theme,
|
theme: user.theme,
|
||||||
|
bahamut: false,
|
||||||
},
|
},
|
||||||
{ path: '/', expires: expiresAt }
|
{ path: '/', expires: expiresAt }
|
||||||
)
|
)
|
||||||
|
|
@ -139,6 +141,7 @@ const SignupModal = (props: Props) => {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
granblueId: '',
|
granblueId: '',
|
||||||
|
role: user.role,
|
||||||
avatar: {
|
avatar: {
|
||||||
picture: user.avatar.picture,
|
picture: user.avatar.picture,
|
||||||
element: user.avatar.element,
|
element: user.avatar.element,
|
||||||
|
|
@ -146,6 +149,7 @@ const SignupModal = (props: Props) => {
|
||||||
gender: user.gender,
|
gender: user.gender,
|
||||||
language: user.language,
|
language: user.language,
|
||||||
theme: user.theme,
|
theme: user.theme,
|
||||||
|
bahamut: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Authorizing account...')
|
console.log('Authorizing account...')
|
||||||
|
|
|
||||||
|
|
@ -65,11 +65,11 @@ function MyApp({ Component, pageProps }: AppProps) {
|
||||||
setHeaders()
|
setHeaders()
|
||||||
if (cookieData.account && cookieData.account.token) {
|
if (cookieData.account && cookieData.account.token) {
|
||||||
console.log(`Logged in as user "${cookieData.account.username}"`)
|
console.log(`Logged in as user "${cookieData.account.username}"`)
|
||||||
|
|
||||||
accountState.account.authorized = true
|
accountState.account.authorized = true
|
||||||
accountState.account.user = {
|
accountState.account.user = {
|
||||||
id: cookieData.account.userId,
|
id: cookieData.account.userId,
|
||||||
username: cookieData.account.username,
|
username: cookieData.account.username,
|
||||||
|
role: cookieData.account.role,
|
||||||
granblueId: '',
|
granblueId: '',
|
||||||
avatar: {
|
avatar: {
|
||||||
picture: cookieData.user.avatar.picture,
|
picture: cookieData.user.avatar.picture,
|
||||||
|
|
@ -78,6 +78,7 @@ function MyApp({ Component, pageProps }: AppProps) {
|
||||||
gender: cookieData.user.gender,
|
gender: cookieData.user.gender,
|
||||||
language: cookieData.user.language,
|
language: cookieData.user.language,
|
||||||
theme: cookieData.user.theme,
|
theme: cookieData.user.theme,
|
||||||
|
bahamut: cookieData.user.bahamut,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(`You are not currently logged in.`)
|
console.log(`You are not currently logged in.`)
|
||||||
|
|
@ -101,6 +102,7 @@ function MyApp({ Component, pageProps }: AppProps) {
|
||||||
|
|
||||||
const cookieObj = {
|
const cookieObj = {
|
||||||
userId: localUserId,
|
userId: localUserId,
|
||||||
|
role: 1,
|
||||||
username: undefined,
|
username: undefined,
|
||||||
token: undefined,
|
token: undefined,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,10 @@
|
||||||
"validation": {
|
"validation": {
|
||||||
"guidebooks": "You cannot equip more than one of each Sephira Guidebook"
|
"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": {
|
"filters": {
|
||||||
"name": "Filter",
|
"name": "Filter",
|
||||||
|
|
@ -349,7 +352,8 @@
|
||||||
"language": "Language",
|
"language": "Language",
|
||||||
"gender": "Main Character",
|
"gender": "Main Character",
|
||||||
"private": "Private",
|
"private": "Private",
|
||||||
"theme": "Theme"
|
"theme": "Theme",
|
||||||
|
"admin": "Bahamut Mode"
|
||||||
},
|
},
|
||||||
"descriptions": {
|
"descriptions": {
|
||||||
"picture": "Displayed next to your name",
|
"picture": "Displayed next to your name",
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,10 @@
|
||||||
"validation": {
|
"validation": {
|
||||||
"guidebooks": "セフィラ導本を複数個装備することはできません"
|
"guidebooks": "セフィラ導本を複数個装備することはできません"
|
||||||
},
|
},
|
||||||
"unauthorized": "行ったアクションを実行する権限がありません"
|
"unauthorized": {
|
||||||
|
"title": "権限がありません",
|
||||||
|
"description": "行ったアクションを実行する権限がありません"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"filters": {
|
"filters": {
|
||||||
"name": "フィルター",
|
"name": "フィルター",
|
||||||
|
|
@ -348,7 +351,8 @@
|
||||||
"language": "言語",
|
"language": "言語",
|
||||||
"gender": "主人公",
|
"gender": "主人公",
|
||||||
"private": "プライベート",
|
"private": "プライベート",
|
||||||
"theme": "表示"
|
"theme": "表示",
|
||||||
|
"admin": "バハムートモード"
|
||||||
},
|
},
|
||||||
"descriptions": {
|
"descriptions": {
|
||||||
"picture": "名前の隣に表示する",
|
"picture": "名前の隣に表示する",
|
||||||
|
|
|
||||||
1
types/AccountCookie.d.ts
vendored
1
types/AccountCookie.d.ts
vendored
|
|
@ -2,4 +2,5 @@ interface AccountCookie {
|
||||||
userId: string
|
userId: string
|
||||||
username: string
|
username: string
|
||||||
token: string
|
token: string
|
||||||
|
role: number
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
types/User.d.ts
vendored
1
types/User.d.ts
vendored
|
|
@ -7,4 +7,5 @@ interface User {
|
||||||
element: string
|
element: string
|
||||||
}
|
}
|
||||||
gender: number
|
gender: number
|
||||||
|
role: number
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ export type UserState = {
|
||||||
id: string
|
id: string
|
||||||
granblueId: string
|
granblueId: string
|
||||||
username: string
|
username: string
|
||||||
|
role: number
|
||||||
avatar: {
|
avatar: {
|
||||||
picture: string
|
picture: string
|
||||||
element: string
|
element: string
|
||||||
|
|
@ -11,6 +12,7 @@ export type UserState = {
|
||||||
gender: number
|
gender: number
|
||||||
language: string
|
language: string
|
||||||
theme: string
|
theme: string
|
||||||
|
bahamut: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AccountState {
|
interface AccountState {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { accountCookie } from './userToken'
|
import { retrieveCookie } from './userToken'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { setCookie } from 'cookies-next'
|
import { setCookie } from 'cookies-next'
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
|
@ -8,7 +8,7 @@ export const createLocalId = (
|
||||||
res: NextApiResponse | undefined = undefined
|
res: NextApiResponse | undefined = undefined
|
||||||
) => {
|
) => {
|
||||||
// If there is no account entry in cookies, create a UUID and store it
|
// 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 uuid = uuidv4()
|
||||||
const expiresAt = new Date()
|
const expiresAt = new Date()
|
||||||
expiresAt.setDate(expiresAt.getDate() + 60)
|
expiresAt.setDate(expiresAt.getDate() + 60)
|
||||||
|
|
@ -16,6 +16,7 @@ export const createLocalId = (
|
||||||
const cookieObj = {
|
const cookieObj = {
|
||||||
userId: uuid,
|
userId: uuid,
|
||||||
username: undefined,
|
username: undefined,
|
||||||
|
role: 1,
|
||||||
token: undefined,
|
token: undefined,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,7 +32,7 @@ export const createLocalId = (
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getLocalId = () => {
|
export const getLocalId = () => {
|
||||||
const cookie = accountCookie()
|
const cookie = retrieveCookie('account')
|
||||||
if (cookie) {
|
if (cookie) {
|
||||||
const parsed = JSON.parse(cookie as string)
|
const parsed = JSON.parse(cookie as string)
|
||||||
if (parsed && !parsed.token)
|
if (parsed && !parsed.token)
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,13 @@ import ls, { get, set } from 'local-storage'
|
||||||
import { getCookie } from 'cookies-next'
|
import { getCookie } from 'cookies-next'
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
|
||||||
export const accountCookie = (
|
export const retrieveCookie = (
|
||||||
|
name: string,
|
||||||
req: NextApiRequest | undefined = undefined,
|
req: NextApiRequest | undefined = undefined,
|
||||||
res: NextApiResponse | undefined = undefined
|
res: NextApiResponse | undefined = undefined
|
||||||
) => {
|
) => {
|
||||||
const options = req && res ? { req, res } : {}
|
const options = req && res ? { req, res } : {}
|
||||||
const cookie = getCookie('account', options)
|
const cookie = getCookie(name, options)
|
||||||
return cookie ? cookie : undefined
|
return cookie ? cookie : undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -16,13 +17,24 @@ export const setHeaders = (
|
||||||
req: NextApiRequest | undefined = undefined,
|
req: NextApiRequest | undefined = undefined,
|
||||||
res: NextApiResponse | undefined = undefined
|
res: NextApiResponse | undefined = undefined
|
||||||
) => {
|
) => {
|
||||||
const cookie = accountCookie(req, res)
|
const accountCookie = retrieveCookie('account', req, res)
|
||||||
if (cookie) {
|
const userCookie = retrieveCookie('user', req, res)
|
||||||
const parsed = JSON.parse(cookie as string)
|
|
||||||
if (parsed.token)
|
if (accountCookie && userCookie) {
|
||||||
axios.defaults.headers.common['Authorization'] = `Bearer ${parsed.token}`
|
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 {
|
} else {
|
||||||
delete axios.defaults.headers.common['Authorization']
|
delete axios.defaults.headers.common['Authorization']
|
||||||
|
delete axios.defaults.headers.common['X-Admin-Mode']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue