mirror of
https://github.com/djdevin/recflare.git
synced 2026-10-27 16:08:58 -07:00
[rooms] log room delete
This commit is contained in:
@@ -69,6 +69,7 @@ import {
|
|||||||
transferRoomOwnership,
|
transferRoomOwnership,
|
||||||
unbanPlayerFromRoom,
|
unbanPlayerFromRoom,
|
||||||
updateRoomFields,
|
updateRoomFields,
|
||||||
|
writeAuditLog,
|
||||||
} from '@repo/domain'
|
} from '@repo/domain'
|
||||||
import {
|
import {
|
||||||
intVar,
|
intVar,
|
||||||
@@ -2117,6 +2118,33 @@ const app = new Hono<App>()
|
|||||||
await c.env.CDN_ASSETS.delete(`room/${imageName}`)
|
await c.env.CDN_ASSETS.delete(`room/${imageName}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A takedown — staff deleting a room that isn't theirs — goes in the audit log; an
|
||||||
|
// owner deleting their own room is their business and doesn't. The row keeps the
|
||||||
|
// room's identity, since the room itself is gone and nothing else will answer "which
|
||||||
|
// room was that". Written after the fact and caught: a failed insert must not report
|
||||||
|
// a deletion that has already happened as refused. It leaves a loud line instead.
|
||||||
|
if (!isOwner) {
|
||||||
|
try {
|
||||||
|
await writeAuditLog(c.env.DB, {
|
||||||
|
playerId: accountId,
|
||||||
|
action: 'room_delete',
|
||||||
|
data: {
|
||||||
|
roomId,
|
||||||
|
name: room.Name,
|
||||||
|
creatorAccountId: room.CreatorAccountId,
|
||||||
|
accessibility: room.Accessibility,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('could not write an audit log row', {
|
||||||
|
action: 'room_delete',
|
||||||
|
playerId: accountId,
|
||||||
|
roomId,
|
||||||
|
error: err instanceof Error ? err.message : String(err),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return roomResult(c, { Success: true })
|
return roomResult(c, { Success: true })
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { beforeAll, describe, expect, it } from 'vitest'
|
|||||||
import '../../rooms.app'
|
import '../../rooms.app'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
AUDIT_LOG_SCHEMA_DDL,
|
||||||
createRoomInstance,
|
createRoomInstance,
|
||||||
getRoomInstance,
|
getRoomInstance,
|
||||||
MessageType,
|
MessageType,
|
||||||
@@ -104,6 +105,9 @@ beforeAll(async () => {
|
|||||||
// Message store (owned by the api worker) — a room-role invite is written there before
|
// Message store (owned by the api worker) — a room-role invite is written there before
|
||||||
// it is pushed, so the invited player can read it back from their inbox.
|
// it is pushed, so the invited player can read it back from their inbox.
|
||||||
for (const stmt of NOTIFICATION_SCHEMA_DDL) await env.DB.prepare(stmt).run()
|
for (const stmt of NOTIFICATION_SCHEMA_DDL) await env.DB.prepare(stmt).run()
|
||||||
|
// The audit log the staff takedown writes to. Owned by `api`'s migrations; built here
|
||||||
|
// directly, the way `notify`'s tests do.
|
||||||
|
for (const stmt of AUDIT_LOG_SCHEMA_DDL) await env.DB.prepare(stmt).run()
|
||||||
// Seed each room and split its subrooms into the subroom table (mirrors 0007's backfill).
|
// Seed each room and split its subrooms into the subroom table (mirrors 0007's backfill).
|
||||||
for (const r of importRooms) await seedRoomWithSubRooms(env.DB, r as Record<string, unknown>)
|
for (const r of importRooms) await seedRoomWithSubRooms(env.DB, r as Record<string, unknown>)
|
||||||
|
|
||||||
@@ -1671,6 +1675,23 @@ describe('rooms endpoints', () => {
|
|||||||
expect(await roomExists()).toBe(true)
|
expect(await roomExists()).toBe(true)
|
||||||
expect(await del(staff)).toMatchObject({ Success: true })
|
expect(await del(staff)).toMatchObject({ Success: true })
|
||||||
expect(await roomExists()).toBe(false)
|
expect(await roomExists()).toBe(false)
|
||||||
|
|
||||||
|
// The takedown is the one row in the audit log: the owner's own deletion in the test
|
||||||
|
// above wrote none, and the refused attempts wrote none. It names the actor and keeps
|
||||||
|
// enough of the room to say which one it was, now that the room itself is gone.
|
||||||
|
const { results: audit } = await env.DB.prepare(
|
||||||
|
'SELECT player_id, action, data FROM audit_log WHERE action = ?1'
|
||||||
|
)
|
||||||
|
.bind('room_delete')
|
||||||
|
.all<{ player_id: number; action: string; data: string }>()
|
||||||
|
expect(audit).toHaveLength(1)
|
||||||
|
expect(audit[0].player_id).toBe(999)
|
||||||
|
expect(JSON.parse(audit[0].data)).toEqual({
|
||||||
|
roomId: 9501,
|
||||||
|
name: 'TakeMeDown',
|
||||||
|
creatorAccountId: 1,
|
||||||
|
accessibility: 1,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('PUT /rooms/:id/roles/:accountId grants a helper role outright, but never co-owner', async () => {
|
it('PUT /rooms/:id/roles/:accountId grants a helper role outright, but never co-owner', async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user