diff --git a/README.md b/README.md index 3b04961..05b64b2 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ You can change the encoding by running `ALTER DATABASE aime CHARACTER SET utf8mb ## Creating Database Tables Before running the database importer script, you must create the tables in your database. This can be done by running the server once with `AUTOMIGRATE` enabled ([see below](#runtime-variables)), or by running `npm run migrate` (make sure you have run `npm i` first and have the [`DATABASE_URL`](#runtime-variables) environment variable set). +If you are running MySQL server, you may need to run `SET GLOBAL log_bin_trust_function_creators = 1;` + # Building Make sure you have set the required [build-time variables](#build-time-variables) correctly. diff --git a/src/actions/chuni/profile.ts b/src/actions/chuni/profile.ts index 718412f..cdc3a42 100644 --- a/src/actions/chuni/profile.ts +++ b/src/actions/chuni/profile.ts @@ -96,8 +96,8 @@ export async function getUserRating(user: UserPayload) { .innerJoin('actaeon_chuni_static_music_ext as musicExt', join => join .onRef('music.songId', '=', 'musicExt.songId') .onRef('music.chartId', '=', 'musicExt.chartId')) - .select(({ lit }) => [...CHUNI_MUSIC_PROPERTIES, sqlChuniRating(sql.raw(`CAST(score.scoreMax AS INT)`)), - sql`CAST(score.scoreMax AS INT)`.as('scoreMax'), + .select(({ lit }) => [...CHUNI_MUSIC_PROPERTIES, sqlChuniRating(), + sql`(score.scoreMax + 0)`.as('scoreMax'), lit(1).as('pastIndex') ]) .where(({ selectFrom, eb }) => eb('music.version', '=', selectFrom('chuni_static_music') @@ -229,11 +229,13 @@ export const updateProfile = async (data: ProfileUpdate) => { } } - await db.updateTable('chuni_profile_data') + await db + .updateTable('chuni_profile_data') .where(({ and, eb, selectFrom }) => and([ eb('user', '=', user.id), - eb('version', '=', selectFrom('chuni_profile_data') - .select(({ fn }) => fn.max('version').as('latest'))) + eb('version', '=', selectFrom(db.selectFrom('chuni_profile_data') + .select(({ fn }) => fn.max('version').as('latest')).as('l')) + .select('latest')) ])) .set(update) .execute(); diff --git a/src/actions/chuni/userbox.ts b/src/actions/chuni/userbox.ts index f621799..baf22a0 100644 --- a/src/actions/chuni/userbox.ts +++ b/src/actions/chuni/userbox.ts @@ -86,6 +86,7 @@ export const getUserboxItems = async (user: UserPayload, profile: ChuniUserData) 'avatar.texturePath' ]).as('avatar')] as const)) .selectFrom(['map_icons', 'name_plates', 'system_voices', 'trophies', 'avatars']) + .groupBy(['map_icons.mapIcon', 'name_plates.namePlate', 'system_voices.systemVoice', 'trophies.trophy']) .select(eb => ['map_icons.mapIcon', 'name_plates.namePlate', 'system_voices.systemVoice', 'trophies.trophy', jsonObjectArray(eb, [ 'avatars.category', 'avatars.avatar' diff --git a/src/app/(with-header)/team/page.tsx b/src/app/(with-header)/team/page.tsx index 5a04499..0b8e05c 100644 --- a/src/app/(with-header)/team/page.tsx +++ b/src/app/(with-header)/team/page.tsx @@ -33,7 +33,7 @@ export default async function TeamPage() { {team.name} - {team.isMember && + {!!team.isMember && } diff --git a/src/data/arcade.ts b/src/data/arcade.ts index 1214ead..f48098c 100644 --- a/src/data/arcade.ts +++ b/src/data/arcade.ts @@ -32,18 +32,21 @@ export const getArcades = async ({ user, uuids, includeUnlisted }: { user?: User await createArcadeExtIfNecessary().catch(console.error); const result = await withUsersVisibleTo(user) - .selectFrom('arcade') - .innerJoin('actaeon_arcade_ext as ext', 'arcade.id', 'ext.arcadeId') - .leftJoin('arcade_owner', join => join.onRef('arcade_owner.arcade', '=', 'arcade.id') - .on('arcade_owner.user', '=', user?.id!)) - .leftJoin(eb => eb.selectFrom('arcade_owner as o') + .with('owners', eb => eb.selectFrom('arcade_owner as o') .innerJoin('aime_user as u', 'u.id', 'o.user') .innerJoin('actaeon_user_ext as owner_ext', 'u.id', 'owner_ext.userId') .where('o.permissions', '>=', 1 << ArcadePermissions.OWNER) .where(userIsVisible('o.user')) - .select(({ fn }) => ['u.username', fn.min('u.id').as('id'), 'owner_ext.uuid', 'o.arcade']) - .groupBy('o.arcade') - .as('owner'), join => join.onRef('owner.arcade', '=', 'arcade.id')) + .select(({ fn }) => ['u.username', fn.min('u.id').as('id'), 'owner_ext.uuid', 'o.arcade'] as const) + .groupBy(['o.arcade', 'u.id'])) + .selectFrom('arcade') + .innerJoin('actaeon_arcade_ext as ext', 'arcade.id', 'ext.arcadeId') + .leftJoin('arcade_owner', join => join.onRef('arcade_owner.arcade', '=', 'arcade.id') + .on('arcade_owner.user', '=', user?.id!)) + .leftJoin('owners as owner', join => join.onRef('owner.arcade', '=', 'arcade.id') + .on(eb => eb('owner.id', '=', eb.selectFrom('owners') + .whereRef('owners.arcade', '=', 'arcade.id') + .select(({ fn }) => fn.min('owners.id').as('least'))))) .where(({ eb, and }) => and([ ...(uuids?.length ? [eb('ext.uuid', 'in', uuids)] : [eb.lit(true)]) ])) diff --git a/src/helpers/chuni/rating.ts b/src/helpers/chuni/rating.ts index 2081c34..dd87302 100644 --- a/src/helpers/chuni/rating.ts +++ b/src/helpers/chuni/rating.ts @@ -1,7 +1,7 @@ import { sql } from 'kysely'; import { BigDecimal } from '../big-decimal'; -export const sqlChuniRating = (score: any = sql.raw(`CAST(score.scoreMax AS INT)`), +export const sqlChuniRating = (score: any = sql.raw(`(score.scoreMax + 0)`), level: any = sql.raw(`(CAST(music.level AS DECIMAL(3, 1)) * 100)`)) => sql` CAST(GREATEST((CASE WHEN ${score} IS NULL THEN NULL diff --git a/src/helpers/parse-json-result.ts b/src/helpers/parse-json-result.ts index e0d1068..d46e170 100644 --- a/src/helpers/parse-json-result.ts +++ b/src/helpers/parse-json-result.ts @@ -14,5 +14,5 @@ export const parseJsonResult = (>(d if (Array.isArray(data)) return data.map(d => parseJsonResult(d, keys)); return Object.fromEntries((Object.entries(data) as Entries) - .map(([key, val]) => [key, keys.includes(key as any) ? JSON.parse(val as any) : val])); + .map(([key, val]) => [key, typeof val === 'string' && keys.includes(key as any) ? JSON.parse(val as any) : val])); }) as ParseJSONResultOptions;