mirror of
https://github.com/LukeHagar/better-auth.git
synced 2025-12-10 12:27:44 +00:00
feat(organization): listUserInvitations adds the ability to list all invitations for a given user (#3121)
* feat(org): listUserInvitations This PR introduces in the org plugin. * chore: lint
This commit is contained in:
@@ -560,7 +560,7 @@ await authClient.organization.getInvitation({
|
|||||||
|
|
||||||
### List Invitations
|
### List Invitations
|
||||||
|
|
||||||
To list all invitations you can use the `listInvitations` function provided by the client.
|
To list all invitations for a given organization you can use the `listInvitations` function provided by the client.
|
||||||
|
|
||||||
```ts title="auth-client.ts"
|
```ts title="auth-client.ts"
|
||||||
const invitations = await authClient.organization.listInvitations({
|
const invitations = await authClient.organization.listInvitations({
|
||||||
@@ -570,6 +570,30 @@ const invitations = await authClient.organization.listInvitations({
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### List user invitations
|
||||||
|
|
||||||
|
To list all invitations for a given user you can use the `listUserInvitations` function provided by the client.
|
||||||
|
|
||||||
|
```ts title="auth-client.ts"
|
||||||
|
const invitations = await authClient.organization.listUserInvitations()
|
||||||
|
```
|
||||||
|
|
||||||
|
On the server, you can pass the user ID as a query parameter.
|
||||||
|
|
||||||
|
```ts title="api.ts"
|
||||||
|
const invitations = await auth.api.listUserInvitations({
|
||||||
|
query: {
|
||||||
|
email: "user@example.com"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
<Callout type="warn">
|
||||||
|
The `email` query parameter is only available on the server to query for invitations for a specific user.
|
||||||
|
</Callout>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Members
|
## Members
|
||||||
|
|
||||||
### Remove Member
|
### Remove Member
|
||||||
|
|||||||
@@ -561,7 +561,13 @@ export const getOrgAdapter = (
|
|||||||
});
|
});
|
||||||
return invitations;
|
return invitations;
|
||||||
},
|
},
|
||||||
|
listUserInvitations: async (email: string) => {
|
||||||
|
const invitations = await adapter.findMany<Invitation>({
|
||||||
|
model: "invitation",
|
||||||
|
where: [{ field: "email", value: email }],
|
||||||
|
});
|
||||||
|
return invitations;
|
||||||
|
},
|
||||||
createInvitation: async ({
|
createInvitation: async ({
|
||||||
invitation,
|
invitation,
|
||||||
user,
|
user,
|
||||||
|
|||||||
@@ -703,10 +703,11 @@ describe("organization", async (it) => {
|
|||||||
password: userOverLimit.password,
|
password: userOverLimit.password,
|
||||||
name: userOverLimit.name,
|
name: userOverLimit.name,
|
||||||
});
|
});
|
||||||
const { res, headers: headers2 } = await signInWithUser(
|
const { headers: headers2 } = await signInWithUser(
|
||||||
userOverLimit2.email,
|
userOverLimit2.email,
|
||||||
userOverLimit2.password,
|
userOverLimit2.password,
|
||||||
);
|
);
|
||||||
|
|
||||||
await client.signUp.email(
|
await client.signUp.email(
|
||||||
{
|
{
|
||||||
email: userOverLimit2.email,
|
email: userOverLimit2.email,
|
||||||
@@ -738,6 +739,92 @@ describe("organization", async (it) => {
|
|||||||
});
|
});
|
||||||
expect(getFullOrganization.data?.members.length).toBe(6);
|
expect(getFullOrganization.data?.members.length).toBe(6);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should allow listing invitations for an org", async () => {
|
||||||
|
const invitations = await client.organization.listInvitations({
|
||||||
|
query: {
|
||||||
|
organizationId: organizationId,
|
||||||
|
},
|
||||||
|
fetchOptions: {
|
||||||
|
headers: headers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(invitations.data?.length).toBe(4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should allow listing invitations for a user using authClient", async () => {
|
||||||
|
const rng = crypto.randomUUID();
|
||||||
|
const user = {
|
||||||
|
email: `${rng}@email.com`,
|
||||||
|
password: rng,
|
||||||
|
name: rng,
|
||||||
|
};
|
||||||
|
const rng2 = crypto.randomUUID();
|
||||||
|
const orgAdminUser = {
|
||||||
|
email: `${rng2}@email.com`,
|
||||||
|
password: rng2,
|
||||||
|
name: rng2,
|
||||||
|
};
|
||||||
|
await auth.api.signUpEmail({
|
||||||
|
body: user,
|
||||||
|
});
|
||||||
|
await auth.api.signUpEmail({
|
||||||
|
body: orgAdminUser,
|
||||||
|
});
|
||||||
|
const { headers: headers2, res: session } = await signInWithUser(
|
||||||
|
user.email,
|
||||||
|
user.password,
|
||||||
|
);
|
||||||
|
const { headers: adminHeaders, res: adminSession } = await signInWithUser(
|
||||||
|
orgAdminUser.email,
|
||||||
|
orgAdminUser.password,
|
||||||
|
);
|
||||||
|
const orgRng = crypto.randomUUID();
|
||||||
|
const org = await auth.api.createOrganization({
|
||||||
|
body: {
|
||||||
|
name: orgRng,
|
||||||
|
slug: orgRng,
|
||||||
|
},
|
||||||
|
headers: adminHeaders,
|
||||||
|
});
|
||||||
|
const invitation = await client.organization.inviteMember({
|
||||||
|
organizationId: org?.id,
|
||||||
|
email: user.email,
|
||||||
|
role: "member",
|
||||||
|
fetchOptions: {
|
||||||
|
headers: adminHeaders,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const userInvitations = await client.organization.listUserInvitations({
|
||||||
|
fetchOptions: {
|
||||||
|
headers: headers2,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(userInvitations.data?.[0].id).toBe(invitation.data?.id);
|
||||||
|
expect(userInvitations.data?.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should allow listing invitations for a user using server", async () => {
|
||||||
|
const orgInvitations = await client.organization.listInvitations({
|
||||||
|
fetchOptions: {
|
||||||
|
headers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!orgInvitations.data?.[0].email) throw new Error("No email found");
|
||||||
|
|
||||||
|
const invitations = await auth.api.listUserInvitations({
|
||||||
|
query: {
|
||||||
|
email: orgInvitations.data?.[0].email,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(invitations?.length).toBe(
|
||||||
|
orgInvitations.data.filter(
|
||||||
|
(x) => x.email === orgInvitations.data?.[0].email,
|
||||||
|
).length,
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("access control", async (it) => {
|
describe("access control", async (it) => {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
getInvitation,
|
getInvitation,
|
||||||
listInvitations,
|
listInvitations,
|
||||||
rejectInvitation,
|
rejectInvitation,
|
||||||
|
listUserInvitations,
|
||||||
} from "./routes/crud-invites";
|
} from "./routes/crud-invites";
|
||||||
import {
|
import {
|
||||||
addMember,
|
addMember,
|
||||||
@@ -386,6 +387,7 @@ export const organization = <O extends OrganizationOptions>(options?: O) => {
|
|||||||
acceptInvitation,
|
acceptInvitation,
|
||||||
getInvitation,
|
getInvitation,
|
||||||
rejectInvitation,
|
rejectInvitation,
|
||||||
|
listUserInvitations,
|
||||||
checkOrganizationSlug,
|
checkOrganizationSlug,
|
||||||
addMember: addMember<O>(),
|
addMember: addMember<O>(),
|
||||||
removeMember,
|
removeMember,
|
||||||
|
|||||||
@@ -745,3 +745,44 @@ export const listInvitations = createAuthEndpoint(
|
|||||||
return ctx.json(invitations);
|
return ctx.json(invitations);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List all invitations recieved for a user
|
||||||
|
*/
|
||||||
|
export const listUserInvitations = createAuthEndpoint(
|
||||||
|
"/organization/list-user-invitations",
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
use: [orgMiddleware],
|
||||||
|
query: z
|
||||||
|
.object({
|
||||||
|
email: z
|
||||||
|
.string({
|
||||||
|
description:
|
||||||
|
"The email of the user to list invitations for. This only works for server side API calls.",
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
},
|
||||||
|
async (ctx) => {
|
||||||
|
const session = await getSessionFromCtx(ctx);
|
||||||
|
|
||||||
|
if (ctx.request && ctx.query?.email) {
|
||||||
|
throw new APIError("BAD_REQUEST", {
|
||||||
|
message: "User email cannot be passed for client side API calls.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const userEmail = session?.user.email || ctx.query?.email;
|
||||||
|
if (!userEmail) {
|
||||||
|
throw new APIError("BAD_REQUEST", {
|
||||||
|
message: "Missing session headers, or email query parameter.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
|
||||||
|
|
||||||
|
const invitations = await adapter.listUserInvitations(userEmail);
|
||||||
|
return ctx.json(invitations);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user