From ab40bb06bfff97e04f652439c0f2f5dbceb104f2 Mon Sep 17 00:00:00 2001 From: sk1982 Date: Mon, 1 Apr 2024 06:50:19 -0400 Subject: [PATCH] chuni: setting for equip unearned --- src/actions/chuni/profile.ts | 17 +++++++++-------- src/actions/chuni/userbox.ts | 21 +++++++++++---------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/actions/chuni/profile.ts b/src/actions/chuni/profile.ts index ba460ce..d925a3a 100644 --- a/src/actions/chuni/profile.ts +++ b/src/actions/chuni/profile.ts @@ -12,6 +12,7 @@ import { UserboxItems } from '@/actions/chuni/userbox'; import { getUser, requireUser } from '@/actions/auth'; import { Entries } from 'type-fest'; import { CHUNI_NAMEPLATE_PROFILE_KEYS } from '@/components/chuni/nameplate'; +import { getGlobalConfig } from '@/config'; type RecentRating = { scoreMax: string, @@ -23,8 +24,6 @@ type RecentRating = { const avatarNames = ['avatarBack', 'avatarFace', 'avatarItem', 'avatarWear', 'avatarFront', 'avatarSkin', 'avatarHead'] as const; -const ALLOW_EQUIP_UNEARNED = ['true', '1', 'yes'].includes(process.env.CHUNI_ALLOW_EQUIP_UNEARNED?.toLowerCase() ?? ''); - export async function getUserData(user: { id: number }) { const res = await db.selectFrom('chuni_profile_data as p') .leftJoin('actaeon_chuni_static_name_plate as nameplate', 'p.nameplateId', 'nameplate.id') @@ -126,7 +125,7 @@ export async function getUserRating(user: UserPayload) { return { recent, top }; } -const validators = new Map, value: any) => Promise>(); +const validators = new Map, value: any) => Promise>(); const itemValidators = [ ['mapIconId', 'actaeon_chuni_static_map_icon as map_icon', 'map_icon.id', ItemKind.MAP_ICON], @@ -135,6 +134,8 @@ const itemValidators = [ ['trophyId', 'actaeon_chuni_static_trophies as trophy', 'trophy.id', ItemKind.TROPHY] ] as const; +const canEquipUnearned = (user: UserPayload) => (getGlobalConfig('chuni_allow_equip_unearned') & user.permissions!); + itemValidators.forEach(([key, table, joinKey, itemKind]) => { validators.set(key, async (user, profile, value) => { value = parseInt(value); @@ -144,7 +145,7 @@ itemValidators.forEach(([key, table, joinKey, itemKind]) => { const res = await db.selectFrom(table) .leftJoin('chuni_item_item as item', join => join .onRef('item.itemId', '=', joinKey as any) - .on('item.user', '=', user) + .on('item.user', '=', user.id) .on('item.itemKind', '=', itemKind)) .where(joinKey as any, '=', value) .select('item.itemId') @@ -153,7 +154,7 @@ itemValidators.forEach(([key, table, joinKey, itemKind]) => { if (!res) throw new Error(`Item with id ${value} does not exist.`); - if (res.itemId === null && value !== profile[key] && !ALLOW_EQUIP_UNEARNED) + if (res.itemId === null && value !== profile[key] && !canEquipUnearned(user)) throw new Error(`You do not own that item.`); return value; @@ -170,7 +171,7 @@ Object.entries(AvatarCategory).forEach(([category, number]) => { const res = await db.selectFrom('chuni_static_avatar as avatar') .leftJoin('chuni_item_item as item', join => join .onRef('item.itemId', '=', 'avatar.avatarAccessoryId') - .on('item.user', '=', user) + .on('item.user', '=', user.id) .on('item.itemKind', '=', ItemKind.AVATAR_ACCESSORY)) .where(({ eb, and, selectFrom }) => and([ eb('avatar.version', '=', selectFrom('chuni_static_avatar') @@ -184,7 +185,7 @@ Object.entries(AvatarCategory).forEach(([category, number]) => { if (!res) throw new Error(`Item with id ${value} does not exist.`); - if (res.itemId === null && value !== profile[key as keyof ChuniUserData] && !ALLOW_EQUIP_UNEARNED) + if (res.itemId === null && value !== profile[key as keyof ChuniUserData] && !canEquipUnearned(user)) throw new Error(`You do not own that item.`); return value; @@ -207,7 +208,7 @@ export const updateProfile = async (data: ProfileUpdate) => { return { error: true, message: `Unknown key "${key}"` }; try { - update[key as keyof ProfileUpdate] = ((await (validators.get(key as any)!(user.id, profile, value))) ?? value) as any; + update[key as keyof ProfileUpdate] = ((await (validators.get(key as any)!(user, profile, value))) ?? value) as any; } catch (e: any) { return { error: true, message: e?.message ?? 'Unknown error occurred.' }; } diff --git a/src/actions/chuni/userbox.ts b/src/actions/chuni/userbox.ts index 2fcc15b..dcda7c2 100644 --- a/src/actions/chuni/userbox.ts +++ b/src/actions/chuni/userbox.ts @@ -9,16 +9,17 @@ import { AvatarCategory } from '@/helpers/chuni/avatar'; import { DB } from '@/types/db'; import { ChuniUserData } from '@/actions/chuni/profile'; import { parseJsonResult } from '@/helpers/parse-json-result'; +import { getGlobalConfig } from '@/config'; -const ALLOW_EQUIP_UNEARNED = ['true', '1', 'yes'].includes(process.env.CHUNI_ALLOW_EQUIP_UNEARNED?.toLowerCase() ?? ''); - -const joinItem = (builder: SelectQueryBuilder, joinKey: any, user: number, itemKind: ItemKind, ...equipped: (number | null | undefined)[]) => { +const joinItem = (builder: SelectQueryBuilder, joinKey: any, user: UserPayload, itemKind: ItemKind, ...equipped: (number | null | undefined)[]) => { + const canEquipUnearned = (getGlobalConfig('chuni_allow_equip_unearned') & user.permissions!); + return (builder.leftJoin('chuni_item_item', join => join.onRef('chuni_item_item.itemId' as any, '=', joinKey) - .on('chuni_item_item.user' as any, '=', user) + .on('chuni_item_item.user' as any, '=', user.id) .on('chuni_item_item.itemKind' as any, '=', itemKind) ) as any) - .where((eb: ExpressionBuilder) => eb.or(ALLOW_EQUIP_UNEARNED ? [eb.lit(true)] : [ + .where((eb: ExpressionBuilder) => eb.or(canEquipUnearned ? [eb.lit(true)] : [ eb('chuni_item_item.itemId', 'is not', null), // owned item ...equipped.map(id => eb(joinKey, '=', id ?? -1)) // equipped but not owned ])) as SelectQueryBuilder; @@ -39,7 +40,7 @@ export type UserboxItems = { export const getUserboxItems = async (user: UserPayload, profile: ChuniUserData): Promise => { const res = await db .with('map_icons', eb => joinItem(eb.selectFrom('actaeon_chuni_static_map_icon as map_icon'), - 'map_icon.id', user.id, ItemKind.MAP_ICON, profile?.mapIconId) + 'map_icon.id', user, ItemKind.MAP_ICON, profile?.mapIconId) .select(eb => jsonObjectArray(eb, [ 'map_icon.id', 'map_icon.name', @@ -48,7 +49,7 @@ export const getUserboxItems = async (user: UserPayload, profile: ChuniUserData) ]).as('mapIcon')) ) .with('name_plates', eb => joinItem(eb.selectFrom('actaeon_chuni_static_name_plate as name_plate'), - 'name_plate.id', user.id, ItemKind.NAME_PLATE, profile?.nameplateId) + 'name_plate.id', user, ItemKind.NAME_PLATE, profile?.nameplateId) .select(eb => jsonObjectArray(eb, [ 'name_plate.id', 'name_plate.name', @@ -56,7 +57,7 @@ export const getUserboxItems = async (user: UserPayload, profile: ChuniUserData) 'name_plate.imagePath' ]).as('namePlate'))) .with('system_voices', eb => joinItem(eb.selectFrom('actaeon_chuni_static_system_voice as system_voice'), - 'system_voice.id', user.id, ItemKind.SYSTEM_VOICE, profile?.voiceId) + 'system_voice.id', user, ItemKind.SYSTEM_VOICE, profile?.voiceId) .select(eb => jsonObjectArray(eb, [ 'system_voice.id', 'system_voice.name', @@ -65,7 +66,7 @@ export const getUserboxItems = async (user: UserPayload, profile: ChuniUserData) 'system_voice.cuePath', ]).as('systemVoice'))) .with('trophies', eb => joinItem(eb.selectFrom('actaeon_chuni_static_trophies as trophy'), - 'trophy.id', user.id, ItemKind.TROPHY, profile?.nameplateId) + 'trophy.id', user, ItemKind.TROPHY, profile?.nameplateId) .select(eb => jsonObjectArray(eb, [ 'trophy.id', 'trophy.name', @@ -73,7 +74,7 @@ export const getUserboxItems = async (user: UserPayload, profile: ChuniUserData) 'trophy.explainText' ]).as('trophy'))) .with('avatars', eb => joinItem(eb.selectFrom('chuni_static_avatar as avatar'), - 'avatar.avatarAccessoryId', user.id, ItemKind.AVATAR_ACCESSORY, profile?.avatarBack, profile?.avatarFace, profile?.avatarItem, + 'avatar.avatarAccessoryId', user, ItemKind.AVATAR_ACCESSORY, profile?.avatarBack, profile?.avatarFace, profile?.avatarItem, profile?.avatarWear, profile?.avatarFront, profile?.avatarSkin, profile?.avatarHead) .where(({ selectFrom, eb }) => eb('avatar.version', '=', selectFrom('chuni_static_avatar') .select(({ fn }) => fn.max('version').as('latest'))))