Completes the first milestone of The Pet Loft ecommerce platform: - apps/storefront: full customer-facing Next.js app with HeroUI (cart, checkout, orders, wishlist, product detail, shop, search, auth) - convex/: serverless backend with schema, queries, mutations, actions, HTTP routes, Stripe/Shippo integrations, and co-located tests - packages/types, packages/utils, packages/convex: shared workspace packages Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
39 lines
1.1 KiB
TypeScript
39 lines
1.1 KiB
TypeScript
import { QueryCtx, MutationCtx } from "../_generated/server";
|
|
import type { Id } from "../_generated/dataModel";
|
|
|
|
type AuthCtx = QueryCtx | MutationCtx;
|
|
|
|
export async function getCurrentUser(ctx: QueryCtx) {
|
|
const identity = await ctx.auth.getUserIdentity();
|
|
if (!identity) return null;
|
|
return await ctx.db
|
|
.query("users")
|
|
.withIndex("by_external_id", (q) => q.eq("externalId", identity.subject))
|
|
.unique();
|
|
}
|
|
|
|
export async function getCurrentUserOrThrow(ctx: AuthCtx) {
|
|
const user = await getCurrentUser(ctx as QueryCtx);
|
|
if (!user) throw new Error("Unauthenticated");
|
|
return user;
|
|
}
|
|
|
|
export async function requireAdmin(ctx: QueryCtx) {
|
|
const user = await getCurrentUserOrThrow(ctx);
|
|
if (user.role !== "admin" && user.role !== "super_admin") {
|
|
throw new Error("Unauthorized: admin access required");
|
|
}
|
|
return user;
|
|
}
|
|
|
|
export async function requireOwnership(
|
|
ctx: AuthCtx,
|
|
resourceUserId: Id<"users">,
|
|
) {
|
|
const user = await getCurrentUserOrThrow(ctx);
|
|
if (resourceUserId !== user._id) {
|
|
throw new Error("Unauthorized: resource does not belong to you");
|
|
}
|
|
return user;
|
|
}
|