Updating ui

This commit is contained in:
Benjamin Sherriff
2024-01-30 20:09:51 -05:00
parent ca9270f3a7
commit 57286bb0e7
32 changed files with 420 additions and 425 deletions

124
ui/package-lock.json generated
View File

@@ -16,6 +16,7 @@
"@pixi/react": "^7.1.1",
"js-cookie": "^3.0.5",
"next": "^14.1.0",
"next-auth": "^4.24.5",
"pixi.js": "^7.4.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -579,6 +580,14 @@
"node": ">= 8"
}
},
"node_modules/@panva/hkdf": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.1.1.tgz",
"integrity": "sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==",
"funding": {
"url": "https://github.com/sponsors/panva"
}
},
"node_modules/@pixi/app": {
"version": "7.3.2",
"resolved": "https://registry.npmjs.org/@pixi/app/-/app-7.3.2.tgz",
@@ -1646,6 +1655,14 @@
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
"node_modules/cookie": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -3274,6 +3291,14 @@
"@pkgjs/parseargs": "^0.11.0"
}
},
"node_modules/jose": {
"version": "4.15.4",
"resolved": "https://registry.npmjs.org/jose/-/jose-4.15.4.tgz",
"integrity": "sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ==",
"funding": {
"url": "https://github.com/sponsors/panva"
}
},
"node_modules/js-cookie": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
@@ -3435,7 +3460,6 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"dependencies": {
"yallist": "^4.0.0"
},
@@ -3569,6 +3593,33 @@
}
}
},
"node_modules/next-auth": {
"version": "4.24.5",
"resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.5.tgz",
"integrity": "sha512-3RafV3XbfIKk6rF6GlLE4/KxjTcuMCifqrmD+98ejFq73SRoj2rmzoca8u764977lH/Q7jo6Xu6yM+Re1Mz/Og==",
"dependencies": {
"@babel/runtime": "^7.20.13",
"@panva/hkdf": "^1.0.2",
"cookie": "^0.5.0",
"jose": "^4.11.4",
"oauth": "^0.9.15",
"openid-client": "^5.4.0",
"preact": "^10.6.3",
"preact-render-to-string": "^5.1.19",
"uuid": "^8.3.2"
},
"peerDependencies": {
"next": "^12.2.5 || ^13 || ^14",
"nodemailer": "^6.6.5",
"react": "^17.0.2 || ^18",
"react-dom": "^17.0.2 || ^18"
},
"peerDependenciesMeta": {
"nodemailer": {
"optional": true
}
}
},
"node_modules/next/node_modules/postcss": {
"version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
@@ -3611,6 +3662,11 @@
"node": ">=0.10.0"
}
},
"node_modules/oauth": {
"version": "0.9.15",
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
"integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -3619,6 +3675,14 @@
"node": ">=0.10.0"
}
},
"node_modules/object-hash": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
"integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==",
"engines": {
"node": ">= 6"
}
},
"node_modules/object-inspect": {
"version": "1.12.3",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
@@ -3727,6 +3791,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/oidc-token-hash": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz",
"integrity": "sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==",
"engines": {
"node": "^10.13.0 || >=12.0.0"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -3736,6 +3808,20 @@
"wrappy": "1"
}
},
"node_modules/openid-client": {
"version": "5.6.4",
"resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.6.4.tgz",
"integrity": "sha512-T1h3B10BRPKfcObdBklX639tVz+xh34O7GjofqrqiAQdm7eHsQ00ih18x6wuJ/E6FxdtS2u3FmUGPDeEcMwzNA==",
"dependencies": {
"jose": "^4.15.4",
"lru-cache": "^6.0.0",
"object-hash": "^2.2.0",
"oidc-token-hash": "^5.0.3"
},
"funding": {
"url": "https://github.com/sponsors/panva"
}
},
"node_modules/optionator": {
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
@@ -4430,6 +4516,26 @@
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"dev": true
},
"node_modules/preact": {
"version": "10.19.3",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.19.3.tgz",
"integrity": "sha512-nHHTeFVBTHRGxJXKkKu5hT8C/YWBkPso4/Gad6xuj5dbptt9iF9NZr9pHbPhBrnT2klheu7mHTxTZ/LjwJiEiQ==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
}
},
"node_modules/preact-render-to-string": {
"version": "5.2.6",
"resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz",
"integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==",
"dependencies": {
"pretty-format": "^3.8.0"
},
"peerDependencies": {
"preact": ">=10"
}
},
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -4466,6 +4572,11 @@
"node": ">=6.0.0"
}
},
"node_modules/pretty-format": {
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz",
"integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew=="
},
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@@ -5503,6 +5614,14 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"dev": true
},
"node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -5697,8 +5816,7 @@
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/yocto-queue": {
"version": "0.1.0",

View File

@@ -17,6 +17,7 @@
"@pixi/react": "^7.1.1",
"js-cookie": "^3.0.5",
"next": "^14.1.0",
"next-auth": "^4.24.5",
"pixi.js": "^7.4.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",

View File

@@ -1,72 +0,0 @@
import Cookies from 'js-cookie';
import { getRequest, postRequest } from '.';
import { RegisterUser, ResponseAuth } from './auth.types';
export async function login(email: string, password: string): Promise<ResponseAuth | undefined> {
const response = await postRequest('auth/login', { email, password });
if (response?.status === 200) {
return response.json();
} else {
return undefined;
}
}
export async function register(user: RegisterUser): Promise<boolean> {
const response = await postRequest('auth/register', user);
if (response?.status === 201) {
return true;
} else {
return false;
}
}
export async function logout() {
return await postRequest('auth/logout', {});
}
export async function refresh(refresh_token_rotation?: boolean): Promise<ResponseAuth | undefined> {
const response = await getRequest('auth/refresh', { refresh_token_rotation });
if (response?.status === 200) {
return response.json();
} else {
return undefined;
}
}
export async function me(): Promise<ResponseAuth | undefined> {
const response = await getRequest('auth/me');
if (response?.status === 200) {
return response.json();
} else {
return undefined;
}
}
export async function hasSession(): Promise<boolean> {
const response = await getRequest('auth/session');
if (response?.status === 200) {
return response?.json();
} else {
return false;
}
}
/**
* Refreshes the logged_in cookie every interval. By default, the interval is 14 minutes.
* @param interval
* @returns interval id
*/
export function refreshLoggedIn(interval = 840000) {
let loggedIn = Cookies.get('logged_in');
const id = setInterval(async () => {
const cookie = Cookies.get('logged_in');
if (cookie != loggedIn) {
loggedIn = cookie;
const response = await refresh(true);
if (!response) {
Cookies.remove('logged_in');
}
}
}, interval);
return id;
}

View File

@@ -0,0 +1,32 @@
import { getRequest, postRequest } from '..';
import { BaseResponse, RegisterUser } from './types';
export async function login(email: string, password: string): Promise<BaseResponse> {
const response = await postRequest('auth/login', { email, password });
const data = await response.json();
return { data, status: response.status };
}
export async function register(user: RegisterUser): Promise<BaseResponse> {
const response = await postRequest('auth/register', user);
const data = await response.json();
return { data, status: response.status };
}
export async function logout(): Promise<BaseResponse> {
const response = await postRequest('auth/logout', {});
const data = await response.json();
return { data, status: response.status };
}
export async function refresh(): Promise<BaseResponse> {
const response = await getRequest('auth/refresh');
const data = await response.json();
return { data, status: response.status };
}
export async function me(): Promise<BaseResponse> {
const response = await getRequest('auth/me');
const data = await response.json();
return { data, status: response.status };
}

View File

@@ -1,8 +1,18 @@
export interface ResponseAuth {
token: string;
import { ErrorResponse } from "..";
export interface AuthResponse {
id: string;
user: User;
}
// AuthResponse can be either a success or an error
export type AuthType = AuthResponse | ErrorResponse;
export interface BaseResponse {
data: AuthType;
status: number;
}
export interface RegisterUser {
email: string;
password: string;

View File

@@ -1,5 +1,5 @@
import { APIResponse, getRequest, postRequest } from '.';
import { GuildChannel, GuildInfo } from './guilds.types';
import { APIResponse, getRequest, postRequest } from '..';
import { GuildChannel, GuildInfo } from './types';
export async function getGuilds(): Promise<GuildInfo[]> {
const response = await getRequest('guilds');

View File

@@ -46,6 +46,11 @@ export interface APIResponse<T> {
metadata: Metadata;
}
export interface ErrorResponse {
status: string;
message: string;
}
export interface Metadata {
limit: number;
page: number;

View File

@@ -1,5 +1,5 @@
import { getRequest } from '.';
import { GetSpellsResponse } from './spells.types';
import { getRequest } from '..';
import { GetSpellsResponse } from './types';
interface GetSpellsParams {
name?: string;

View File

@@ -1,4 +1,4 @@
import { Metadata } from '.';
import { Metadata } from '..';
export interface Spell {
id: string;

View File

@@ -1,4 +1,4 @@
import { getRequest, postRequest } from '.';
import { getRequest, postRequest } from '..';
export async function getPicture(): Promise<Blob | undefined> {
const response = await getRequest('users/picture');

View File

@@ -8,6 +8,7 @@ import { Notifications } from '@mantine/notifications';
import 'styles/globals.css';
import '@mantine/core/styles.css';
import '@mantine/notifications/styles.css';
import Loader from '@/components/Loader';
export const metadata = {
title: 'Siren',
@@ -27,8 +28,10 @@ export default function RootLayout({ children }: { children: React.ReactNode })
<MantineProvider>
<Notifications />
<ModalsProvider>
<Header />
<Box>{children}</Box>
<Loader>
<Header />
<Box>{children}</Box>
</Loader>
</ModalsProvider>
</MantineProvider>
</RecoilRootWrapper>

View File

@@ -12,12 +12,10 @@ export default function Auth(Component: any, adminOnly = false) {
const isAdmin = useRecoilValue(isAdminState);
function isAuthenticated() {
console.log('hasUser', hasUser, 'adminOnly', adminOnly, 'isAdmin', isAdmin)
return hasUser && (adminOnly ? isAdmin : true);
}
useEffect(() => {
console.log('isAuthenticated', isAuthenticated());
if (!isAuthenticated) {
router.push('/');
}

View File

@@ -1,7 +1,8 @@
'use client';
import { ErrorResponse } from '@/api';
import { login, register } from '@/api/auth';
import { User } from '@/api/auth.types';
import { AuthResponse, User } from '@/api/auth.types';
import {
Modal,
Container,
@@ -22,10 +23,9 @@ interface HeaderModalProps {
type?: string;
toggle: any;
setUser: (user: User) => void;
setRefreshId: (id: NodeJS.Timeout) => void;
}
export function HeaderModal({ type, toggle, setUser, setRefreshId }: HeaderModalProps) {
export function HeaderModal({ type, toggle, setUser }: HeaderModalProps) {
function passwordValidator(value: string) {
if (value.trim().length < 10) {
return 'Password must be at least 10 characters';
@@ -144,22 +144,24 @@ export function HeaderModal({ type, toggle, setUser, setRefreshId }: HeaderModal
});
if (registerResponse) {
const loginResponse = await login(values.email, values.password);
if (loginResponse) {
setUser(loginResponse.user);
if (loginResponse.status == 200) {
const user = (loginResponse.data as AuthResponse).user;
setUser(user);
onClose();
notifications.update({
id,
title: `Account created`,
message: `Welcome ${loginResponse.user.first_name}!`,
message: `Welcome ${user.first_name}!`,
color: 'green',
autoClose: 2000,
loading: false
});
} else {
const error = loginResponse.data as ErrorResponse;
notifications.update({
id,
title: `Unable to Login`,
message: `Please try again.`,
message: `${error.message}`,
color: 'red',
autoClose: 2000,
loading: false
@@ -219,13 +221,14 @@ export function HeaderModal({ type, toggle, setUser, setRefreshId }: HeaderModal
<form
onSubmit={loginForm.onSubmit(async (values) => {
const response = await login(values.email, values.password);
if (response) {
setUser(response.user);
if (response.status == 200) {
setUser((response.data as AuthResponse).user);
onClose();
} else {
const error = response.data as ErrorResponse;
notifications.show({
title: `Unable to Login`,
message: `Please try again.`,
message: `${error.message}`,
color: 'red',
autoClose: 2000
});

View File

@@ -6,7 +6,7 @@ import './header.css';
import { Avatar, Button, Card, Center, FileButton, Grid, Group, Menu, Text, UnstyledButton } from '@mantine/core';
import Cookies from 'js-cookie';
import { useEffect, useState } from 'react';
import { hasSession, logout, refresh, refreshLoggedIn } from '@/api/auth';
import { logout } from '@/api/auth';
import { useToggle } from '@mantine/hooks';
import { HeaderModal } from './HeaderModal';
import { HeaderItem, headerItems } from './headerItems';
@@ -21,7 +21,6 @@ export default function Header() {
const [modalType, toggle] = useToggle([undefined, 'login', 'register', 'reset']);
const [headers] = useState<HeaderItem[]>(headerItems);
const [user, setUser] = useRecoilState(userState);
const [refreshId, setRefreshId] = useState<NodeJS.Timeout | undefined>(undefined);
const [profilePicture, setProfilePicture] = useState<File | null>(null);
const router = useRouter();
@@ -32,9 +31,6 @@ export default function Header() {
}, [user]);
function updateUser(user?: User) {
if (!refreshId) {
setRefreshId(refreshLoggedIn());
}
if (user) {
getPicture().then((response) => {
if (response) {
@@ -161,12 +157,7 @@ export default function Header() {
await logout();
Cookies.remove('logged_in');
setUser(undefined);
clearInterval(refreshId);
setRefreshId(undefined);
setProfilePicture(null);
if (refreshId) {
clearInterval(refreshId);
}
}}
>
Logout
@@ -203,7 +194,6 @@ export default function Header() {
setUser(u);
updateUser(u);
}}
setRefreshId={setRefreshId}
/>
</>
);

View File

@@ -1,6 +1,6 @@
'use client';
import { hasSession, refresh } from "@/api/auth";
import { refresh } from "@/api/auth";
import { userState } from "@/state/auth";
import { Skeleton } from "@mantine/core";
import { useEffect, useState } from "react";
@@ -11,26 +11,20 @@ export default function Loading({ children }: { children: React.ReactNode }) {
const [user, setUser] = useRecoilState(userState);
useEffect(() => {
if (!user) {
hasSession().then((response) => {
if (response) {
refresh().then((response) => {
if (response) {
setUser(response.user);
setLoading(false);
} else {
setLoading(false);
}
});
} else {
setLoading(false);
}
});
} else {
setLoading(false);
}
checkUser();
}, []);
async function checkUser() {
setLoading(true);
if (!user) {
const response = await refresh();
if (response) {
setUser(response.user);
}
}
setLoading(false);
}
if (loading) {
return <Skeleton height={'100%'} />;
} else {

View File

@@ -1,6 +1,6 @@
'use client';
import Cookies from "js-cookie";
import { NextRequest } from "next/server";
export default function middleware(request: NextRequest) {
console.log(Cookies.get('user_id'))
}

View File

@@ -30,7 +30,7 @@
"./src/*"
],
"@api/*": [
"src/api"
"src/app/api"
],
"@app/*": [
"./src/app/*"