mirror of
https://github.com/djdevin/recflare.git
synced 2026-09-08 14:41:28 -07:00
allow co-owners to edit tags
This commit is contained in:
+12
-11
@@ -1250,9 +1250,9 @@ const app = new Hono<App>()
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Toggle a tag on a room. Auth-gated (401) and owner-only. Body is the `tag`
|
// Toggle a tag on a room. Auth-gated (401) and owner/co-owner-only (403). Body is
|
||||||
// form field. There's no delete/patch endpoint, so this call toggles: it adds
|
// the `tag` form field. There's no delete/patch endpoint, so this call toggles: it
|
||||||
// the tag (Type 0) if absent and removes it if present. The "main" tags
|
// adds the tag (Type 0) if absent and removes it if present. The "main" tags
|
||||||
// (#pvp/#quest/#game/#hangout/#art) are radio buttons — setting one clears the
|
// (#pvp/#quest/#game/#hangout/#art) are radio buttons — setting one clears the
|
||||||
// others. Returns the `{ success, error, value }` envelope with the updated
|
// others. Returns the `{ success, error, value }` envelope with the updated
|
||||||
// room as `value`; business failures are 200 with success:false.
|
// room as `value`; business failures are 200 with success:false.
|
||||||
@@ -1262,11 +1262,11 @@ const app = new Hono<App>()
|
|||||||
tags: ['Room settings'],
|
tags: ['Room settings'],
|
||||||
summary: 'Toggle a tag on a room',
|
summary: 'Toggle a tag on a room',
|
||||||
description: [
|
description: [
|
||||||
'Owner-only. There is no delete/patch counterpart, so this call TOGGLES: it adds the',
|
'Owner or co-owner only (403 otherwise). There is no delete/patch counterpart, so',
|
||||||
'tag (Type 0) when absent and removes it when present. The “main” tags',
|
'this call TOGGLES: it adds the tag (Type 0) when absent and removes it when',
|
||||||
'(`pvp`/`quest`/`game`/`hangout`/`art`) behave as radio buttons — setting one clears',
|
'present. The “main” tags (`pvp`/`quest`/`game`/`hangout`/`art`) behave as radio',
|
||||||
'the others. Answers the lowercase envelope with the updated room, which the client',
|
'buttons — setting one clears the others. Answers the lowercase envelope with the',
|
||||||
're-renders from.',
|
'updated room, which the client re-renders from.',
|
||||||
].join(' '),
|
].join(' '),
|
||||||
security: AUTHED,
|
security: AUTHED,
|
||||||
parameters: [roomIdParam],
|
parameters: [roomIdParam],
|
||||||
@@ -1274,6 +1274,7 @@ const app = new Hono<App>()
|
|||||||
responses: {
|
responses: {
|
||||||
200: json(RoomEnvelope, 'The updated room, or a rejection with `success: false`'),
|
200: json(RoomEnvelope, 'The updated room, or a rejection with `success: false`'),
|
||||||
401: UNAUTHORIZED_RESPONSE,
|
401: UNAUTHORIZED_RESPONSE,
|
||||||
|
403: FORBIDDEN_RESPONSE,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
@@ -1283,9 +1284,9 @@ const app = new Hono<App>()
|
|||||||
const roomId = Number.parseInt(c.req.param('roomId'), 10)
|
const roomId = Number.parseInt(c.req.param('roomId'), 10)
|
||||||
const room = await getRoomById(c.env.DB, roomId)
|
const room = await getRoomById(c.env.DB, roomId)
|
||||||
if (!room) return roomEnvelope(c, null, 'This room does not exist!')
|
if (!room) return roomEnvelope(c, null, 'This room does not exist!')
|
||||||
if (room.CreatorAccountId !== accountId) {
|
// A valid token but not the room's owner/co-owner → 403 (the auth gate above
|
||||||
return roomEnvelope(c, null, 'You are not the owner of this room!')
|
// already returned 401 for a missing/invalid token).
|
||||||
}
|
if (!canManageRoom(room, accountId)) return c.body(null, 403)
|
||||||
|
|
||||||
const body = (await c.req.parseBody().catch(() => ({}))) as Record<string, unknown>
|
const body = (await c.req.parseBody().catch(() => ({}))) as Record<string, unknown>
|
||||||
const tag = typeof body.tag === 'string' ? body.tag.trim() : ''
|
const tag = typeof body.tag === 'string' ? body.tag.trim() : ''
|
||||||
|
|||||||
@@ -1743,7 +1743,7 @@ describe('rooms endpoints', () => {
|
|||||||
expect(clone.CurrentSave!.SubRoomId).toBe(clone.SubRoomId)
|
expect(clone.CurrentSave!.SubRoomId).toBe(clone.SubRoomId)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('PUT /rooms/:id/tags is auth-gated, owner-only, and toggles (add/remove)', async () => {
|
it('PUT /rooms/:id/tags is auth-gated, owner/co-owner-only, and toggles (add/remove)', async () => {
|
||||||
// The lowercase `{ success, error, value }` envelope this endpoint returns.
|
// The lowercase `{ success, error, value }` envelope this endpoint returns.
|
||||||
type TagResult = {
|
type TagResult = {
|
||||||
success: boolean
|
success: boolean
|
||||||
@@ -1755,11 +1755,8 @@ describe('rooms endpoints', () => {
|
|||||||
|
|
||||||
// No token → 401.
|
// No token → 401.
|
||||||
expect((await putForm('/rooms/2/tags', { tag: 'quest' })).status).toBe(401)
|
expect((await putForm('/rooms/2/tags', { tag: 'quest' })).status).toBe(401)
|
||||||
// Not the owner → failure envelope.
|
// A valid token but no role on the room → 403.
|
||||||
expect(await envOf(await putForm('/rooms/2/tags', { tag: 'quest' }, '999'))).toMatchObject({
|
expect((await putForm('/rooms/2/tags', { tag: 'quest' }, '999')).status).toBe(403)
|
||||||
success: false,
|
|
||||||
error: 'You are not the owner of this room!',
|
|
||||||
})
|
|
||||||
// Unknown room → failure envelope.
|
// Unknown room → failure envelope.
|
||||||
expect(await envOf(await putForm('/rooms/99999/tags', { tag: 'quest' }, '1'))).toMatchObject({
|
expect(await envOf(await putForm('/rooms/99999/tags', { tag: 'quest' }, '1'))).toMatchObject({
|
||||||
success: false,
|
success: false,
|
||||||
@@ -1795,6 +1792,11 @@ describe('rooms endpoints', () => {
|
|||||||
const off = await envOf(await putForm('/rooms/2/tags', { tag: 'quest' }, '1'))
|
const off = await envOf(await putForm('/rooms/2/tags', { tag: 'quest' }, '1'))
|
||||||
expect(tagsIn(off)).not.toContain('quest')
|
expect(tagsIn(off)).not.toContain('quest')
|
||||||
expect(tagsIn(off)).toContain('campfire')
|
expect(tagsIn(off)).toContain('campfire')
|
||||||
|
|
||||||
|
// The co-owner (account 2, Role 30) may edit tags too.
|
||||||
|
const byCoOwner = await envOf(await putForm('/rooms/2/tags', { tag: 'spooky' }, '2'))
|
||||||
|
expect(byCoOwner).toMatchObject({ success: true, error: '' })
|
||||||
|
expect(tagsIn(byCoOwner)).toContain('spooky')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('PUT /rooms/:id/name is auth-gated, owner-only, unique, and persists', async () => {
|
it('PUT /rooms/:id/name is auth-gated, owner-only, unique, and persists', async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user