-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a simple user list to admin panel (#36)
- Loading branch information
Showing
15 changed files
with
358 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
NEXT_PUBLIC_CLIENT_ID=a1b2c3 # Replace with your real clientId | ||
NEXT_PUBLIC_REDIRECT_URI=http://localhost:3000 # Replace with the host of your react app | ||
NEXT_PUBLIC_CLIENT_ID=a1b2c3 # Replace with your real SPA clientId | ||
NEXT_PUBLIC_CLIENT_URI=http://localhost:3000 # Replace with the host of your react app | ||
NEXT_PUBLIC_SERVER_URI=http://localhost:8787 # Replace with your melody-auth server base uri | ||
SERVER_CLIENT_ID=b2c3d4 # Replace with your real S2S clientId | ||
SERVER_CLIENT_SECRET=abc # Replace with your real S2S clientSecret | ||
CLIENT_JWT_SECRET=abc # Replace with your ACCESS_TOKEN_SECRET |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
'use client' | ||
|
||
import { useTranslations } from 'next-intl' | ||
import { Button } from 'flowbite-react' | ||
import Link from 'next/link' | ||
import useCurrentLocale from 'hooks/useCurrentLocale' | ||
import { routeTool } from 'tools' | ||
|
||
const Page = () => { | ||
const local = useCurrentLocale() | ||
const t = useTranslations() | ||
return ( | ||
<section className='flex'> | ||
<Button | ||
size='sm' | ||
as={Link} | ||
href={`/${local}/${routeTool.Internal.Users}`}> | ||
{t('layout.users')} | ||
</Button> | ||
</section> | ||
) | ||
} | ||
|
||
export default Page |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
'use client' | ||
|
||
export default function Home () { | ||
return ( | ||
<section>Home Page</section> | ||
<section> | ||
Home | ||
</section> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
'use client' | ||
|
||
import { useAuth } from '@melody-auth/react' | ||
import { | ||
Badge, Table, | ||
} from 'flowbite-react' | ||
import { useTranslations } from 'next-intl' | ||
import { | ||
useEffect, useState, | ||
} from 'react' | ||
import { proxyTool } from 'tools' | ||
|
||
const Page = () => { | ||
const t = useTranslations() | ||
|
||
const [users, setUsers] = useState([]) | ||
const { acquireToken } = useAuth() | ||
|
||
useEffect( | ||
() => { | ||
const getUsers = async () => { | ||
const token = await acquireToken() | ||
const data = await proxyTool.sendNextRequest({ | ||
endpoint: '/api/users', | ||
method: 'GET', | ||
token, | ||
}) | ||
setUsers(data.users) | ||
} | ||
|
||
getUsers() | ||
}, | ||
[acquireToken], | ||
) | ||
|
||
return ( | ||
<section> | ||
<Table> | ||
<Table.Head> | ||
<Table.HeadCell>{t('users.authId')}</Table.HeadCell> | ||
<Table.HeadCell>{t('users.email')}</Table.HeadCell> | ||
<Table.HeadCell>{t('users.name')}</Table.HeadCell> | ||
<Table.HeadCell>{t('users.status')}</Table.HeadCell> | ||
</Table.Head> | ||
<Table.Body className='divide-y'> | ||
{users.map((user) => ( | ||
<Table.Row key={user.id}> | ||
<Table.Cell>{user.authId}</Table.Cell> | ||
<Table.Cell>{user.email}</Table.Cell> | ||
<Table.Cell>{`${user.firstName ?? ''} ${user.lastName ?? ''}`} </Table.Cell> | ||
<Table.Cell> | ||
<div className='flex'> | ||
{user.deletedAt ? (<Badge color='failure'>Disabled</Badge>) : (<Badge color='success'>Active</Badge>)} | ||
</div> | ||
</Table.Cell> | ||
</Table.Row> | ||
))} | ||
</Table.Body> | ||
</Table> | ||
</section> | ||
) | ||
} | ||
|
||
export default Page |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { headers } from 'next/headers' | ||
import { NextResponse } from 'next/server' | ||
import jwt from 'jsonwebtoken' | ||
|
||
let accessToken: string | null = null | ||
let accessTokenExpiresOn: number | null = null | ||
|
||
const basicAuth = btoa(`${process.env.SERVER_CLIENT_ID}:${process.env.SERVER_CLIENT_SECRET}`) | ||
|
||
export const throwForbiddenError = (message?: string) => { | ||
throw NextResponse.json( | ||
{}, | ||
{ | ||
status: 400, statusText: message, | ||
}, | ||
) | ||
} | ||
|
||
export const verifyAccessToken = () => { | ||
const headersList = headers() | ||
const authHeader = headersList.get('authorization') | ||
const accessToken = authHeader?.split(' ')[1] | ||
if (!accessToken) throwForbiddenError() | ||
|
||
const tokenBody = jwt.verify( | ||
accessToken, | ||
process.env.CLIENT_JWT_SECRET, | ||
) | ||
if (!tokenBody) throwForbiddenError() | ||
|
||
if (!tokenBody.roles || !tokenBody.roles.includes('super_admin')) throwForbiddenError() | ||
} | ||
|
||
export const obtainS2SAccessToken = async () => { | ||
if (accessToken && accessTokenExpiresOn) { | ||
const currentTime = new Date().getTime() / 1000 | ||
if (currentTime + 5 < accessTokenExpiresOn) return accessToken | ||
} | ||
|
||
const body = { | ||
grant_type: 'client_credentials', | ||
scope: 'read_user write_user', | ||
} | ||
const res = await fetch( | ||
`${process.env.NEXT_PUBLIC_SERVER_URI}/oauth2/v1/token`, | ||
{ | ||
method: 'POST', | ||
headers: { | ||
Authorization: `Basic ${basicAuth}`, | ||
'Content-Type': 'application/x-www-form-urlencoded', | ||
}, | ||
body: new URLSearchParams(body).toString(), | ||
}, | ||
) | ||
if (res.ok) { | ||
const data = await res.json() | ||
if (!data.scope || !data.access_token || !data.expires_on) { | ||
throwForbiddenError() | ||
} | ||
|
||
if (!data.scope.includes('read_user')) throwForbiddenError('read_user scope required.') | ||
if (!data.scope.includes('write_user')) throwForbiddenError('write_user scope required.') | ||
|
||
accessToken = data.access_token | ||
accessTokenExpiresOn = data.expires_on | ||
|
||
return accessToken | ||
} else { | ||
throwForbiddenError() | ||
} | ||
} | ||
|
||
export const sendS2SRequest = async ({ | ||
method, | ||
uri, | ||
}: { | ||
uri: string; | ||
method: 'GET'; | ||
}) => { | ||
const token = await obtainS2SAccessToken() | ||
|
||
const res = await fetch( | ||
`${process.env.NEXT_PUBLIC_SERVER_URI}/api/v1${uri}`, | ||
{ | ||
method, | ||
headers: { Authorization: `Bearer ${token}` }, | ||
}, | ||
) | ||
if (res.ok) { | ||
const data = await res.json() | ||
return data | ||
} else { | ||
throwForbiddenError() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { NextResponse } from 'next/server' | ||
import { | ||
verifyAccessToken, sendS2SRequest, | ||
} from 'app/api/request' | ||
|
||
export async function GET () { | ||
verifyAccessToken() | ||
|
||
const data = await sendS2SRequest({ | ||
method: 'GET', | ||
uri: '/users?include_disabled=true', | ||
}) | ||
return NextResponse.json(data) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * as proxyTool from 'tools/proxy' | ||
export * as routeTool from 'tools/route' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
export const sendNextRequest = async ({ | ||
endpoint, | ||
method, | ||
token, | ||
}: { | ||
endpoint: string; | ||
method: 'GET'; | ||
token: string; | ||
}) => { | ||
const res = await fetch( | ||
endpoint, | ||
{ | ||
method, | ||
headers: { Authorization: `bearer ${token}` }, | ||
}, | ||
) | ||
if (res.ok) { | ||
const data = await res.json() | ||
return data | ||
} else { | ||
throw new Error('Can not fetch data') | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export enum Internal { | ||
Dashboard = '/dashboard', | ||
Users = '/users', | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.