diff --git a/apps/aris-backend/package.json b/apps/aris-backend/package.json index 6d8abd4..129ef05 100644 --- a/apps/aris-backend/package.json +++ b/apps/aris-backend/package.json @@ -13,8 +13,6 @@ "@aris/source-location": "workspace:*", "@aris/source-tfl": "workspace:*", "@aris/source-weatherkit": "workspace:*", - "@hono/trpc-server": "^0.3", - "@trpc/server": "^11", "arktype": "^2.1.29", "better-auth": "^1", "hono": "^4", diff --git a/apps/aris-backend/src/auth/session-middleware.ts b/apps/aris-backend/src/auth/session-middleware.ts index 85c51d7..cc4aba3 100644 --- a/apps/aris-backend/src/auth/session-middleware.ts +++ b/apps/aris-backend/src/auth/session-middleware.ts @@ -1,13 +1,16 @@ import type { Context, Next } from "hono" +import type { AuthSession, AuthUser } from "./session.ts" + import { auth } from "./index.ts" -type SessionUser = typeof auth.$Infer.Session.user -type Session = typeof auth.$Infer.Session.session - export interface SessionVariables { - user: SessionUser | null - session: Session | null + user: AuthUser | null + session: AuthSession | null +} + +declare module "hono" { + interface ContextVariableMap extends SessionVariables {} } /** @@ -48,7 +51,7 @@ export async function requireSession(c: Context, next: Next): Promise { +): Promise<{ user: AuthUser; session: AuthSession } | null> { const session = await auth.api.getSession({ headers }) return session } diff --git a/apps/aris-backend/src/auth/session.ts b/apps/aris-backend/src/auth/session.ts new file mode 100644 index 0000000..187cf48 --- /dev/null +++ b/apps/aris-backend/src/auth/session.ts @@ -0,0 +1,4 @@ +import type { auth } from "./index.ts" + +export type AuthUser = typeof auth.$Infer.Session.user +export type AuthSession = typeof auth.$Infer.Session.session diff --git a/apps/aris-backend/src/location/http.ts b/apps/aris-backend/src/location/http.ts new file mode 100644 index 0000000..083cbd8 --- /dev/null +++ b/apps/aris-backend/src/location/http.ts @@ -0,0 +1,56 @@ +import type { Context, Hono } from "hono" + +import { type } from "arktype" +import { createMiddleware } from "hono/factory" + +import type { UserSessionManager } from "../session/index.ts" + +import { requireSession } from "../auth/session-middleware.ts" + +type Env = { Variables: { sessionManager: UserSessionManager } } + +const locationInput = type({ + lat: "number", + lng: "number", + accuracy: "number", + timestamp: "string.date.iso", +}) + +export function registerLocationHttpHandlers( + app: Hono, + { sessionManager }: { sessionManager: UserSessionManager }, +) { + const inject = createMiddleware(async (c, next) => { + c.set("sessionManager", sessionManager) + await next() + }) + + app.post("/api/location", inject, requireSession, handleUpdateLocation) +} + +async function handleUpdateLocation(c: Context) { + let body: unknown + try { + body = await c.req.json() + } catch { + return c.json({ error: "Invalid JSON" }, 400) + } + + const result = locationInput(body) + + if (result instanceof type.errors) { + return c.json({ error: result.summary }, 400) + } + + const user = c.get("user")! + const sessionManager = c.get("sessionManager") + const session = sessionManager.getOrCreate(user.id) + await session.engine.executeAction("aris.location", "update-location", { + lat: result.lat, + lng: result.lng, + accuracy: result.accuracy, + timestamp: new Date(result.timestamp), + }) + + return c.body(null, 204) +} diff --git a/apps/aris-backend/src/location/router.ts b/apps/aris-backend/src/location/router.ts deleted file mode 100644 index 0dcd47c..0000000 --- a/apps/aris-backend/src/location/router.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { type } from "arktype" - -import type { UserSessionManager } from "../session/index.ts" -import type { TRPC } from "../trpc/router.ts" - -const locationInput = type({ - lat: "number", - lng: "number", - accuracy: "number", - timestamp: "Date", -}) - -export function createLocationRouter( - t: TRPC, - { sessionManager }: { sessionManager: UserSessionManager }, -) { - return t.router({ - update: t.procedure.input(locationInput).mutation(async ({ input, ctx }) => { - const session = sessionManager.getOrCreate(ctx.user.id) - await session.engine.executeAction("aris.location", "update-location", { - lat: input.lat, - lng: input.lng, - accuracy: input.accuracy, - timestamp: input.timestamp, - }) - }), - }) -} diff --git a/apps/aris-backend/src/server.ts b/apps/aris-backend/src/server.ts index 553a6f6..5b907a4 100644 --- a/apps/aris-backend/src/server.ts +++ b/apps/aris-backend/src/server.ts @@ -1,12 +1,10 @@ import { LocationSource } from "@aris/source-location" -import { trpcServer } from "@hono/trpc-server" import { Hono } from "hono" import { registerAuthHandlers } from "./auth/http.ts" +import { registerLocationHttpHandlers } from "./location/http.ts" import { UserSessionManager } from "./session/index.ts" import { WeatherSourceProvider } from "./weather/provider.ts" -import { createContext } from "./trpc/context.ts" -import { createTRPCRouter } from "./trpc/router.ts" function main() { const sessionManager = new UserSessionManager([ @@ -21,21 +19,12 @@ function main() { }), ]) - const trpcRouter = createTRPCRouter({ sessionManager }) - const app = new Hono() app.get("/health", (c) => c.json({ status: "ok" })) registerAuthHandlers(app) - - app.use( - "/trpc/*", - trpcServer({ - router: trpcRouter, - createContext, - }), - ) + registerLocationHttpHandlers(app, { sessionManager }) return app } diff --git a/apps/aris-backend/src/session/user-session-manager.test.ts b/apps/aris-backend/src/session/user-session-manager.test.ts index 187d2d2..19a7ddb 100644 --- a/apps/aris-backend/src/session/user-session-manager.test.ts +++ b/apps/aris-backend/src/session/user-session-manager.test.ts @@ -1,11 +1,11 @@ +import type { WeatherKitClient, WeatherKitResponse } from "@aris/source-weatherkit" + import { LocationSource } from "@aris/source-location" import { describe, expect, mock, test } from "bun:test" import { WeatherSourceProvider } from "../weather/provider.ts" import { UserSessionManager } from "./user-session-manager.ts" -import type { WeatherKitClient, WeatherKitResponse } from "@aris/source-weatherkit" - const mockWeatherClient: WeatherKitClient = { fetch: async () => ({}) as WeatherKitResponse, } diff --git a/apps/aris-backend/src/session/user-session-manager.ts b/apps/aris-backend/src/session/user-session-manager.ts index 441b0a1..7c9251e 100644 --- a/apps/aris-backend/src/session/user-session-manager.ts +++ b/apps/aris-backend/src/session/user-session-manager.ts @@ -1,4 +1,5 @@ import type { FeedSourceProviderInput } from "./feed-source-provider.ts" + import { UserSession } from "./user-session.ts" export class UserSessionManager { diff --git a/apps/aris-backend/src/trpc/context.ts b/apps/aris-backend/src/trpc/context.ts deleted file mode 100644 index d62265c..0000000 --- a/apps/aris-backend/src/trpc/context.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { FetchCreateContextFnOptions } from "@trpc/server/adapters/fetch" - -import { auth } from "../auth/index.ts" - -export async function createContext(opts: FetchCreateContextFnOptions) { - const session = await auth.api.getSession({ headers: opts.req.headers }) - - return { - user: session?.user ?? null, - session: session?.session ?? null, - } -} - -export type Context = Awaited> diff --git a/apps/aris-backend/src/trpc/router.ts b/apps/aris-backend/src/trpc/router.ts deleted file mode 100644 index 0141c52..0000000 --- a/apps/aris-backend/src/trpc/router.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { initTRPC, TRPCError } from "@trpc/server" - -import type { UserSessionManager } from "../session/index.ts" -import type { Context } from "./context.ts" - -import { createLocationRouter } from "../location/router.ts" - -export type TRPC = ReturnType - -export interface TRPCRouterDeps { - sessionManager: UserSessionManager -} - -export function createTRPCRouter({ sessionManager }: TRPCRouterDeps) { - const t = createTRPC() - - return t.router({ - location: createLocationRouter(t, { sessionManager }), - }) -} - -export type TRPCRouter = ReturnType - -function createTRPC() { - const t = initTRPC.context().create() - - const isAuthed = t.middleware(({ ctx, next }) => { - if (!ctx.user || !ctx.session) { - throw new TRPCError({ code: "UNAUTHORIZED" }) - } - return next({ - ctx: { - user: ctx.user, - session: ctx.session, - }, - }) - }) - - return { - router: t.router, - procedure: t.procedure.use(isAuthed), - } -} - diff --git a/bun.lock b/bun.lock index 7d18721..d577642 100644 --- a/bun.lock +++ b/bun.lock @@ -21,8 +21,6 @@ "@aris/source-location": "workspace:*", "@aris/source-tfl": "workspace:*", "@aris/source-weatherkit": "workspace:*", - "@hono/trpc-server": "^0.3", - "@trpc/server": "^11", "arktype": "^2.1.29", "better-auth": "^1", "hono": "^4", @@ -468,8 +466,6 @@ "@hapi/topo": ["@hapi/topo@5.1.0", "", { "dependencies": { "@hapi/hoek": "^9.0.0" } }, "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg=="], - "@hono/trpc-server": ["@hono/trpc-server@0.3.4", "", { "peerDependencies": { "@trpc/server": "^10.10.0 || >11.0.0-rc", "hono": ">=4.*" } }, "sha512-xFOPjUPnII70FgicDzOJy1ufIoBTu8eF578zGiDOrYOrYN8CJe140s9buzuPkX+SwJRYK8LjEBHywqZtxdm8aA=="], - "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="], @@ -686,8 +682,6 @@ "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - "@trpc/server": ["@trpc/server@11.10.0", "", { "peerDependencies": { "typescript": ">=5.7.2" } }, "sha512-zZjTrR6He61e5TiT7e/bQqab/jRcXBZM8Fg78Yoo8uh5pz60dzzbYuONNUCOkafv5ppXVMms4NHYfNZgzw50vg=="], - "@tsconfig/node10": ["@tsconfig/node10@1.0.12", "", {}, "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ=="], "@tsconfig/node12": ["@tsconfig/node12@1.0.11", "", {}, "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag=="],