mirror of
https://github.com/djdevin/recflare.git
synced 2026-09-08 14:41:28 -07:00
true matchmaking
This commit is contained in:
@@ -2,6 +2,8 @@ import type { HonoApp } from '@repo/hono-helpers'
|
||||
import type { SharedHonoEnv, SharedHonoVariables } from '@repo/hono-helpers/src/types'
|
||||
|
||||
export type Env = SharedHonoEnv & {
|
||||
/** Shared `recflare` D1 — the `images` metadata table this worker owns. */
|
||||
DB: D1Database
|
||||
/** R2 bucket holding the served image objects, keyed by filename. */
|
||||
IMAGES: R2Bucket
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Image-metadata storage on the shared `recflare` D1 database. Each image is a
|
||||
* single JSON blob in the `data` column; queryable fields (Id, ImageName,
|
||||
* PlayerId, RoomId) are SQLite generated (virtual) columns extracted from that
|
||||
* JSON — the same JSON-blob pattern the rooms/accounts tables use.
|
||||
*
|
||||
* The `img` worker owns this schema/migration (migrations/0001_images.sql, applied
|
||||
* with its own `migrations_table` so it doesn't clash with the other workers'
|
||||
* migrations on the shared database). The `api` worker writes a row on upload and
|
||||
* reads it back, keeping its own copy of these helpers in sync.
|
||||
*/
|
||||
|
||||
/** Schema DDL (mirror of migrations/0001_image.sql, sans any seed rows). */
|
||||
export const SCHEMA_DDL: string[] = [
|
||||
`CREATE TABLE IF NOT EXISTS image (
|
||||
data TEXT NOT NULL,
|
||||
id INTEGER GENERATED ALWAYS AS (json_extract(data, '$.Id')) VIRTUAL,
|
||||
image_name TEXT GENERATED ALWAYS AS (json_extract(data, '$.ImageName')) VIRTUAL,
|
||||
player_id INTEGER GENERATED ALWAYS AS (json_extract(data, '$.PlayerId')) VIRTUAL,
|
||||
room_id INTEGER GENERATED ALWAYS AS (json_extract(data, '$.RoomId')) VIRTUAL
|
||||
)`,
|
||||
`CREATE UNIQUE INDEX IF NOT EXISTS idx_image_id ON image (id)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_image_image_name ON image (image_name)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_image_player_id ON image (player_id)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_image_room_id ON image (room_id)`,
|
||||
]
|
||||
|
||||
/** A stored image record (the client-facing SavedImage shape). */
|
||||
export interface SavedImage {
|
||||
Id: number
|
||||
Type: number
|
||||
Accessibility: number
|
||||
AccessibilityLocked: boolean
|
||||
ImageName: string
|
||||
Description: string | null
|
||||
PlayerId: number
|
||||
TaggedPlayerIds: number[]
|
||||
RoomId: number | null
|
||||
PlayerEventId: number | null
|
||||
CreatedAt: string
|
||||
CheerCount: number
|
||||
CommentCount: number
|
||||
}
|
||||
|
||||
interface ImageRow {
|
||||
data: string
|
||||
}
|
||||
|
||||
/** Fields supplied at upload time (from `imgMeta`); everything else defaults. */
|
||||
export interface NewImage {
|
||||
imageName: string
|
||||
playerId: number
|
||||
type?: number
|
||||
accessibility?: number
|
||||
roomId?: number | null
|
||||
description?: string | null
|
||||
taggedPlayerIds?: number[]
|
||||
playerEventId?: number | null
|
||||
}
|
||||
|
||||
/** Insert a new image record for an upload, returning the stored row. */
|
||||
export async function createImage(db: D1Database, input: NewImage): Promise<SavedImage> {
|
||||
// Sequential id: one past the current max (the table starts empty).
|
||||
const row = await db
|
||||
.prepare('SELECT COALESCE(MAX(id), 0) + 1 AS next FROM image')
|
||||
.first<{ next: number }>()
|
||||
const image: SavedImage = {
|
||||
Id: row?.next ?? 1,
|
||||
Type: input.type ?? 1,
|
||||
Accessibility: input.accessibility ?? 1,
|
||||
AccessibilityLocked: false,
|
||||
ImageName: input.imageName,
|
||||
Description: input.description ?? null,
|
||||
PlayerId: input.playerId,
|
||||
TaggedPlayerIds: input.taggedPlayerIds ?? [],
|
||||
RoomId: input.roomId ?? null,
|
||||
PlayerEventId: input.playerEventId ?? null,
|
||||
CreatedAt: new Date().toISOString(),
|
||||
CheerCount: 0,
|
||||
CommentCount: 0,
|
||||
}
|
||||
await db.prepare('INSERT INTO image (data) VALUES (?1)').bind(JSON.stringify(image)).run()
|
||||
return image
|
||||
}
|
||||
|
||||
/** Look up an image record by its ImageName (the R2 key / filename), or null. */
|
||||
export async function getImageByName(db: D1Database, name: string): Promise<SavedImage | null> {
|
||||
const row = await db
|
||||
.prepare('SELECT data FROM image WHERE image_name = ?1')
|
||||
.bind(name)
|
||||
.first<ImageRow>()
|
||||
return row ? (JSON.parse(row.data) as SavedImage) : null
|
||||
}
|
||||
Reference in New Issue
Block a user