Skip to content

Commit

Permalink
fix: get rid of playerbans collection (#111)
Browse files Browse the repository at this point in the history
  • Loading branch information
garrappachc authored Dec 13, 2024
1 parent 2f85d55 commit 85129b1
Show file tree
Hide file tree
Showing 14 changed files with 152 additions and 107 deletions.
2 changes: 0 additions & 2 deletions src/database/collections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import type { GameModel } from './models/game.model'
import type { KeyModel } from './models/key.model'
import type { MapPoolEntry } from './models/map-pool-entry.model'
import type { OnlinePlayerModel } from './models/online-player.model'
import type { PlayerBanModel } from './models/player-ban.model'
import type { PlayerModel } from './models/player.model'
import type { QueueFriendshipModel } from './models/queue-friendship.model'
import type { QueueMapOptionModel } from './models/queue-map-option.model'
Expand All @@ -27,7 +26,6 @@ export const collections = {
maps: database.collection<MapPoolEntry>('maps'),
onlinePlayers: database.collection<OnlinePlayerModel>('onlineplayers'),
players: database.collection<PlayerModel>('players'),
playerBans: database.collection<PlayerBanModel>('playerbans'),
queueFriends: database.collection<QueueFriendshipModel>('queue.friends'),
queueSlots: database.collection<QueueSlotModel>('queue.slots'),
queueState: database.collection<QueueStateModel>('queue.state'),
Expand Down
9 changes: 0 additions & 9 deletions src/database/models/player-ban.model.ts

This file was deleted.

9 changes: 9 additions & 0 deletions src/database/models/player.model.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { Bot } from '../../shared/types/bot'
import type { SteamId64 } from '../../shared/types/steam-id-64'
import { Tf2ClassName } from '../../shared/types/tf2-class-name'
import type { GameNumber } from './game.model'
Expand All @@ -17,6 +18,13 @@ export interface PlayerPreferences {
soundVolume?: number
}

export interface PlayerBan {
actor: SteamId64 | Bot
start: Date
end: Date
reason: string
}

export interface PlayerModel {
name: string
steamId: SteamId64
Expand All @@ -30,4 +38,5 @@ export interface PlayerModel {
skill?: Partial<Record<Tf2ClassName, number>>
preReadyUntil?: Date
preferences: PlayerPreferences
bans?: PlayerBan[]
}
9 changes: 5 additions & 4 deletions src/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ import { logger } from './logger'
import type { QueueSlotModel } from './database/models/queue-slot.model'
import { QueueState } from './database/models/queue-state.model'
import type { GameModel, GameNumber } from './database/models/game.model'
import type { PlayerModel } from './database/models/player.model'
import type { PlayerBan, PlayerModel } from './database/models/player.model'
import type { MapPoolEntry } from './database/models/map-pool-entry.model'
import type { StaticGameServerModel } from './database/models/static-game-server.model'
import type { LogMessage } from './log-receiver/parse-log-message'
import type { Tf2Team } from './shared/types/tf2-team'
import type { PlayerBanModel } from './database/models/player-ban.model'
import type { StreamModel } from './database/models/stream.model'
import type { Bot } from './shared/types/bot'
import type { PlayerConnectionStatus } from './database/models/game-slot.model'
Expand Down Expand Up @@ -114,10 +113,12 @@ export interface Events {
after: PlayerModel
}
'player/ban:added': {
ban: PlayerBanModel
player: SteamId64
ban: PlayerBan
}
'player/ban:revoked': {
ban: PlayerBanModel
player: SteamId64
ban: PlayerBan
admin: SteamId64
}

Expand Down
47 changes: 47 additions & 0 deletions src/migrations/004-remove-playerbans.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import type { ObjectId } from 'mongodb'
import { database } from '../database/database'
import { collections } from '../database/collections'
import { logger } from '../logger'

interface PlayerBanModel {
player: ObjectId
admin: ObjectId
start: Date
end: Date
reason: string
}

export async function up() {
const collection = database.collection<PlayerBanModel>('playerbans')
const bans = await collection.find().toArray()
for (const ban of bans) {
const player = await collections.players.findOne({ _id: ban.player })
if (player === null) {
logger.warn(`actor ${ban.player.toString()} not found`)
continue
}

const actor = await collections.players.findOne({ _id: ban.admin })
if (actor === null) {
logger.warn(`actor ${ban.admin.toString()} not found; was this bot?`)
}

await collections.players.updateOne(
{ steamId: player.steamId },
{
$push: {
bans: {
actor: actor?.steamId ?? 'bot',
start: ban.start,
end: ban.end,
reason: ban.reason,
},
},
},
)

await collection.deleteOne({ _id: ban._id })
}

await collection.drop()
}
22 changes: 9 additions & 13 deletions src/players/add-ban.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,29 @@
import { collections } from '../database/collections'
import type { PlayerBanModel } from '../database/models/player-ban.model'
import type { PlayerBan } from '../database/models/player.model'
import { events } from '../events'
import type { SteamId64 } from '../shared/types/steam-id-64'
import { PlayerNotFoundError } from './errors'
import { update } from './update'

export async function addBan(props: {
player: SteamId64
admin: SteamId64
end: Date
reason: string
}): Promise<PlayerBanModel> {
const player = await collections.players.findOne({ steamId: props.player })
if (!player) {
throw new PlayerNotFoundError(props.player)
}

}): Promise<PlayerBan> {
const admin = await collections.players.findOne({ steamId: props.admin })
if (!admin) {
throw new PlayerNotFoundError(props.admin)
}

const { insertedId } = await collections.playerBans.insertOne({
player: player._id,
admin: admin._id,
const ban: PlayerBan = {
actor: admin.steamId,
start: new Date(),
end: props.end,
reason: props.reason,
})
const ban = (await collections.playerBans.findOne({ _id: insertedId }))!
events.emit('player/ban:added', { ban })
}

await update(props.player, { $push: { bans: ban } })
events.emit('player/ban:added', { player: props.player, ban })
return ban
}
41 changes: 23 additions & 18 deletions src/players/revoke-ban.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
import type { ObjectId } from 'mongodb'
import { collections } from '../database/collections'
import type { SteamId64 } from '../shared/types/steam-id-64'
import { events } from '../events'
import { update } from './update'
import type { PlayerBan } from '../database/models/player.model'

export async function revokeBan(banId: ObjectId, adminId: SteamId64) {
let ban = await collections.playerBans.findOne({ _id: banId })
if (!ban) {
throw new Error(`ban not found: ${banId}`)
}
export async function revokeBan(props: {
player: SteamId64
banStart: Date
admin: SteamId64
}): Promise<PlayerBan> {
const after = await update(
props.player,
{
$set: {
'bans.$[ban].end': new Date(),
},
},
{
arrayFilters: [{ 'ban.start': { $eq: props.banStart } }],
},
)

if (ban.end < new Date()) {
throw new Error(`ban already expired: ${banId}`)
const ban = after.bans?.find(b => b.start.getTime() === props.banStart.getTime())
if (!ban) {
throw new Error(`ban not found`)
}

const after = (await collections.playerBans.findOneAndUpdate(
{ _id: banId },
{ $set: { end: new Date() } },
{
returnDocument: 'after',
},
))!
events.emit('player/ban:revoked', { ban: after, admin: adminId })
return after
events.emit('player/ban:revoked', { player: after.steamId, ban, admin: props.admin })
return ban
}
20 changes: 14 additions & 6 deletions src/players/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { banExpiryFormSchema } from './schemas/ban-expiry-form.schema'
import { format } from 'date-fns'
import { getBanExpiryDate } from './get-ban-expiry-date'
import { addBan } from './add-ban'
import { ObjectId } from 'mongodb'
import { revokeBan } from './revoke-ban'

export default fp(
Expand Down Expand Up @@ -226,22 +225,31 @@ export default fp(
},
)
.put(
'/players/:steamId/edit/bans/:banId/revoke',
'/players/:steamId/edit/bans/:banStart/revoke',
{
config: {
authorize: [PlayerRole.admin],
},
schema: {
params: z.object({
steamId: steamId64,
banId: z.string().transform(value => new ObjectId(value)),
banStart: z.coerce.number().transform(value => new Date(value)),
}),
},
},
async (request, reply) => {
const { banId } = request.params
const ban = await revokeBan(banId, request.user!.player.steamId)
reply.status(200).html(await BanDetails({ ban }))
const { steamId, banStart } = request.params
const player = await collections.players.findOne({ steamId })
if (player === null) {
return reply.status(404).send()
}

const ban = await revokeBan({
player: steamId,
banStart,
admin: request.user!.player.steamId,
})
reply.status(200).html(await BanDetails({ player, ban }))
},
)
.get(
Expand Down
4 changes: 3 additions & 1 deletion src/players/update.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { StrictUpdateFilter } from 'mongodb'
import type { FindOneAndUpdateOptions, StrictUpdateFilter } from 'mongodb'
import type { SteamId64 } from '../shared/types/steam-id-64'
import type { PlayerModel } from '../database/models/player.model'
import { mutex } from './mutex'
Expand All @@ -9,6 +9,7 @@ import { events } from '../events'
export async function update(
steamId: SteamId64,
update: StrictUpdateFilter<PlayerModel>,
options?: FindOneAndUpdateOptions,
): Promise<PlayerModel> {
return await mutex.runExclusive(async () => {
const before = await collections.players.findOne({ steamId })
Expand All @@ -18,6 +19,7 @@ export async function update(

const after = (await collections.players.findOneAndUpdate({ steamId }, update, {
returnDocument: 'after',
...options,
}))!

events.emit('player:updated', { before, after })
Expand Down
52 changes: 27 additions & 25 deletions src/players/views/html/edit-player.page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { resolve } from 'node:path'
import type { PlayerModel } from '../../../database/models/player.model'
import type { PlayerBan, PlayerModel } from '../../../database/models/player.model'
import { Layout } from '../../../html/layout'
import { NavigationBar } from '../../../html/components/navigation-bar'
import type { User } from '../../../auth/types/user'
Expand All @@ -24,8 +24,8 @@ import { GameClassIcon } from '../../../html/components/game-class-icon'
import { configuration } from '../../../configuration'
import { collections } from '../../../database/collections'
import type { WithId } from 'mongodb'
import type { PlayerBanModel } from '../../../database/models/player-ban.model'
import { format } from 'date-fns'
import { isBot } from '../../../shared/types/bot'

const editPlayerPages = {
'/profile': 'Profile',
Expand Down Expand Up @@ -100,11 +100,7 @@ export async function EditPlayerSkillPage(props: { player: PlayerModel; user: Us
}

export async function EditPlayerBansPage(props: { player: WithId<PlayerModel>; user: User }) {
const bans = await collections.playerBans
.find({ player: props.player._id })
.sort({ start: -1 })
.toArray()

const bans = props.player.bans?.toSorted((a, b) => b.start.getTime() - a.start.getTime())
return (
<EditPlayer
player={props.player}
Expand All @@ -118,16 +114,16 @@ export async function EditPlayerBansPage(props: { player: WithId<PlayerModel>; u
}
>
<div class="admin-panel-content">
{bans.length === 0 ? (
<span class="italic text-abru-light-75">No bans</span>
) : (
{bans?.length ? (
<>
<div class="edit-player-ban-list">
{bans.map(ban => (
<BanDetails ban={ban} />
<BanDetails player={props.player} ban={ban} />
))}
</div>
</>
) : (
<span class="italic text-abru-light-75">No bans</span>
)}
</div>
</EditPlayer>
Expand Down Expand Up @@ -186,35 +182,41 @@ function EditPlayer(props: {
)
}

export async function BanDetails(props: { ban: WithId<PlayerBanModel> }) {
const player = await collections.players.findOne({ _id: props.ban.player })
if (!player) {
throw new Error(`player ${props.ban.player.toString()} not found`)
export async function BanDetails(props: { player: PlayerModel; ban: PlayerBan }) {
let actorDesc = <></>
if (isBot(props.ban.actor)) {
actorDesc = <>bot</>
} else {
const actor = await collections.players.findOne({ steamId: props.ban.actor })
if (actor === null) {
throw new Error(`actor not found: ${props.ban.actor}`)
}

actorDesc = (
<a href={`/players/${actor.steamId}`} safe>
{' '}
{actor.name}
</a>
)
}
const admin = await collections.players.findOne({ _id: props.ban.admin })

return (
<div class="ban-item group" id={`player-ban-${props.ban._id}`}>
<div class="ban-item group" id={`player-ban-${props.ban.start.getTime().toString()}`}>
<form class="contents">
<div class="col-span-3">
<span class="me-2 text-2xl font-bold" safe>
{props.ban.reason}
</span>
<span class="text-sm">
by{' '}
<a href={`/players/${admin?.steamId}`} safe>
{admin?.name ?? 'unknown admin'}
</a>
</span>
<span class="text-sm">by {actorDesc}</span>
</div>

<div class="row-span-2 flex items-center">
{props.ban.end > new Date() ? (
<button
class="button button--darker"
hx-put={`/players/${player.steamId}/edit/bans/${props.ban._id.toString()}/revoke`}
hx-put={`/players/${props.player.steamId}/edit/bans/${props.ban.start.getTime().toString()}/revoke`}
hx-trigger="click"
hx-target={`#player-ban-${props.ban._id}`}
hx-target={`#player-ban-${props.ban.start.getTime().toString()}`}
hx-swap="outerHTML"
>
<IconX />
Expand Down
Loading

0 comments on commit 85129b1

Please sign in to comment.