This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
||||
// Private RPCs used only by the Next BFF's server-side ConvexHttpClient. They expose the narrow
|
||||
// global reads/writes kit-auth needs to maintain its sealed OIDC sessions and user rows. Domain
|
||||
// procedures never use this path: each of those executes through one generated public
|
||||
// query/mutation and therefore one real Convex transaction.
|
||||
|
||||
import { apiKeys, sessions } from "@conusai-kit/auth/entities"
|
||||
import type { Id } from "@conusai-kit/core"
|
||||
import { createConvexDataSource } from "@conusai-kit/data-convex"
|
||||
import { executorFor } from "@conusai-kit/data-convex/runtime"
|
||||
import {
|
||||
type AppIdentityClaims,
|
||||
type AppUserClaims,
|
||||
appUserIdentities,
|
||||
resolveAppUser,
|
||||
users,
|
||||
} from "@conusai-kit/tenancy"
|
||||
import type { GenericDataModel, GenericMutationCtx, GenericQueryCtx } from "convex/server"
|
||||
import { v } from "convex/values"
|
||||
import { internalMutation, internalQuery } from "./_generated/server"
|
||||
import { convexCapabilities } from "./lib/runtime"
|
||||
|
||||
const entities = [users, appUserIdentities, sessions, apiKeys]
|
||||
type FunctionContext<DataModel extends GenericDataModel> = {
|
||||
db: GenericQueryCtx<DataModel>["db"] | GenericMutationCtx<DataModel>["db"]
|
||||
}
|
||||
const data = <DataModel extends GenericDataModel>(ctx: FunctionContext<DataModel>) =>
|
||||
createConvexDataSource({ entities, executor: executorFor(ctx) })
|
||||
|
||||
// One internal mutation is the whole first-login/backfill transaction. Separate BFF calls could
|
||||
// never provide the uniqueness and rollback guarantees of the resolver.
|
||||
export const appUserResolve = internalMutation({
|
||||
args: {
|
||||
claims: v.object({
|
||||
issuer: v.string(),
|
||||
subject: v.string(),
|
||||
email: v.optional(v.string()),
|
||||
displayName: v.optional(v.string()),
|
||||
locale: v.optional(v.string()),
|
||||
}),
|
||||
create: v.boolean(),
|
||||
},
|
||||
handler: async (ctx, { claims, create }) =>
|
||||
resolveAppUser(data(ctx), claims as AppUserClaims | AppIdentityClaims, { create }),
|
||||
})
|
||||
|
||||
export const sessionById = internalQuery({
|
||||
args: { id: v.string() },
|
||||
handler: async (ctx, { id }) =>
|
||||
data(ctx)
|
||||
.global(sessions)
|
||||
.get(id as Id),
|
||||
})
|
||||
|
||||
export const sessionByHandleHash = internalQuery({
|
||||
args: { handleHash: v.string() },
|
||||
handler: async (ctx, { handleHash }) => {
|
||||
const page = await data(ctx)
|
||||
.global(sessions)
|
||||
.list({
|
||||
where: [{ field: "handleHash", op: "eq", value: handleHash }],
|
||||
limit: 1,
|
||||
})
|
||||
return page.items[0] ?? null
|
||||
},
|
||||
})
|
||||
|
||||
export const sessionInsert = internalMutation({
|
||||
args: { input: v.any() },
|
||||
handler: async (ctx, { input }) => data(ctx).global(sessions).insert(input),
|
||||
})
|
||||
|
||||
export const sessionUpdate = internalMutation({
|
||||
args: { id: v.string(), patch: v.any(), expectedVersion: v.number() },
|
||||
handler: async (ctx, { id, patch, expectedVersion }) =>
|
||||
data(ctx)
|
||||
.global(sessions)
|
||||
.update(id as Id, patch, expectedVersion),
|
||||
})
|
||||
|
||||
export const apiKeyByHash = internalQuery({
|
||||
args: { tokenHash: v.string() },
|
||||
handler: async (ctx, { tokenHash }) => {
|
||||
const source = data(ctx)
|
||||
const page = await source.acrossTenants(apiKeys).list({
|
||||
where: [{ field: "tokenHash", op: "eq", value: tokenHash }],
|
||||
limit: 1,
|
||||
})
|
||||
return page.items[0] ?? null
|
||||
},
|
||||
})
|
||||
|
||||
export const ready = internalQuery({
|
||||
args: {},
|
||||
handler: async () => ({ ready: true, ...convexCapabilities }),
|
||||
})
|
||||
Reference in New Issue
Block a user