mirror of
https://github.com/lukevella/rallly.git
synced 2025-07-10 13:07:28 +02:00
✨ Add space member model (#1780)
This commit is contained in:
parent
dd9bdbcfc4
commit
424f39ae6b
8 changed files with 200 additions and 68 deletions
|
@ -9,6 +9,7 @@
|
|||
*
|
||||
* See: https://github.com/lukevella/rallly/issues/949
|
||||
*/
|
||||
import { createUser } from "@/features/user/mutations";
|
||||
import { PrismaAdapter } from "@auth/prisma-adapter";
|
||||
import { prisma } from "@rallly/database";
|
||||
import type { Adapter } from "next-auth/adapters";
|
||||
|
@ -38,26 +39,15 @@ export function CustomPrismaAdapter(options: {
|
|||
});
|
||||
},
|
||||
createUser: async (user) => {
|
||||
const newUser = await prisma.user.create({
|
||||
data: {
|
||||
return await createUser({
|
||||
name: user.name ?? "Unknown",
|
||||
email: user.email,
|
||||
emailVerified: user.emailVerified,
|
||||
image: user.image,
|
||||
timeZone: user.timeZone,
|
||||
weekStart: user.weekStart,
|
||||
timeFormat: user.timeFormat,
|
||||
locale: user.locale,
|
||||
role: "user",
|
||||
spaces: {
|
||||
create: {
|
||||
name: "Personal",
|
||||
},
|
||||
},
|
||||
},
|
||||
emailVerified: user.emailVerified ?? undefined,
|
||||
image: user.image ?? undefined,
|
||||
timeZone: user.timeZone ?? undefined,
|
||||
timeFormat: user.timeFormat ?? undefined,
|
||||
locale: user.locale ?? undefined,
|
||||
});
|
||||
|
||||
return newUser;
|
||||
},
|
||||
} as Adapter;
|
||||
}
|
||||
|
|
22
apps/web/src/features/spaces/mutations.ts
Normal file
22
apps/web/src/features/spaces/mutations.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { prisma } from "@rallly/database";
|
||||
|
||||
export async function createSpace({
|
||||
ownerId,
|
||||
name,
|
||||
}: {
|
||||
ownerId: string;
|
||||
name: string;
|
||||
}) {
|
||||
return await prisma.space.create({
|
||||
data: {
|
||||
ownerId,
|
||||
name,
|
||||
members: {
|
||||
create: {
|
||||
userId: ownerId,
|
||||
role: "OWNER",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
54
apps/web/src/features/user/mutations.ts
Normal file
54
apps/web/src/features/user/mutations.ts
Normal file
|
@ -0,0 +1,54 @@
|
|||
import { type TimeFormat, prisma } from "@rallly/database";
|
||||
|
||||
export async function createUser({
|
||||
name,
|
||||
email,
|
||||
emailVerified,
|
||||
image,
|
||||
timeZone,
|
||||
timeFormat,
|
||||
locale,
|
||||
weekStart,
|
||||
}: {
|
||||
name: string;
|
||||
email: string;
|
||||
emailVerified?: Date;
|
||||
image?: string;
|
||||
timeZone?: string;
|
||||
timeFormat?: TimeFormat;
|
||||
locale?: string;
|
||||
weekStart?: number;
|
||||
}) {
|
||||
return await prisma.$transaction(async (tx) => {
|
||||
const user = await tx.user.create({
|
||||
data: {
|
||||
name,
|
||||
email,
|
||||
emailVerified,
|
||||
image,
|
||||
timeZone,
|
||||
timeFormat,
|
||||
locale,
|
||||
weekStart,
|
||||
role: "user",
|
||||
},
|
||||
});
|
||||
|
||||
const space = await tx.space.create({
|
||||
data: {
|
||||
ownerId: user.id,
|
||||
name: "Personal",
|
||||
},
|
||||
});
|
||||
|
||||
await tx.spaceMember.create({
|
||||
data: {
|
||||
spaceId: space.id,
|
||||
userId: user.id,
|
||||
role: "OWNER",
|
||||
},
|
||||
});
|
||||
|
||||
return user;
|
||||
});
|
||||
}
|
|
@ -12,6 +12,7 @@ import { isValidName } from "@/utils/is-valid-name";
|
|||
import { createToken, decryptToken } from "@/utils/session";
|
||||
|
||||
import { getInstanceSettings } from "@/features/instance-settings/queries";
|
||||
import { createUser } from "@/features/user/mutations";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { createRateLimitMiddleware, publicProcedure, router } from "../trpc";
|
||||
import type { RegistrationTokenPayload } from "../types";
|
||||
|
@ -124,21 +125,14 @@ export const auth = router({
|
|||
return { ok: false };
|
||||
}
|
||||
|
||||
const user = await prisma.user.create({
|
||||
select: { id: true, name: true, email: true },
|
||||
data: {
|
||||
const user = await createUser({
|
||||
name,
|
||||
email,
|
||||
emailVerified: new Date(),
|
||||
timeZone: input.timeZone,
|
||||
timeFormat: input.timeFormat,
|
||||
weekStart: input.weekStart,
|
||||
locale: input.locale,
|
||||
spaces: {
|
||||
create: {
|
||||
name: "Personal",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (ctx.user?.isGuest) {
|
||||
|
@ -166,7 +160,14 @@ export const auth = router({
|
|||
},
|
||||
});
|
||||
|
||||
return { ok: true, user };
|
||||
return {
|
||||
ok: true,
|
||||
user: {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
},
|
||||
};
|
||||
}),
|
||||
getUserPermission: publicProcedure
|
||||
.input(z.object({ token: z.string() }))
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
-- CreateEnum
|
||||
CREATE TYPE "SpaceMemberRole" AS ENUM ('OWNER', 'ADMIN', 'MEMBER');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "space_members" (
|
||||
"id" TEXT NOT NULL,
|
||||
"space_id" TEXT NOT NULL,
|
||||
"user_id" TEXT NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"role" "SpaceMemberRole" NOT NULL DEFAULT 'MEMBER',
|
||||
|
||||
CONSTRAINT "space_members_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "space_members_space_id_idx" ON "space_members"("space_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "space_members_space_id_user_id_key" ON "space_members"("space_id", "user_id");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "space_members" ADD CONSTRAINT "space_members_space_id_fkey" FOREIGN KEY ("space_id") REFERENCES "spaces"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "space_members" ADD CONSTRAINT "space_members_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@ -0,0 +1,15 @@
|
|||
-- Create space members with OWNER role for existing spaces
|
||||
INSERT INTO "space_members" ("id", "space_id", "user_id", "created_at", "updated_at", "role")
|
||||
SELECT
|
||||
gen_random_uuid(),
|
||||
id as space_id,
|
||||
owner_id as user_id,
|
||||
NOW() as created_at,
|
||||
NOW() as updated_at,
|
||||
'OWNER' as role
|
||||
FROM "spaces"
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM "space_members"
|
||||
WHERE "space_members"."space_id" = "spaces"."id"
|
||||
AND "space_members"."user_id" = "spaces"."owner_id"
|
||||
);
|
39
packages/database/prisma/models/space.prisma
Normal file
39
packages/database/prisma/models/space.prisma
Normal file
|
@ -0,0 +1,39 @@
|
|||
model Space {
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
ownerId String @map("owner_id")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
owner User @relation("UserSpaces", fields: [ownerId], references: [id], onDelete: Cascade)
|
||||
polls Poll[]
|
||||
scheduledEvents ScheduledEvent[]
|
||||
subscription Subscription? @relation("SpaceToSubscription")
|
||||
|
||||
members SpaceMember[]
|
||||
|
||||
@@index([ownerId], type: Hash)
|
||||
@@map("spaces")
|
||||
}
|
||||
|
||||
enum SpaceMemberRole {
|
||||
OWNER
|
||||
ADMIN
|
||||
MEMBER
|
||||
}
|
||||
|
||||
model SpaceMember {
|
||||
id String @id @default(uuid())
|
||||
spaceId String @map("space_id")
|
||||
userId String @map("user_id")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
role SpaceMemberRole @default(MEMBER)
|
||||
|
||||
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([spaceId, userId])
|
||||
@@index([spaceId])
|
||||
@@map("space_members")
|
||||
}
|
|
@ -59,6 +59,7 @@ model User {
|
|||
subscription Subscription? @relation("UserToSubscription")
|
||||
|
||||
spaces Space[] @relation("UserSpaces")
|
||||
memberOf SpaceMember[]
|
||||
|
||||
pollViews PollView[]
|
||||
scheduledEvents ScheduledEvent[]
|
||||
|
@ -67,22 +68,6 @@ model User {
|
|||
@@map("users")
|
||||
}
|
||||
|
||||
model Space {
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
ownerId String @map("owner_id")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
owner User @relation("UserSpaces", fields: [ownerId], references: [id], onDelete: Cascade)
|
||||
polls Poll[]
|
||||
scheduledEvents ScheduledEvent[]
|
||||
subscription Subscription? @relation("SpaceToSubscription")
|
||||
|
||||
@@index([ownerId], type: Hash)
|
||||
@@map("spaces")
|
||||
}
|
||||
|
||||
model VerificationToken {
|
||||
identifier String @db.Citext
|
||||
token String @unique
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue