Add API option to add member without invite (#378)

Co-authored-by: Bereket Engida <86073083+Bekacru@users.noreply.github.com>
Co-authored-by: Bereket Engida <bekacru@gmail.com>
This commit is contained in:
Elliot Alexander
2024-11-22 21:54:11 +10:00
committed by GitHub
parent 2fb8a17022
commit f73ea98c74
5 changed files with 114 additions and 1 deletions

View File

@@ -393,6 +393,21 @@ To get the current member of the organization you can use the `organization.getA
const member = await client.organization.getActiveMember()
```
### Add Member
If you want to add a member directly to an organization without sending an invitation, you can use the `addMember` function which can only be invoked on the server.
```ts title="api.ts"
import { auth } from "@/auth";
auth.api.addMember({
body: {
userId: "user-id",
organizationId: "organization-id",
role: "admin"
}
})
```
## Access Control

View File

@@ -214,7 +214,7 @@ export const getSessionFromCtx = async <
return session as {
session: S & Session;
user: U & User;
};
} | null;
};
export const sessionMiddleware = createAuthMiddleware(async (ctx) => {

View File

@@ -302,6 +302,31 @@ describe("organization", async (it) => {
expectTypeOf(auth.api.createOrganization).toBeFunction();
expectTypeOf(auth.api.getInvitation).toBeFunction();
});
it("should add member on the server directly", async () => {
const newUser = await auth.api.signUpEmail({
body: {
email: "new-member@email.com",
password: "password",
name: "new member",
},
});
const org = await auth.api.createOrganization({
body: {
name: "test",
slug: "test",
},
headers,
});
const member = await auth.api.addMember({
body: {
organizationId: org?.id,
userId: newUser.user.email,
role: "admin",
},
});
expect(member?.role).toBe("admin");
});
});
describe("access control", async (it) => {

View File

@@ -29,6 +29,7 @@ import {
rejectInvitation,
} from "./routes/crud-invites";
import {
addMember,
getActiveMember,
removeMember,
updateMemberRole,
@@ -215,6 +216,7 @@ export const organization = <O extends OrganizationOptions>(options?: O) => {
acceptInvitation,
getInvitation,
rejectInvitation,
addMember: addMember<O>(),
removeMember,
updateMemberRole: updateMemberRole(options as O),
getActiveMember,

View File

@@ -4,7 +4,78 @@ import { getOrgAdapter } from "../adapter";
import { orgMiddleware, orgSessionMiddleware } from "../call";
import type { InferRolesFromOption, Member } from "../schema";
import { APIError } from "better-call";
import type { User } from "../../../db/schema";
import { generateId } from "../../../utils";
import type { OrganizationOptions } from "../organization";
import { getSessionFromCtx } from "../../../api";
export const addMember = <O extends OrganizationOptions>() =>
createAuthEndpoint(
"/organization/add-member",
{
method: "POST",
body: z.object({
userId: z.string(),
role: z.string() as unknown as InferRolesFromOption<O>,
organizationId: z.string().optional(),
}),
use: [orgMiddleware],
metadata: {
SERVER_ONLY: true,
},
},
async (ctx) => {
const session = ctx.body.userId
? await getSessionFromCtx<{
session: {
activeOrganizationId?: string;
};
}>(ctx).catch((e) => null)
: null;
const orgId =
ctx.body.organizationId || session?.session.activeOrganizationId;
if (!orgId) {
return ctx.json(null, {
status: 400,
body: {
message: "No active organization found!",
},
});
}
const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
const user = await ctx.context.internalAdapter.findUserById(
ctx.body.userId,
);
if (!user) {
throw new APIError("BAD_REQUEST", {
message: "User not found!",
});
}
const alreadyMember = await adapter.findMemberByEmail({
email: user.email,
organizationId: orgId,
});
if (alreadyMember) {
throw new APIError("BAD_REQUEST", {
message: "User is already a member of this organization",
});
}
const createdMember = await adapter.createMember({
id: generateId(),
organizationId: orgId,
userId: user.id,
role: ctx.body.role as string,
createdAt: new Date(),
});
return ctx.json(createdMember);
},
);
export const removeMember = createAuthEndpoint(
"/organization/remove-member",