bump deps

This commit is contained in:
sk1982 2024-04-12 05:17:41 -04:00
parent c00c429ede
commit e1ec5f275f
23 changed files with 200 additions and 1418 deletions

1
empty.cjs Normal file
View File

@ -0,0 +1 @@
module.exports = {};

View File

@ -56,12 +56,19 @@ module.exports = bundleAnalyzer({ enabled: !!process.env.ANALYZE })({
additionalData: `$asset-url: "${baseAssetUrl}";` additionalData: `$asset-url: "${baseAssetUrl}";`
}, },
experimental: { experimental: {
instrumentationHook: true instrumentationHook: true,
serverComponentsExternalPackages: ['kysely', 'mysql2', 'bcrypt']
}, },
productionBrowserSourceMaps: true, productionBrowserSourceMaps: true,
webpack: config => { webpack: (config, { nextRuntime }) => {
config.externals = [...config.externals, 'bcrypt', 'mysql2'];
config.resolve.alias['resize-observer-polyfill'] = path.resolve(__dirname, 'resize-observer.cjs'); config.resolve.alias['resize-observer-polyfill'] = path.resolve(__dirname, 'resize-observer.cjs');
if (nextRuntime === 'edge') {
config.resolve.alias['mysql2'] = path.resolve(__dirname, 'empty.cjs');
config.resolve.alias['kysely'] = path.resolve(__dirname, 'empty.cjs');
config.resolve.alias['bcrypt'] = path.resolve(__dirname, 'empty.cjs');
config.resolve.alias['crypto'] = path.resolve(__dirname, 'empty.cjs');
config.resolve.alias['node:crypto'] = path.resolve(__dirname, 'empty.cjs');
}
return config; return config;
} }
}); });

1483
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -17,21 +17,21 @@
}, },
"dependencies": { "dependencies": {
"@heroicons/react": "^2.1.3", "@heroicons/react": "^2.1.3",
"@next/bundle-analyzer": "^14.1.4", "@next/bundle-analyzer": "^14.2.0",
"@nextui-org/react": "^2.2.10", "@nextui-org/react": "^2.2.10",
"@tanstack/react-virtual": "^3.2.0", "@tanstack/react-virtual": "^3.2.1",
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"clsx": "^2.1.0", "clsx": "^2.1.0",
"db-migrate": "^0.11.14", "db-migrate": "^0.11.14",
"db-migrate-mysql": "^2.3.2", "db-migrate-mysql": "^2.3.2",
"deep-is": "^0.1.4", "deep-is": "^0.1.4",
"framer-motion": "^10.18.0", "framer-motion": "^11.0.28",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"kysely": "^0.27.3", "kysely": "^0.27.3",
"mysql2": "^3.9.2", "mysql2": "^3.9.4",
"next": "14.1.4", "next": "14.2.0",
"next-auth": "^5.0.0-beta.15", "next-auth": "^5.0.0-beta.15",
"next-client-cookies": "^1.1.0", "next-client-cookies": "^1.1.1",
"next-themes": "^0.3.0", "next-themes": "^0.3.0",
"react": "^18", "react": "^18",
"react-day-picker": "^8.10.0", "react-day-picker": "^8.10.0",
@ -40,13 +40,13 @@
"react-icons": "^5.0.1", "react-icons": "^5.0.1",
"react-resizable": "^3.0.5", "react-resizable": "^3.0.5",
"react-swipeable": "^7.0.1", "react-swipeable": "^7.0.1",
"sass": "^1.72.0", "sass": "^1.75.0",
"tailwind-merge": "^2.2.2", "tailwind-merge": "^2.2.2",
"tailwindcss-text-fill-stroke": "^2.0.0-beta.1", "tailwindcss-text-fill-stroke": "^2.0.0-beta.1",
"usehooks-ts": "^3.0.2" "usehooks-ts": "^3.1.0"
}, },
"devDependencies": { "devDependencies": {
"@dotenvx/dotenvx": "^0.26.0", "@dotenvx/dotenvx": "^0.32.0",
"@types/bcrypt": "^5.0.2", "@types/bcrypt": "^5.0.2",
"@types/jsonwebtoken": "^9.0.6", "@types/jsonwebtoken": "^9.0.6",
"@types/node": "^20", "@types/node": "^20",
@ -54,15 +54,14 @@
"@types/react-dom": "^18", "@types/react-dom": "^18",
"@types/react-grid-layout": "^1.3.5", "@types/react-grid-layout": "^1.3.5",
"@types/react-resizable": "^3.0.7", "@types/react-resizable": "^3.0.7",
"@types/react-virtualized": "^9.21.29",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"eslint": "^8", "eslint": "^8",
"eslint-config-next": "14.1.4", "eslint-config-next": "14.2.0",
"kysely-codegen": "^0.14.1", "kysely-codegen": "^0.14.2",
"postcss": "^8", "postcss": "^8",
"tailwindcss": "^3.4.1", "tailwindcss": "^3.4.3",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"type-fest": "^4.14.0", "type-fest": "^4.15.0",
"typescript": "^5" "typescript": "^5"
} }
} }

View File

@ -44,13 +44,15 @@ type LoginOptions = {
export const login = async (options?: LoginOptions) => { export const login = async (options?: LoginOptions) => {
if (!options) if (!options)
return signIn(); return { redirect: await signIn(undefined, { redirect: false }) as string };
try { try {
return await signIn('credentials', { const res = await signIn('credentials', {
...options, ...options,
redirectTo: options?.redirectTo ?? '/' redirectTo: options.redirectTo ?? '/',
redirect: false,
}); });
return { redirect: res };
} catch (e) { } catch (e) {
if (e instanceof AuthError) { if (e instanceof AuthError) {
if (e.type === 'CredentialsSignin') if (e.type === 'CredentialsSignin')
@ -63,8 +65,11 @@ export const login = async (options?: LoginOptions) => {
} }
}; };
export const logout = async (options: { redirectTo?: string, redirect?: boolean }) => { export const logout = async (options: { redirectTo?: string }) => {
return signOut(options); return await signOut({
...options,
redirect: false
});
}; };
export const register = async (formData: FormData) => { export const register = async (formData: FormData) => {

View File

@ -124,7 +124,7 @@ export async function getPlaylog(opts: GetPlaylogOptions) {
.select(({ fn }) => fn.countAll().as('total')) .select(({ fn }) => fn.countAll().as('total'))
.executeTakeFirstOrThrow()).total); .executeTakeFirstOrThrow()).total);
return { data: playlog, total }; return { data: structuredClone(playlog), total };
} }
export type ChuniPlaylog = Awaited<ReturnType<typeof getPlaylog>>; export type ChuniPlaylog = Awaited<ReturnType<typeof getPlaylog>>;

View File

@ -3,17 +3,15 @@
import { GeneratedDB, db } from '@/db'; import { GeneratedDB, db } from '@/db';
import { getUser, requireUser } from './auth'; import { getUser, requireUser } from './auth';
import { notFound } from 'next/navigation'; import { notFound } from 'next/navigation';
import { CompiledQuery, Transaction, sql } from 'kysely'; import { Transaction, sql } from 'kysely';
import { syncUserFriends, withChuniRivalCount } from '@/data/friend'; import { syncUserFriends, withChuniRivalCount } from '@/data/friend';
import { SqlBool } from 'kysely';
import { Exact } from 'type-fest';
import { revalidatePath } from 'next/cache'; import { revalidatePath } from 'next/cache';
export const getFriendRequests = async () => { export const getFriendRequests = async () => {
const user = await getUser(); const user = await getUser();
if (!user) return []; if (!user) return [];
return db.selectFrom('actaeon_friend_requests as req') return structuredClone(await db.selectFrom('actaeon_friend_requests as req')
.where('user', '=', user.id) .where('user', '=', user.id)
.innerJoin('aime_user as u', 'u.id', 'req.friend') .innerJoin('aime_user as u', 'u.id', 'req.friend')
.innerJoin('actaeon_user_ext as ext', 'ext.userId', 'u.id') .innerJoin('actaeon_user_ext as ext', 'ext.userId', 'u.id')
@ -23,7 +21,7 @@ export const getFriendRequests = async () => {
'ext.uuid as userUuid' 'ext.uuid as userUuid'
]) ])
.orderBy('req.createdDate desc') .orderBy('req.createdDate desc')
.execute(); .execute());
}; };
export type FriendRequest = Awaited<ReturnType<typeof getFriendRequests>>[number]; export type FriendRequest = Awaited<ReturnType<typeof getFriendRequests>>[number];

View File

@ -157,5 +157,5 @@ export const createMachine = async ({ arcade, update }: { arcade: number, update
revalidatePath('/arcade', 'page'); revalidatePath('/arcade', 'page');
revalidatePath('/arcade/[arcadeId]', 'page'); revalidatePath('/arcade/[arcadeId]', 'page');
return { error: false, message: '', data: await getArcadeCabs({ arcade, user, permissions: arcadePermissions }) }; return { error: false, message: '', data: structuredClone(await getArcadeCabs({ arcade, user, permissions: arcadePermissions })) };
} }

View File

@ -40,7 +40,8 @@ export const LoginCard = ({ initialError, referer, callback }: LoginCardProps) =
}) })
.then(res => { .then(res => {
if (res?.error) if (res?.error)
setError(res.message) return setError(res.message)
setTimeout(() => location.href = res.redirect, 10);
}) })
.finally(() => setLoading(false)); .finally(() => setLoading(false));
}; };

View File

@ -18,8 +18,8 @@ export default async function ArcadeDetailPage({ params }: { params: { arcadeId:
const [users, cabs, links] = await Promise.all([ const [users, cabs, links] = await Promise.all([
getArcadeUsers({ arcade: arcade.id, permissions: arcade.permissions, user }), getArcadeUsers({ arcade: arcade.id, permissions: arcade.permissions, user }),
getArcadeCabs({ arcade: arcade.id, permissions: arcade.permissions, user }), getArcadeCabs({ arcade: arcade.id, permissions: arcade.permissions, user }).then(d => structuredClone(d)),
getArcadeInviteLinks({ arcade: arcade.id, permissions: arcade.permissions, user }) getArcadeInviteLinks({ arcade: arcade.id, permissions: arcade.permissions, user }).then(d => structuredClone(d))
]); ]);
return (<ArcadeDetail users={users} arcade={arcade} cabs={cabs} links={links} />) return (<ArcadeDetail users={users} arcade={arcade} cabs={cabs} links={links} />)

View File

@ -17,9 +17,9 @@ export default async function ChuniDashboard() {
return (<ChuniNoProfile />); return (<ChuniNoProfile />);
const [profile, rating, playlog] = await Promise.all([ const [profile, rating, playlog] = await Promise.all([
getUserData(user), getUserData(user).then(d => structuredClone(d)),
getUserRating(user), getUserRating(user).then(d => structuredClone(d)),
getPlaylog({ limit: 72 }) getPlaylog({ limit: 72 }).then(d => structuredClone(d))
]); ]);
return (<div className="flex h-full flex-col md:flex-row"> return (<div className="flex h-full flex-col md:flex-row">

View File

@ -10,7 +10,7 @@ export default async function ChuniMusicDetailPage({ params }: { params: { music
return notFound(); return notFound();
const [music, playlog] = await Promise.all([ const [music, playlog] = await Promise.all([
getMusic(musicId), getMusic(musicId).then(d => structuredClone(d)),
getPlaylog({ musicId, limit: 500 }) getPlaylog({ musicId, limit: 500 })
]); ]);

View File

@ -6,6 +6,6 @@ export default async function ChuniMusicPage() {
const music = await getMusic(); const music = await getMusic();
return ( return (
<ChuniMusicList music={music} /> <ChuniMusicList music={structuredClone(music)} />
); );
} }

View File

@ -19,8 +19,8 @@ export default async function ChuniUserboxPage() {
if (!user?.chuni) if (!user?.chuni)
return (<ChuniNoProfile />); return (<ChuniNoProfile />);
const profile = await getUserData(user); const profile = structuredClone(await getUserData(user));
const userboxItems = await getUserboxItems(user, profile); const userboxItems = structuredClone(await getUserboxItems(user, profile));
return (<ChuniUserbox profile={profile} userboxItems={userboxItems} />); return (<ChuniUserbox profile={profile} userboxItems={userboxItems} />);
} }

View File

@ -9,13 +9,13 @@ export const dynamic = 'force-dynamic';
export default async function DashboardPage() { export default async function DashboardPage() {
const [user, status] = await Promise.all([ const [user, status] = await Promise.all([
getUser(), getUser(),
getServerStatus() getServerStatus().then(d => structuredClone(d))
]); ]);
if (!user) return (<Dashboard serverStatus={status} />) if (!user) return (<Dashboard serverStatus={status} />)
const [chuniProfile, dashboard] = await Promise.all([ const [chuniProfile, dashboard] = await Promise.all([
getUserData(user), getUserData(user).then(d => structuredClone(d)),
getDashboard(user) getDashboard(user)
]); ]);

View File

@ -6,7 +6,7 @@ export const dynamic = 'force-dynamic';
export default async function FriendsPage() { export default async function FriendsPage() {
const user = await requireUser(); const user = await requireUser();
const friends = await getFriends(user.id); const friends = structuredClone(await getFriends(user.id));
return (<Friends friends={friends} />); return (<Friends friends={friends} />);
}; };

View File

@ -305,7 +305,8 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
</div> </div>
</DropdownItem> </DropdownItem>
<DropdownItem className="p-0" color="danger" variant="flat"> <DropdownItem className="p-0" color="danger" variant="flat">
<div className="w-full h-full block px-2 py-1.5 text-danger" onClick={() => logout({ redirectTo: '/' })}> <div className="w-full h-full block px-2 py-1.5 text-danger" onClick={() => logout({ redirectTo: '/' })
.then(r => setTimeout(() => location.href = r.redirect, 10))}>
Logout Logout
</div> </div>
</DropdownItem> </DropdownItem>
@ -314,7 +315,8 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
</> : </> :
<> <>
<ThemeSwitcherDropdown /> <ThemeSwitcherDropdown />
<Button size="sm" className="ml-2" color="primary" onClick={() => login()}> <Button size="sm" className="ml-2" color="primary" onClick={() => login()
.then(r => setTimeout(() => location.href = r.redirect, 10))}>
Login Login
</Button> </Button>
</> </>
@ -397,7 +399,8 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
</Button> </Button>
</Link>} </Link>}
</div> </div>
<Button color="primary" className="w-full flex-shrink-0" onClick={() => user ? logout({ redirectTo: '/' }) : login()}> <Button color="primary" className="w-full flex-shrink-0" onClick={() => (user ? logout({ redirectTo: '/' }) : login())
.then(r => setTimeout(() => location.href = r.redirect!, 10))}>
{user ? 'Logout' : 'Login'} {user ? 'Logout' : 'Login'}
</Button> </Button>
</div> </div>
@ -457,7 +460,8 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
</div> </div>
</section> </section>
<Button className="mt-auto mx-3 flex-shrink-0" color="danger" onPress={() => logout({ redirectTo: '/' })}> <Button className="mt-auto mx-3 flex-shrink-0" color="danger" onPress={() => logout({ redirectTo: '/' })
.then(r => setTimeout(() => location.href = r.redirect, 10))}>
Logout Logout
</Button> </Button>
</div> </div>

View File

@ -7,7 +7,7 @@ import { getGlobalConfig } from '@/config';
export default async function SettingsPage() { export default async function SettingsPage() {
const user = await requireUser(); const user = await requireUser();
const cards = await getCards(user.id); const cards = structuredClone(await getCards(user.id));
return (<div className="w-full flex items-center justify-center"> return (<div className="w-full flex items-center justify-center">
<div className="w-full max-w-full sm:max-w-5xl flex flex-col gap-2 2xl:max-w-screen-4xl 2xl:grid grid-cols-12"> <div className="w-full max-w-full sm:max-w-5xl flex flex-col gap-2 2xl:max-w-screen-4xl 2xl:grid grid-cols-12">

View File

@ -15,7 +15,7 @@ export default async function TeamDetailPage({ params }: { params: { teamId: str
const [users, links] = await Promise.all([ const [users, links] = await Promise.all([
getTeamUsers({ user, team }), getTeamUsers({ user, team }),
getTeamInviteLinks({ user, team }) getTeamInviteLinks({ user, team }).then(d => structuredClone(d))
]); ]);
if (!team.visible) if (!team.visible)

View File

@ -9,7 +9,7 @@ export const dynamic = 'force-dynamic';
export default async function UserProfilePage({ params }: { params: { userId: string; }; }) { export default async function UserProfilePage({ params }: { params: { userId: string; }; }) {
const viewingUser = await getUser(); const viewingUser = await getUser();
const user = await withUsersVisibleTo(viewingUser) const user = structuredClone(await withUsersVisibleTo(viewingUser)
.selectFrom('aime_user as u') .selectFrom('aime_user as u')
.innerJoin('actaeon_user_ext as ext', 'ext.userId', 'u.id') .innerJoin('actaeon_user_ext as ext', 'ext.userId', 'u.id')
.where('ext.uuid', '=', params.userId) .where('ext.uuid', '=', params.userId)
@ -22,7 +22,7 @@ export default async function UserProfilePage({ params }: { params: { userId: st
'last_login_date', 'last_login_date',
userIsVisible('u.id').as('visible') userIsVisible('u.id').as('visible')
]) ])
.executeTakeFirst(); .executeTakeFirst());
if (!user) if (!user)
return notFound(); return notFound();
@ -32,18 +32,18 @@ export default async function UserProfilePage({ params }: { params: { userId: st
.where('user1', '=', user.id) .where('user1', '=', user.id)
.where('user2', '=', viewingUser?.id!) .where('user2', '=', viewingUser?.id!)
.select('chuniRival') .select('chuniRival')
.executeTakeFirst(), .executeTakeFirst().then(d => structuredClone(d)),
db.selectFrom('actaeon_friend_requests') db.selectFrom('actaeon_friend_requests')
.where('user', '=', user.id) .where('user', '=', user.id)
.where('friend', '=', viewingUser?.id!) .where('friend', '=', viewingUser?.id!)
.select('user') .select('user')
.executeTakeFirst() .executeTakeFirst().then(d => structuredClone(d))
]); ]);
if (!user.visible) if (!user.visible)
return (<UserProfile friend={friend} pendingFriend={!!pendingFriend} user={user as UserProfile<false>}/>); return (<UserProfile friend={friend} pendingFriend={!!pendingFriend} user={user as UserProfile<false>}/>);
const chuniProfile = await getChuniUserData(user); const chuniProfile = structuredClone(await getChuniUserData(user));
return (<UserProfile friend={friend} pendingFriend={!!pendingFriend} user={user as UserProfile<true>} chuniProfile={chuniProfile} />); return (<UserProfile friend={friend} pendingFriend={!!pendingFriend} user={user as UserProfile<true>} chuniProfile={chuniProfile} />);
} }

View File

@ -3,7 +3,7 @@ import CredentialsProvider from 'next-auth/providers/credentials';
import { db, GeneratedDB } from '@/db'; import { db, GeneratedDB } from '@/db';
import { DBUserPayload } from '@/types/user'; import { DBUserPayload } from '@/types/user';
import { cache } from 'react'; import { cache } from 'react';
import { SelectQueryBuilder, sql } from 'kysely'; import { SelectQueryBuilder } from 'kysely';
import { AimeUser } from '@/types/db'; import { AimeUser } from '@/types/db';
import crypto from 'crypto'; import crypto from 'crypto';
import { createActaeonTeamsFromExistingTeams } from './data/team'; import { createActaeonTeamsFromExistingTeams } from './data/team';
@ -40,7 +40,7 @@ else if (['1', 'true', 'yes'].includes(process.env.COOKIE_SECURE?.toLowerCase()!
const nextAuth = NextAuth({ const nextAuth = NextAuth({
pages: { pages: {
signIn: `${basePath}/auth/login` signIn: `${basePath}/auth/login/`
}, },
...config, ...config,
basePath: `${basePath}/api/auth/`, basePath: `${basePath}/api/auth/`,

View File

@ -11,7 +11,8 @@ export const PrivateVisibilityError = () => {
<LockClosedIcon className="w-48 mb-10" /> <LockClosedIcon className="w-48 mb-10" />
<header className="text-2xl font-semibold">This page is private.</header> <header className="text-2xl font-semibold">This page is private.</header>
{!user ? <span>You may be able to access it by&nbsp; {!user ? <span>You may be able to access it by&nbsp;
<span className="underline hover:text-secondary transition cursor-pointer" onClick={() => login()}>logging in.</span> <span className="underline hover:text-secondary transition cursor-pointer" onClick={() => login()
.then(r => setTimeout(() => location.href = r.redirect!, 10))}>logging in.</span>
</span> : <span>You do not have permission to view this page.</span>} </span> : <span>You do not have permission to view this page.</span>}
</main>); </main>);
} }

View File

@ -81,8 +81,5 @@ export async function register() {
createActaeonTeamsFromExistingTeams().catch(console.error), createActaeonTeamsFromExistingTeams().catch(console.error),
createActaeonFriendsFromExistingFriends().catch(console.error) createActaeonFriendsFromExistingFriends().catch(console.error)
]); ]);
} else if (process.env.NEXT_RUNTIME === 'edge') {
(globalThis as any).bcrypt = {};
(globalThis as any).mysql2 = {};
} }
} }