-
Notifications
You must be signed in to change notification settings - Fork 30
feat(mesh): render user names via USER_GET tool #2135
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
ce31c77
feat(mesh): render user names via USER_GET tool
tlgimenes 6ce937a
fix avatar
viktormarinho d3da41a
fix query
viktormarinho 211fb9a
rename hook
viktormarinho 0858d50
rename
viktormarinho e461d0d
fix not necessary casts
viktormarinho 9e60316
cubic fix
viktormarinho fab4397
fix created/updated by detectyion
viktormarinho File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -55,8 +55,13 @@ export async function seed(db: Kysely<Database>): Promise<BenchmarkSeedResult> { | |
| .values({ | ||
| id: userId, | ||
| email: "[email protected]", | ||
| emailVerified: 1, | ||
| name: "Benchmark User", | ||
| image: null, | ||
| role: "admin", | ||
| banned: null, | ||
| banReason: null, | ||
| banExpires: null, | ||
| createdAt: now, | ||
| updatedAt: now, | ||
| }) | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| /** | ||
| * User Storage | ||
| * | ||
| * Provides access to Better Auth user data with organization-scoped access control. | ||
| * Users can only fetch data for users in their shared organizations. | ||
| */ | ||
|
|
||
| import type { Kysely } from "kysely"; | ||
| import type { Database, UserWithImage } from "./types"; | ||
|
|
||
| /** | ||
| * User storage interface | ||
| */ | ||
| export interface UserStoragePort { | ||
| findById( | ||
| userId: string, | ||
| requestingUserId: string, | ||
| ): Promise<UserWithImage | null>; | ||
| } | ||
|
|
||
| /** | ||
| * User storage implementation using Kysely | ||
| */ | ||
| export class UserStorage implements UserStoragePort { | ||
| constructor(private db: Kysely<Database>) {} | ||
|
|
||
| /** | ||
| * Find a user by ID, ensuring the requesting user shares at least one organization | ||
| * | ||
| * @param userId - The user ID to fetch | ||
| * @param requestingUserId - The user making the request (for authorization) | ||
| * @returns User data or null if not found/unauthorized | ||
| */ | ||
| async findById( | ||
| userId: string, | ||
| requestingUserId: string, | ||
| ): Promise<UserWithImage | null> { | ||
| // Query the user table, but only if the requesting user shares an organization | ||
| // with the target user (via the member table) | ||
| const result = await this.db | ||
| .selectFrom("user") | ||
| .select([ | ||
| "user.id", | ||
| "user.name", | ||
| "user.email", | ||
| "user.image", | ||
| "user.createdAt", | ||
| "user.updatedAt", | ||
| ]) | ||
| .where("user.id", "=", userId) | ||
| .where((eb) => | ||
| eb.exists( | ||
| eb | ||
| .selectFrom("member as m1") | ||
| .innerJoin("member as m2", "m1.organizationId", "m2.organizationId") | ||
| .select("m1.id") | ||
| .where("m1.userId", "=", userId) | ||
| .where("m2.userId", "=", requestingUserId), | ||
| ), | ||
| ) | ||
| .executeTakeFirst(); | ||
|
|
||
| if (!result) { | ||
| return null; | ||
| } | ||
|
|
||
| return { | ||
| id: result.id, | ||
| name: result.name, | ||
| email: result.email, | ||
| role: "", // Not exposed in this context | ||
| createdAt: result.createdAt, | ||
| updatedAt: result.updatedAt, | ||
| image: result.image ?? undefined, | ||
| }; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| /** | ||
| * USER_GET Tool | ||
| * | ||
| * Fetch a user's public profile (name/email/avatar) by user id. | ||
| * Access is restricted to users in shared organizations. | ||
| */ | ||
|
|
||
| import { z } from "zod"; | ||
| import { defineTool } from "../../core/define-tool"; | ||
| import { getUserId, requireAuth } from "../../core/mesh-context"; | ||
|
|
||
| const InputSchema = z.object({ | ||
| id: z.string().min(1), | ||
| }); | ||
|
|
||
| const OutputSchema = z.object({ | ||
| user: z | ||
| .object({ | ||
| id: z.string(), | ||
| name: z.string(), | ||
| email: z.string(), | ||
| image: z.string().nullable(), | ||
| }) | ||
| .nullable(), | ||
| }); | ||
|
|
||
| export const USER_GET = defineTool({ | ||
| name: "USER_GET", | ||
| description: "Get a user by id (restricted to shared organizations)", | ||
| inputSchema: InputSchema, | ||
| outputSchema: OutputSchema, | ||
| handler: async (input, ctx) => { | ||
| await ctx.access.check(); | ||
| requireAuth(ctx); | ||
|
|
||
| const requesterUserId = getUserId(ctx); | ||
| if (!requesterUserId) { | ||
| throw new Error("Authentication required"); | ||
| } | ||
|
|
||
| const user = await ctx.storage.users.findById(input.id, requesterUserId); | ||
| if (!user) { | ||
| return { user: null }; | ||
| } | ||
|
|
||
| return { | ||
| user: { | ||
| id: user.id, | ||
| name: user.name, | ||
| email: user.email, | ||
| image: user.image ?? null, | ||
| }, | ||
| }; | ||
| }, | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export { USER_GET } from "./get"; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.