mirror of
https://github.com/yusufipk/OpenFrame.git
synced 2026-09-11 09:36:08 +00:00
feat: add workspace management features including member invitations and role updates
- Implemented API endpoints for managing workspace members (GET, POST, PATCH, DELETE). - Added workspace creation and retrieval functionalities. - Enhanced project model to associate with workspaces. - Updated project member roles and access control logic. - Created sign-out page and updated authentication flow. - Modified header to include navigation to workspaces. - Updated Prisma schema to include workspace and member models. - Seed script updated to create demo workspaces and associated members.
This commit is contained in:
+64
-9
@@ -23,11 +23,13 @@ model User {
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
// Relations
|
||||
accounts Account[]
|
||||
sessions Session[]
|
||||
projects Project[]
|
||||
comments Comment[]
|
||||
memberships ProjectMember[]
|
||||
accounts Account[]
|
||||
sessions Session[]
|
||||
ownedWorkspaces Workspace[]
|
||||
workspaceMemberships WorkspaceMember[]
|
||||
projects Project[]
|
||||
comments Comment[]
|
||||
projectMemberships ProjectMember[]
|
||||
|
||||
@@map("users")
|
||||
}
|
||||
@@ -76,6 +78,55 @@ model VerificationToken {
|
||||
// APPLICATION MODELS
|
||||
// ============================================
|
||||
|
||||
// ---- Workspace ----
|
||||
|
||||
model Workspace {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
slug String @unique
|
||||
description String? @db.Text
|
||||
|
||||
// Ownership
|
||||
ownerId String
|
||||
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
||||
|
||||
// Timestamps
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
// Relations
|
||||
members WorkspaceMember[]
|
||||
projects Project[]
|
||||
|
||||
@@index([ownerId])
|
||||
@@index([slug])
|
||||
@@map("workspaces")
|
||||
}
|
||||
|
||||
model WorkspaceMember {
|
||||
id String @id @default(cuid())
|
||||
role WorkspaceMemberRole @default(COMMENTATOR)
|
||||
|
||||
workspaceId String
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
|
||||
userId String
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@unique([workspaceId, userId])
|
||||
@@index([userId])
|
||||
@@map("workspace_members")
|
||||
}
|
||||
|
||||
enum WorkspaceMemberRole {
|
||||
ADMIN // Full access: manage members, delete projects, etc.
|
||||
COMMENTATOR // Can view all projects and comment only
|
||||
}
|
||||
|
||||
// ---- Project ----
|
||||
|
||||
model Project {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
@@ -89,6 +140,10 @@ model Project {
|
||||
ownerId String
|
||||
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
||||
|
||||
// Workspace (required - every project belongs to a workspace)
|
||||
workspaceId String
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
|
||||
// Timestamps
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
@@ -100,6 +155,7 @@ model Project {
|
||||
|
||||
@@index([ownerId])
|
||||
@@index([slug])
|
||||
@@index([workspaceId])
|
||||
@@map("projects")
|
||||
}
|
||||
|
||||
@@ -111,7 +167,7 @@ enum ProjectVisibility {
|
||||
|
||||
model ProjectMember {
|
||||
id String @id @default(cuid())
|
||||
role ProjectMemberRole @default(VIEWER)
|
||||
role ProjectMemberRole @default(COMMENTATOR)
|
||||
|
||||
projectId String
|
||||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
@@ -127,9 +183,8 @@ model ProjectMember {
|
||||
}
|
||||
|
||||
enum ProjectMemberRole {
|
||||
VIEWER // Can view and comment
|
||||
EDITOR // Can add/edit videos
|
||||
ADMIN // Can manage members and settings
|
||||
ADMIN // Can manage members, settings, delete videos
|
||||
COMMENTATOR // Can view and comment only
|
||||
}
|
||||
|
||||
model Video {
|
||||
|
||||
+44
-6
@@ -1,4 +1,5 @@
|
||||
import { PrismaClient, ProjectVisibility, ProjectMemberRole, SharePermission } from '@prisma/client';
|
||||
import 'dotenv/config';
|
||||
import { PrismaClient, ProjectVisibility, ProjectMemberRole, SharePermission, WorkspaceMemberRole } from '@prisma/client';
|
||||
import { PrismaPg } from '@prisma/adapter-pg';
|
||||
import pg from 'pg';
|
||||
|
||||
@@ -21,6 +22,8 @@ async function main() {
|
||||
await prisma.shareLink.deleteMany();
|
||||
await prisma.projectMember.deleteMany();
|
||||
await prisma.project.deleteMany();
|
||||
await prisma.workspaceMember.deleteMany();
|
||||
await prisma.workspace.deleteMany();
|
||||
await prisma.session.deleteMany();
|
||||
await prisma.account.deleteMany();
|
||||
await prisma.verificationToken.deleteMany();
|
||||
@@ -57,7 +60,39 @@ async function main() {
|
||||
|
||||
console.log('✓ Created 3 demo users');
|
||||
|
||||
// Create projects
|
||||
// Create workspaces
|
||||
const mainWorkspace = await prisma.workspace.create({
|
||||
data: {
|
||||
name: 'OpenFrame Studio',
|
||||
slug: 'openframe-studio',
|
||||
description: 'Main workspace for all video production projects',
|
||||
ownerId: demoUser.id,
|
||||
members: {
|
||||
create: [
|
||||
{ userId: collaborator.id, role: WorkspaceMemberRole.ADMIN },
|
||||
{ userId: reviewer.id, role: WorkspaceMemberRole.COMMENTATOR },
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const clientWorkspace = await prisma.workspace.create({
|
||||
data: {
|
||||
name: 'XYZ Corp',
|
||||
slug: 'xyz-corp',
|
||||
description: 'Client workspace for XYZ Corporation projects',
|
||||
ownerId: collaborator.id,
|
||||
members: {
|
||||
create: [
|
||||
{ userId: demoUser.id, role: WorkspaceMemberRole.ADMIN },
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
console.log('✓ Created 2 workspaces with members');
|
||||
|
||||
// Create projects (linked to workspace)
|
||||
const techProject = await prisma.project.create({
|
||||
data: {
|
||||
name: 'Tech Review Series',
|
||||
@@ -65,10 +100,11 @@ async function main() {
|
||||
slug: 'tech-review-series',
|
||||
visibility: ProjectVisibility.PRIVATE,
|
||||
ownerId: demoUser.id,
|
||||
workspaceId: mainWorkspace.id,
|
||||
members: {
|
||||
create: [
|
||||
{ userId: collaborator.id, role: ProjectMemberRole.EDITOR },
|
||||
{ userId: reviewer.id, role: ProjectMemberRole.VIEWER },
|
||||
{ userId: collaborator.id, role: ProjectMemberRole.ADMIN },
|
||||
{ userId: reviewer.id, role: ProjectMemberRole.COMMENTATOR },
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -81,6 +117,7 @@ async function main() {
|
||||
slug: 'programming-tutorials',
|
||||
visibility: ProjectVisibility.INVITE,
|
||||
ownerId: demoUser.id,
|
||||
workspaceId: mainWorkspace.id,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -91,6 +128,7 @@ async function main() {
|
||||
slug: 'xyz-corp-promo',
|
||||
visibility: ProjectVisibility.PRIVATE,
|
||||
ownerId: collaborator.id,
|
||||
workspaceId: clientWorkspace.id,
|
||||
members: {
|
||||
create: [{ userId: demoUser.id, role: ProjectMemberRole.ADMIN }],
|
||||
},
|
||||
@@ -273,8 +311,8 @@ async function main() {
|
||||
console.log('\n✅ Seeding complete!\n');
|
||||
console.log('Demo accounts:');
|
||||
console.log(' - [email protected] (Owner)');
|
||||
console.log(' - [email protected] (Editor)');
|
||||
console.log(' - [email protected] (Viewer)');
|
||||
console.log(' - [email protected] (Admin)');
|
||||
console.log(' - [email protected] (Commentator)');
|
||||
}
|
||||
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user