turso migration

This commit is contained in:
Devin Zuczek
2026-09-27 15:38:52 -04:00
parent b32b16c1f0
commit 3f2cf2e700
34 changed files with 946 additions and 49 deletions
+3 -2
View File
@@ -11,6 +11,7 @@ import {
getAccountsByIds,
searchAccounts,
updateAccount,
withDatabase,
} from '@repo/domain'
import {
logger,
@@ -55,7 +56,7 @@ import { resolveWhitelistedEmoji, WHITELISTED_EMOJIS } from './whitelisted-emoji
import type { Context } from 'hono'
import type { DescribeRouteOptions } from 'hono-openapi'
import type { Account } from '@repo/domain'
import type { App } from './context'
import type { App, Env } from './context'
/**
* Account reads/writes are backed by the shared `accounts` table in D1 (schema
@@ -856,4 +857,4 @@ app.get(
)
)
export default app
export default withDatabase<Env>(app)
+3 -2
View File
@@ -2,6 +2,7 @@ import { Hono } from 'hono'
import { describeRoute, openAPIRouteHandler } from 'hono-openapi'
import { useWorkersLogger } from 'workers-tagged-logger'
import { withDatabase } from '@repo/domain'
import { withCleanSpec, withDefaultCors, withNotFound, withOnError } from '@repo/hono-helpers'
import { accountRoutes } from './routes/account'
@@ -16,7 +17,7 @@ import { progressionRoutes } from './routes/progression'
import { roomRoutes } from './routes/rooms'
import { socialRoutes } from './routes/social'
import type { App } from './context'
import type { App, Env } from './context'
/**
* The Game API surface. Endpoints that would be backed by a database or on-disk
@@ -105,4 +106,4 @@ app.get(
)
)
export default app
export default withDatabase<Env>(app)
+3 -2
View File
@@ -21,6 +21,7 @@ import {
subRoomDataBlob,
updateAccount,
verifyPassword,
withDatabase,
writeAuditLog,
} from '@repo/domain'
import {
@@ -65,7 +66,7 @@ import { verifySteamTicket } from './steam-ticket'
import type { Context } from 'hono'
import type { Account } from '@repo/domain'
import type { App } from './context'
import type { App, Env } from './context'
import type { PlatformLink } from './platform-db'
/** OAuth scopes granted by `/connect/token`. */
@@ -1351,4 +1352,4 @@ app.get(
)
)
export default app
export default withDatabase<Env>(app)
+1
View File
@@ -17,6 +17,7 @@
},
"dependencies": {
"@2toad/profanity": "3.3.0",
"@repo/domain": "workspace:*",
"@repo/hono-helpers": "workspace:*",
"@repo/jwt": "workspace:*",
"@standard-community/standard-json": "0.3.5",
+3 -2
View File
@@ -2,6 +2,7 @@ import { Hono } from 'hono'
import { describeRoute, openAPIRouteHandler } from 'hono-openapi'
import { useWorkersLogger } from 'workers-tagged-logger'
import { withDatabase } from '@repo/domain'
import { logger, withCleanSpec, withNotFound, withOnError } from '@repo/hono-helpers'
import { validateAndGetAccountId } from '@repo/jwt'
@@ -40,9 +41,9 @@ import {
ChatThreadType,
createThread,
getOrCreateThreadWithMembers,
getPartyThreadForPlayer,
getThreadForPlayer,
getThreadMemberIds,
getPartyThreadForPlayer,
getThreadMeta,
getThreadsForPlayer,
isThreadMember,
@@ -1557,4 +1558,4 @@ app.get(
)
)
export default app
export default withDatabase<Env>(app)
+3 -2
View File
@@ -36,6 +36,7 @@ import {
setMemberType,
subscribeToClub,
updateClub,
withDatabase,
} from '@repo/domain'
import { intVar, logger, withCleanSpec, withNotFound, withOnError } from '@repo/hono-helpers'
import { validateAndGetAccountId } from '@repo/jwt'
@@ -76,7 +77,7 @@ import {
} from './openapi'
import type { Context } from 'hono'
import type { App } from './context'
import type { App, Env } from './context'
/**
* Clubs Worker. Hosts the club endpoints the game client calls on the `clubs` host:
@@ -1706,4 +1707,4 @@ app.get(
)
)
export default app
export default withDatabase<Env>(app)
+2 -1
View File
@@ -17,6 +17,7 @@ import {
levelsReached,
ownsInvention,
setOutfit,
withDatabase,
} from '@repo/domain'
import { intVar, logger, withCleanSpec, withNotFound, withOnError } from '@repo/hono-helpers'
import { validateAndGetAccountId, validateAndGetPlus, validateAndGetVersion } from '@repo/jwt'
@@ -5918,4 +5919,4 @@ export const scheduled: ExportedHandlerScheduledHandler<Env> = (_controller, env
// A Worker only runs `scheduled` when it's on the default export, so econ takes the object
// form the runtime requires (as `www` does for its role sweep and `match` for presence).
export default { fetch: app.fetch, scheduled } satisfies ExportedHandler<Env>
export default withDatabase<Env>({ fetch: app.fetch, scheduled })
+1
View File
@@ -17,6 +17,7 @@
},
"dependencies": {
"@cf-wasm/photon": "^0.3.6",
"@repo/domain": "workspace:*",
"@repo/hono-helpers": "workspace:*",
"@standard-community/standard-json": "0.3.5",
"@standard-community/standard-openapi": "0.2.9",
+2 -1
View File
@@ -3,6 +3,7 @@ import { Hono } from 'hono'
import { describeRoute, openAPIRouteHandler } from 'hono-openapi'
import { useWorkersLogger } from 'workers-tagged-logger'
import { withDatabase } from '@repo/domain'
import { withCleanSpec, withNotFound, withOnError, writeContentRange } from '@repo/hono-helpers'
import { imageBytes, json, ServiceStatus } from './openapi'
@@ -562,4 +563,4 @@ app.get(
}
)
export default app
export default withDatabase<Env>(app)
+5 -4
View File
@@ -2,6 +2,7 @@ import { Hono } from 'hono'
import { describeRoute, openAPIRouteHandler } from 'hono-openapi'
import { useWorkersLogger } from 'workers-tagged-logger'
import { withDatabase } from '@repo/domain'
import { logger, withCleanSpec, withNotFound, withOnError } from '@repo/hono-helpers'
import { validateAndGetAccountId } from '@repo/jwt'
@@ -14,7 +15,6 @@ import {
NO_SCORE,
UNRANKED,
} from './leaderboard-db'
import {
CheckAndSetStatBody,
CheckAndSetStatResponse,
@@ -27,7 +27,7 @@ import {
PlayerRank,
} from './openapi'
import type { App } from './context'
import type { App, Env } from './context'
import type { Board } from './leaderboard-db'
/**
@@ -73,7 +73,8 @@ function board(body: BoardBody): Board {
return {
roomId: int(body.RoomId, 0),
statChannel: int(body.StatChannel, 0),
...(int(body.FilterType, 0) === FilterType.Friends && playerId !== 0 && { friendsOf: playerId }),
...(int(body.FilterType, 0) === FilterType.Friends &&
playerId !== 0 && { friendsOf: playerId }),
}
}
@@ -353,4 +354,4 @@ app.get(
)
)
export default app
export default withDatabase<Env>(app)
+3 -2
View File
@@ -10,6 +10,7 @@ import {
getPlayerList,
getRecentlyUpdatedRooms,
getVisitedRooms,
withDatabase,
} from '@repo/domain'
import { withCleanSpec, withNotFound, withOnError } from '@repo/hono-helpers'
import { validateAndGetAccountId } from '@repo/jwt'
@@ -41,7 +42,7 @@ import {
import type { Context } from 'hono'
import type { CuratedList, Room } from '@repo/domain'
import type { CatalogKindValue } from '../../econ/src/catalog-load'
import type { App } from './context'
import type { App, Env } from './context'
// The item catalog's sellable-rarity rule, from the worker that owns the table. Imported
// rather than restated so a row can never offer an id the storefront omits.
@@ -1006,4 +1007,4 @@ app.get(
)
)
export default app
export default withDatabase<Env>(app)
+2 -1
View File
@@ -45,6 +45,7 @@ import {
setRoomInstanceInProgress,
setRoomInstancePrivate,
subRoomDataBlob,
withDatabase,
} from '@repo/domain'
import { logger, withCleanSpec, withNotFound, withOnError } from '@repo/hono-helpers'
import { generatePhotonAuthToken, validateAndGetAccountId, validateAndGetVersion } from '@repo/jwt'
@@ -3365,4 +3366,4 @@ export const scheduled: ExportedHandlerScheduledHandler<Env> = (_controller, env
// Standalone entry: a Worker only runs `scheduled` when it's on the default export,
// so match keeps the object form the runtime requires to fire its `*/5 * * * *` cron.
export default { fetch: app.fetch, scheduled } satisfies ExportedHandler<Env>
export default withDatabase<Env>({ fetch: app.fetch, scheduled })
+27 -15
View File
@@ -20,6 +20,8 @@
* Cloudflare allows only one static-assets binding per Worker. Resolve that (serve
* their static trees from R2, or keep those three as their own Workers) before adding.
*/
import { withDatabase } from '@repo/domain'
import accounts from '../../accounts/src/accounts.app'
import api from '../../api/src/api.app'
import auth from '../../auth/src/auth.app'
@@ -45,24 +47,34 @@ type Mounted = {
fetch(request: Request, env: Env, ctx: ExecutionContext): Response | Promise<Response>
}
/**
* Mount an app on this worker's Env. Each app declares only the bindings it reads, and
* this Env is the union of them, so the app's narrower Env is not checked against this one
* here — as it never was through Hono's `fetch`, which accepts any env. `withDatabase`
* (@repo/domain) types a wrapped app's `fetch` by its own Env, which is what needs the cast.
*/
const mount = (app: {
fetch(request: Request, env: never, ctx: ExecutionContext): Response | Promise<Response>
}): Mounted => app as Mounted
/**
* Subdomain -> mounted app. Keys must match the `<sub>` in `<sub>.<domain>` from the
* `ns` service-discovery document so production host-based routing lines up.
*/
const services = {
accounts,
api,
auth,
cdn,
chat,
clubs,
commerce,
match,
notify,
ns,
playersettings,
rooms,
storage,
accounts: mount(accounts),
api: mount(api),
auth: mount(auth),
cdn: mount(cdn),
chat: mount(chat),
clubs: mount(clubs),
commerce: mount(commerce),
match: mount(match),
notify: mount(notify),
ns: mount(ns),
playersettings: mount(playersettings),
rooms: mount(rooms),
storage: mount(storage),
} satisfies Record<string, Mounted>
type ServiceName = keyof typeof services
@@ -90,7 +102,7 @@ function resolve(request: Request): { name: ServiceName; request: Request } | un
return undefined
}
export default {
export default withDatabase<Env>({
fetch(request: Request, env: Env, ctx: ExecutionContext): Response | Promise<Response> {
const resolved = resolve(request)
if (resolved === undefined) {
@@ -117,4 +129,4 @@ export default {
matchScheduled(controller, env, ctx)
roomsScheduled(controller, env, ctx)
},
} satisfies ExportedHandler<Env>
})
+3 -3
View File
@@ -1,7 +1,7 @@
import { Hono } from 'hono'
import { useWorkersLogger } from 'workers-tagged-logger'
import { createNotification, writeAuditLog } from '@repo/domain'
import { createNotification, withDatabase, writeAuditLog } from '@repo/domain'
import { logger, withDefaultCors, withNotFound, withOnError } from '@repo/hono-helpers'
import { validateAndGetAccountId, validateAndGetRoles } from '@repo/jwt'
@@ -13,7 +13,7 @@ import {
} from './notifications-hub'
import type { Context, MiddlewareHandler } from 'hono'
import type { App } from './context'
import type { App, Env } from './context'
/**
* Maps a SignalR hub at `/hub/v1`. The hub itself — WebSocket transport, the
@@ -371,4 +371,4 @@ const app = new Hono<App>()
})
export { NotificationsHub }
export default app
export default withDatabase<Env>(app)
+8 -3
View File
@@ -2,7 +2,12 @@ import { Hono } from 'hono'
import { describeRoute, openAPIRouteHandler } from 'hono-openapi'
import { useWorkersLogger } from 'workers-tagged-logger'
import { createRoomComment, DEFAULT_COMMENT_COUNT, getRoomComments } from '@repo/domain'
import {
createRoomComment,
DEFAULT_COMMENT_COUNT,
getRoomComments,
withDatabase,
} from '@repo/domain'
import { withCleanSpec, withNotFound, withOnError } from '@repo/hono-helpers'
import { validateAndGetAccountId } from '@repo/jwt'
@@ -17,7 +22,7 @@ import {
} from './openapi'
import type { Context } from 'hono'
import type { App } from './context'
import type { App, Env } from './context'
/**
* Room Comments Worker. Serves the notes a player pins in a room's scene — a message, a
@@ -257,4 +262,4 @@ app.get(
)
)
export default app
export default withDatabase<Env>(app)
+2 -1
View File
@@ -74,6 +74,7 @@ import {
transferRoomOwnership,
unbanPlayerFromRoom,
updateRoomFields,
withDatabase,
writeAuditLog,
} from '@repo/domain'
import {
@@ -4455,4 +4456,4 @@ export const scheduled: ExportedHandlerScheduledHandler<Env> = (_controller, env
// Standalone entry: a Worker only runs `scheduled` when it's on the default export,
// so rooms keeps the object form the runtime requires to fire its `*/5 * * * *` cron.
export default { fetch: app.fetch, scheduled } satisfies ExportedHandler<Env>
export default withDatabase<Env>({ fetch: app.fetch, scheduled })
+2 -1
View File
@@ -1,6 +1,7 @@
import { Hono } from 'hono'
import { useWorkersLogger } from 'workers-tagged-logger'
import { withDatabase } from '@repo/domain'
import { getAccount, updateAccount } from '@repo/domain/src/accounts-db'
import { PlatformType } from '@repo/domain/src/enums'
import { countOnlinePlayers } from '@repo/domain/src/presence-db'
@@ -539,4 +540,4 @@ export const scheduled: ExportedHandlerScheduledHandler<Env> = (_controller, env
// A Worker only runs `scheduled` when it's on the default export, so www takes the object
// form the runtime requires (as `match` does for its presence sweep).
export default { fetch: app.fetch, scheduled } satisfies ExportedHandler<Env>
export default withDatabase<Env>({ fetch: app.fetch, scheduled })