diff --git a/prisma/migrations/20230913142372_sqloptimization/migration.sql b/prisma/migrations/20230913142372_sqloptimization/migration.sql new file mode 100644 index 0000000..869fe88 --- /dev/null +++ b/prisma/migrations/20230913142372_sqloptimization/migration.sql @@ -0,0 +1,21 @@ +-- AlterTable +alter table banned_user + add constraint banned_user_user_id_fk + foreign key (user_id) references user (id) + on delete cascade; + +alter table game_sessions + modify user_id int not null; + +alter table game_sessions + add constraint game_sessions_user_id_fk + foreign key (user_id) references user (id) + on delete cascade; + +alter table riitag.game_sessions + modify game_id int unsigned not null; + +alter table game_sessions + add constraint game_sessions_game_game_pk_fk + foreign key (game_id) references game (game_pk); + diff --git a/prisma/schema.prisma b/prisma/schema.prisma index cab8e37..cb4f7e8 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -23,14 +23,15 @@ model account { } model game { - game_pk Int @id @default(autoincrement()) @db.UnsignedInt - game_id String @db.VarChar(50) - console String @db.VarChar(9) - name String? @db.VarChar(255) - playcount Int @default(1) @db.UnsignedInt - first_played DateTime @default(now()) - last_played DateTime @default(now()) @updatedAt - playlog playlog[] + game_pk Int @id @default(autoincrement()) @db.UnsignedInt + game_id String @db.VarChar(50) + console String @db.VarChar(9) + name String? @db.VarChar(255) + playcount Int @default(1) @db.UnsignedInt + first_played DateTime @default(now()) + last_played DateTime @default(now()) @updatedAt + game_sessions game_sessions[] + playlog playlog[] @@unique([game_id, console], map: "game_id_console") @@index([console], map: "console") @@ -67,35 +68,37 @@ model following { } model user { - id Int @id @default(autoincrement()) - username String @unique @db.VarChar(50) - role String @default("user") @db.VarChar(25) - display_name String? @db.VarChar(255) - image String? @db.VarChar(255) - randkey String? @unique @db.VarChar(200) - coins Int @default(0) @db.UnsignedInt - cover_region String @default("EN") @db.VarChar(6) - cover_type String @default("cover3D") @db.VarChar(10) - comment String? @db.VarChar(50) - overlay String @default("overlay1") @db.VarChar(20) - background String @default("riiconnect241.png") @db.VarChar(120) - flag String @default("rc24") @db.VarChar(20) - coin String @default("mario") @db.VarChar(20) - font String @default("default") @db.VarChar(50) - show_avatar Boolean @default(false) - show_mii Boolean @default(false) - mii_type String @default("guest") @db.VarChar(10) - mii_data String? @db.VarChar(8192) - cmoc_entry_no String? @db.VarChar(12) - created_at DateTime @default(now()) - updated_at DateTime @default(now()) @updatedAt - badge String? @db.VarChar(50) - isBanned Boolean @default(false) - isPublic Boolean @default(true) + id Int @id @default(autoincrement()) + username String @unique @db.VarChar(50) + role String @default("user") @db.VarChar(25) + display_name String? @db.VarChar(255) + image String? @db.VarChar(255) + randkey String? @unique @db.VarChar(200) + coins Int @default(0) @db.UnsignedInt + cover_region String @default("EN") @db.VarChar(6) + cover_type String @default("cover3D") @db.VarChar(10) + comment String? @db.VarChar(50) + overlay String @default("overlay1") @db.VarChar(20) + background String @default("riiconnect241.png") @db.VarChar(120) + flag String @default("rc24") @db.VarChar(20) + coin String @default("mario") @db.VarChar(20) + font String @default("default") @db.VarChar(50) + show_avatar Boolean @default(false) + show_mii Boolean @default(false) + mii_type String @default("guest") @db.VarChar(10) + mii_data String? @db.VarChar(8192) + cmoc_entry_no String? @db.VarChar(12) + created_at DateTime @default(now()) + updated_at DateTime @default(now()) @updatedAt + badge String? @db.VarChar(50) + isBanned Boolean @default(false) + isPublic Boolean @default(true) publicOverride Boolean? - language String @default("en") @db.VarChar(11) + language String @default("en") @db.VarChar(11) accounts account[] + banned_user banned_user? following following[] + game_sessions game_sessions[] playlog playlog[] } @@ -109,9 +112,14 @@ model events { model game_sessions { id Int @id @unique(map: "game_sessions_id_uindex") @default(autoincrement()) - user_id String @db.VarChar(64) - game_id String @db.VarChar(50) + user_id Int + game_id Int @db.UnsignedInt start_time DateTime @default(now()) @db.DateTime(0) + game game @relation(fields: [game_id], references: [game_pk], onUpdate: Restrict, map: "game_sessions_game_game_pk_fk") + user user @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: Restrict, map: "game_sessions_user_id_fk") + + @@index([user_id], map: "game_sessions_user_id_fk") + @@index([game_id], map: "game_sessions_game_game_pk_fk") } model banned_user { @@ -120,6 +128,7 @@ model banned_user { ip_address String @db.VarChar(64) reason String @db.VarChar(255) action_time DateTime @default(now()) @db.DateTime(0) + user user @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: Restrict, map: "banned_user_user_id_fk") } model moderation_log { diff --git a/src/components/user/PlayLog.jsx b/src/components/user/PlayLog.jsx index 9cd46ba..c7f23bc 100644 --- a/src/components/user/PlayLog.jsx +++ b/src/components/user/PlayLog.jsx @@ -11,6 +11,10 @@ function PlayLog ({ playlog, current }) { return duration.humanize() } + function isPlaying (gameId) { + return current && current.some((session) => session.game.game_id === gameId) + } + const [hydrated, setHydrated] = React.useState(false) React.useEffect(() => { // This forces a rerender, so the date is rendered @@ -43,7 +47,7 @@ function PlayLog ({ playlog, current }) { {log.game.name === null ? log.game.game_id : log.game.name} {log.play_time > 0 ? `${getTimeStamp(log.play_time)}` : 'Not Tracked'} {log.play_count} times - {current && current.game_id === log.game.game_id ? 'Now' : moment(log.played_on).from()} + {current.length > 0 && isPlaying(log.game.game_id) ? 'Now' : moment(log.played_on).from()} ))} diff --git a/src/components/user/PlayingStatus.jsx b/src/components/user/PlayingStatus.jsx index 8d3b3cf..b7f9bef 100644 --- a/src/components/user/PlayingStatus.jsx +++ b/src/components/user/PlayingStatus.jsx @@ -3,23 +3,25 @@ import PropTypes from 'prop-types' import { Card } from 'react-bootstrap' import moment from 'moment/moment' -function PlayingStatus ({ session, game }) { +function PlayingStatus ({ session, games: sessions }) { return ( - - Now Playing - - - - + sessions.map((session, index) => ( + + Now Playing + + + + + )) ) } PlayingStatus.propTypes = { session: PropTypes.object.isRequired, - game: PropTypes.object.isRequired + game: PropTypes.array.isRequired } export default PlayingStatus diff --git a/src/pages/edit.jsx b/src/pages/edit.jsx index 906dbc6..3ae0278 100644 --- a/src/pages/edit.jsx +++ b/src/pages/edit.jsx @@ -23,20 +23,32 @@ import LanguageContext from '@/components/shared/LanguageContext' import AppNavbar from '@/components/shared/AppNavbar' export const getServerSideProps = withSession(async ({ req }) => { - const username = req.session?.username + // get the current user session + const sessionAccount = req.session?.username - const loggedInUser = username != null + const session = sessionAccount != null ? await prisma.user.findUnique({ where: { - username + username: sessionAccount }, select: { - language: true + language: true, + display_name: true, + cover_region: true, + cover_type: true, + comment: true, + overlay: true, + background: true, + flag: true, + coin: true, + font: true, + show_avatar: true, + show_mii: true } }) - : { role: 'guest' } + : null - if (!username) { + if (!sessionAccount) { return { redirect: { destination: '/', @@ -45,26 +57,7 @@ export const getServerSideProps = withSession(async ({ req }) => { } } - const tagInfo = await prisma.user.findUnique({ - where: { - username - }, - select: { - display_name: true, - cover_region: true, - cover_type: true, - comment: true, - overlay: true, - background: true, - flag: true, - coin: true, - font: true, - show_avatar: true, - show_mii: true - } - }) - - return { props: { tagInfo, language: loggedInUser?.language || 'en' } } + return { props: { tagInfo: session, language: session?.language || 'en' } } }) function EditPage ({ tagInfo, language }) { diff --git a/src/pages/game-leaderboard.jsx b/src/pages/game-leaderboard.jsx index cca3cb0..505069d 100644 --- a/src/pages/game-leaderboard.jsx +++ b/src/pages/game-leaderboard.jsx @@ -12,14 +12,14 @@ const limit = TOTAL_GAMES_ON_LEADERBOARD export async function getServerSideProps ({ query }) { let { page, search } = query - page = page === undefined ? 1 : Number.parseInt(page, 10) - if (Number.isNaN(page) || page <= 0) { - page = 1 - } + + page = Number.parseInt(page, 10) || 1 + + if (Number.isNaN(page) || page <= 0) page = 1 // Add logic for the search handler - let leaderboard - let totalGames + let leaderboard, totalGames + if (search) { // Call your search function here and pass the search parameter [totalGames, leaderboard] = await getGameLeaderboardSearch(page, limit, search) diff --git a/src/pages/index.jsx b/src/pages/index.jsx index 687f73c..589259b 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -17,36 +17,56 @@ import LocalizedString from '@/components/shared/LocalizedString' import AppNavbar from '@/components/shared/AppNavbar' export const getServerSideProps = withSession(async ({ req, query }) => { - const userCount = await prisma.user.count() - const playCountResult = await prisma.$queryRaw` - SELECT SUM(coins) - FROM user - ` - - const loggedInUsername = req.session?.username - const loggedInUser = await prisma.user.findFirst({ + // Get user session and their language + const session = req.session?.username + + // count users and total coins and then get 5 random users + const totalCounts = await prisma.user.aggregate({ + _sum: { + coins: true + }, + _count: { + username: true + } + }) + + const skip = Math.floor(Math.random() * totalCounts._count.username) + + // get random users with 5 or more coins and is public. + const randomUsers = await prisma.user.findMany({ + select: { + username: true, + display_name: true, + updated_at: true + }, where: { - username: loggedInUsername + coins: { + gt: 10 + }, + isPublic: true, + publicOverride: null + }, + orderBy: { + updated_at: 'desc' + }, + take: 5, + skip + }) + + const sessionUser = await prisma.user.findUnique({ + where: { + username: session }, select: { language: true } }) - const playCount = Number(playCountResult[0]['SUM(coins)']) - const randomUsers = await prisma.$queryRaw` - SELECT user.username, user.display_name, user.updated_at - FROM user - WHERE user.coins > 10 AND user.isPublic = 1 AND user.publicOverride IS NULL - ORDER BY RAND() - LIMIT 5 - ` - return { props: { - userCount, - playCount, - language: loggedInUser.language, + userCount: totalCounts._count.username, + playCount: totalCounts._sum.coins, + language: sessionUser?.language || 'en', randomUsers: JSON.parse(safeJsonStringify(randomUsers)) } } @@ -54,7 +74,6 @@ export const getServerSideProps = withSession(async ({ req, query }) => { function IndexPage ({ userCount, playCount, language, randomUsers }) { const router = useRouter() - const { user, isLoading } = useInfo() useEffect(() => { @@ -68,105 +87,105 @@ function IndexPage ({ userCount, playCount, language, randomUsers }) { return ( - - - - - -

- -
- - { - isLoading === false - ? ( - - - {user?.username === undefined && ( - <> -
- -
{' '} - - )} - - + + + + + +

+ +
+ + { + isLoading === false + ? ( + + + {user?.username === undefined && ( + <> +
+ +
{' '} + + )} + + + +
+ ) + : null + } + + { + isLoading === false && user?.username === undefined && ( + + +

+ If an account does not exist, it will be created. You agree to our{' '} + Privacy Policy and our{' '} + Terms of Service. +

- ) - : null - } + ) + } - { - isLoading === false && user?.username === undefined && ( - - -

- If an account does not exist, it will be created. You agree to our{' '} - Privacy Policy and our{' '} - Terms of Service. -

- -
- ) - } + { + userCount !== 0 && ( + + +

+ {' '} +

+ +
+ ) + } - { - userCount !== 0 && ( - - -

- {' '} -

- -
- ) - } + { + randomUsers.length > 0 && ( + + + + + + ) + } - { - randomUsers.length > 0 && ( - + +

+ +

+ - +
Wii
+
    +
  • Dolphin Discord RPC
  • +
  • USB Loaders
  • +
      +
    • Configurable USB Loader
    • +
    • USB Loader GX
    • +
    • WiiFlow
    • +
    +
-
- ) - } - - -

- -

- - -
Wii
-
    -
  • Dolphin Discord RPC
  • -
  • USB Loaders
  • + +
    Wii U
      -
    • Configurable USB Loader
    • -
    • USB Loader GX
    • -
    • WiiFlow
    • +
    • Aroma Plugin
    • +
    • Cemu Discord RPC
    -
- - -
Wii U
-
    -
  • Aroma Plugin
  • -
  • Cemu Discord RPC
  • -
- + +
-
-
+
) } diff --git a/src/pages/user/[username]/index.jsx b/src/pages/user/[username]/index.jsx index 0fa56b7..4899d48 100644 --- a/src/pages/user/[username]/index.jsx +++ b/src/pages/user/[username]/index.jsx @@ -17,8 +17,10 @@ import AppNavbar from '@/components/shared/AppNavbar' export const getServerSideProps = withSession(async ({ req, query }) => { const { username } = query - const loggedInUsername = req.session?.username + // Get the logged in user. This can be null. + const session = req.session?.username + // get user and ban reason const user = await prisma.user.findUnique({ where: { username: username.toString() @@ -29,7 +31,6 @@ export const getServerSideProps = withSession(async ({ req, query }) => { image: true, display_name: true, created_at: true, - updated_at: true, overlay: true, background: true, coin: true, @@ -40,15 +41,32 @@ export const getServerSideProps = withSession(async ({ req, query }) => { role: true, isBanned: true, isPublic: true, - publicOverride: true + publicOverride: true, + banned_user: true, + playlog: { + select: { + game: true, + play_time: true, + play_count: true, + played_on: true + }, + orderBy: { + played_on: 'desc' + } + }, + game_sessions: { + select: { + game: true, + start_time: true + } + } } }) - const banReason = await prisma.banned_user.findFirst({ - where: { - user_id: user.id - } - }) + // If there's no user, don't continue from here. + if (!user) { + return { notFound: true } + } const event = await prisma.events.findFirst({ where: { @@ -61,45 +79,10 @@ export const getServerSideProps = withSession(async ({ req, query }) => { } }) - const playlog = await prisma.playlog.findMany({ - where: { - user: { - username: username.toString() - } - }, - select: { - play_time: true, - play_count: true, - played_on: true, - game: { - select: { - game_id: true, - name: true - } - } - }, - orderBy: { - played_on: 'desc' - }, - take: 10 - }) - - const session = await prisma.game_sessions.findFirst({ - where: { - user_id: user.username - } - }) - - const sessionGame = await prisma.game.findFirst({ - where: { - game_id: session?.game_id || '0' - } - }) - - const loggedInUser = loggedInUsername != null + const loggedInUser = session != null ? await prisma.user.findUnique({ where: { - username: loggedInUsername + username: session }, select: { role: true, @@ -108,26 +91,21 @@ export const getServerSideProps = withSession(async ({ req, query }) => { }) : { role: 'guest' } - if (!user) { - return { notFound: true } - } - return { props: { user: JSON.parse(safeJsonStringify(user)), - isLoggedIn: user.username === loggedInUsername, + isLoggedIn: user.username === session, loggedInUser, language: loggedInUser?.language || 'en', - banReason: JSON.parse(safeJsonStringify(banReason)), + banReason: JSON.parse(safeJsonStringify(user.banned_user)), event: JSON.parse(safeJsonStringify(event)), - playlog: JSON.parse(safeJsonStringify(playlog)), - session: JSON.parse(safeJsonStringify(session)), - game: JSON.parse(safeJsonStringify(sessionGame)) + playlog: JSON.parse(safeJsonStringify(user.playlog)), + session: JSON.parse(safeJsonStringify(user.game_sessions)) } } }) -function ProfilePage ({ user, isLoggedIn, banReason, loggedInUser, event, playlog, language, session, game }) { +function ProfilePage ({ user, isLoggedIn, banReason, loggedInUser, event, playlog, language, session }) { return ( @@ -166,13 +144,13 @@ function ProfilePage ({ user, isLoggedIn, banReason, loggedInUser, event, playlo /> - + : ''} {event && An event is currently ongoing: {event.name}.
Until {event.date}, you will recieve {event.bonus + 1}x more coins.
} - {session && } + {session && } {isLoggedIn && }