clean up some social endpoints, sockets not working yet

This commit is contained in:
Devin Zuczek
2026-07-16 13:15:06 -04:00
parent 710031b2a3
commit 2ff6526834
7 changed files with 210 additions and 51 deletions
+66 -10
View File
@@ -1,5 +1,7 @@
import { Hono } from 'hono'
import { logger } from '@repo/hono-helpers'
import { authedId, unauthorized } from '../http'
import {
acceptFriendRequest,
@@ -12,6 +14,43 @@ import {
import type { Context } from 'hono'
import type { App } from '../context'
import type { RelationshipFlag } from '../relationships-db'
/** The notifications hub is a single global DO instance (see the `notify` worker). */
const HUB_INSTANCE = 'global'
/** NotificationType.RelationshipChanged (see apps/notify/src/notification-types.ts). */
const RELATIONSHIP_CHANGED = 1
/**
* Apply a per-player relationship flag toggle (favorited/ignored/muted) and hand the
* result to the client the way the Go server does: the resulting relationship rides a
* `RelationshipChanged` hub notification to the caller, and the HTTP body is just the
* `{ Success, Message }` ack. Hub failures are logged and swallowed — the DB write has
* already committed, so a hub hiccup must not fail the request.
*/
async function applyFlag(
c: Context<App>,
playerId: number,
otherId: number,
flag: RelationshipFlag,
value: boolean
): Promise<Response> {
const rel = await setRelationshipFlag(c.env.DB, playerId, otherId, flag, value)
try {
await c.env.RECFLARE_NOTIFICATIONS_HUB.getByName(HUB_INSTANCE).notifyPlayer(
playerId,
RELATIONSHIP_CHANGED,
{ ...rel }
)
} catch (err) {
logger.error('failed to push RelationshipChanged notification', {
playerId,
error: err instanceof Error ? err.message : String(err),
})
}
return c.json({ Success: true, Message: '' })
}
/**
* Read the other player's id from a relationship-mutation request. The exact wire
@@ -89,43 +128,60 @@ export const socialRoutes = new Hono<App>({ strict: false })
return c.json(await addFriend(c.env.DB, id, target))
})
// Ignore / mute another player (target arrives as `PlayerId` in the POST body).
// These set a per-player flag on the *caller's* side of the relationship row,
// creating a bare (None) row when the pair aren't otherwise related — so you can
// ignore/mute someone you've never friended. Auth-gated. Returns the resulting
// relationship from the caller's point of view.
// Ignore / mute another player, and their inverses unignore / unmute (target
// arrives as `PlayerId` in the POST body). These set a per-player flag on the
// *caller's* side of the relationship row, creating a bare (None) row when the
// pair aren't otherwise related — so you can ignore/mute someone you've never
// friended. The un- variants just clear the same flag. Auth-gated. The resulting
// relationship is delivered via a RelationshipChanged hub notification (see
// applyFlag); the HTTP body is just the { Success, Message } ack.
.on(['GET', 'POST'], '/api/relationships/v1/ignore', async (c) => {
const id = await authedId(c)
if (id === null) return unauthorized(c)
const target = await targetPlayerId(c)
if (target === null || target === id) return c.json({ error: 'invalid player id' }, 400)
return c.json(await setRelationshipFlag(c.env.DB, id, target, 'ignored', true))
return applyFlag(c, id, target, 'ignored', true)
})
.on(['GET', 'POST'], '/api/relationships/v1/unignore', async (c) => {
const id = await authedId(c)
if (id === null) return unauthorized(c)
const target = await targetPlayerId(c)
if (target === null || target === id) return c.json({ error: 'invalid player id' }, 400)
return applyFlag(c, id, target, 'ignored', false)
})
.on(['GET', 'POST'], '/api/relationships/v1/mute', async (c) => {
const id = await authedId(c)
if (id === null) return unauthorized(c)
const target = await targetPlayerId(c)
if (target === null || target === id) return c.json({ error: 'invalid player id' }, 400)
return c.json(await setRelationshipFlag(c.env.DB, id, target, 'muted', true))
return applyFlag(c, id, target, 'muted', true)
})
.on(['GET', 'POST'], '/api/relationships/v1/unmute', async (c) => {
const id = await authedId(c)
if (id === null) return unauthorized(c)
const target = await targetPlayerId(c)
if (target === null || target === id) return c.json({ error: 'invalid player id' }, 400)
return applyFlag(c, id, target, 'muted', false)
})
// Favorite / unfavorite another player (the client calls these as a GET with the
// target in `?id=`). Same per-side flag mechanics as ignore/mute above: the write
// lands on the *caller's* side of the row, and favoriting someone you have no
// relationship with creates a bare (None) row. Auth-gated.
// relationship with creates a bare (None) row. Auth-gated. Result rides a
// RelationshipChanged notification; the body is the { Success, Message } ack.
.on(['GET', 'POST'], '/api/relationships/v1/favorite', async (c) => {
const id = await authedId(c)
if (id === null) return unauthorized(c)
const target = await targetPlayerId(c)
if (target === null || target === id) return c.json({ error: 'invalid player id' }, 400)
return c.json(await setRelationshipFlag(c.env.DB, id, target, 'favorited', true))
return applyFlag(c, id, target, 'favorited', true)
})
.on(['GET', 'POST'], '/api/relationships/v1/unfavorite', async (c) => {
const id = await authedId(c)
if (id === null) return unauthorized(c)
const target = await targetPlayerId(c)
if (target === null || target === id) return c.json({ error: 'invalid player id' }, 400)
return c.json(await setRelationshipFlag(c.env.DB, id, target, 'favorited', false))
return applyFlag(c, id, target, 'favorited', false)
})
.get('/api/messages/v2/get', (c) => c.json([]))