Repository navigation
feat: implement unified role naming system - #49
Conversation
Replace 'user' role with 'member' across entire codebase to align with unified RBAC system using owner/admin/member roles with isSystem flag. **Changes:** - Schema: Updated default role from 'user' to 'member' - Adapters: Updated better-auth adapter fallbacks and role conversions - Validation: Updated default values in CreateUserSchema and InviteUserSchema - Constants: Removed obsolete Role.User enum value - Tests: Updated all test expectations and mock data (17 files) - Frontend: Updated role references in components and tests **Verification:** ✅ All identity tests passing (70/70) ✅ All API tests passing (190/190) ✅ Unified role system: owner, admin, member (with isSystem flag) This completes the role unification, ensuring Nexiom operates as a tenant in its own system with the same role structure as all other tenants.
📝 WalkthroughWalkthroughRenames the default identity role from "user" to "member" across validation schemas, constants, adapters, DB schema/migration, API controllers, web UI, and test fixtures. No control-flow changes beyond updating defaults and expectations. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
No actionable comments were generated in the recent review. 🎉 Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
packages/identity/src/adapters/drizzle-permission.adapter.spec.ts (1)
199-209:⚠️ Potential issue | 🟡 MinorStale mock data:
roleId: "user"should be updated to"member".The
hasRoletest at lines 199–209 still usesroleId: "user"androleName: "User"in the mock DB response. Since the PR removes the"user"role entirely from the system, this mock should use a role that actually exists (e.g.,"member"/"Member"). The test logic (assertinghasRole(user, "admin", "o1")returnsfalse) would still be valid with any non-admin role, but using a removed role in test fixtures is misleading.Proposed fix
db.select.mockReturnValue( mockChainedQuery([ { - roleId: "user", - roleName: "User", + roleId: "member", + roleName: "Member", permId: null, permOrgId: null, }, ]), );apps/api/src/modules/identity/users/users.validation.spec.ts (2)
87-96:⚠️ Potential issue | 🟡 MinorStale test description: still says "default role to user".
The test description on line 87 reads
'should default role to user if not provided'but the assertion now checks for'member'. Update the description to match.Proposed fix
- it('should default role to user if not provided', () => { + it('should default role to member if not provided', () => {
74-85:⚠️ Potential issue | 🟡 MinorTest fixture includes removed
'user'role as a "predefined" role.Line 75 lists
'user'among the standard predefined roles, but this PR removes'user'from the role system. While the test still passes (the schema accepts any lowercase alphanumeric string), listing'user'as a predefined role is misleading. Consider replacing it with'member'.Proposed fix
- const roles = ['admin', 'editor', 'viewer', 'user']; + const roles = ['admin', 'editor', 'viewer', 'member'];
🤖 Fix all issues with AI agents
In `@apps/web/src/modules/identity/users/InviteUserDialog.tsx`:
- Line 85: The predicate in InviteUserDialog where you compute the default role
(const def ...) uses a duplicated check (r.id === 'member' || r.id ===
'member'); replace the redundant disjunction with a single check (e.g., r.id ===
'member') so the search for the default role is correct, keeping the fallback to
roles[0]?.id as before; update the expression that assigns def in the
InviteUserDialog component to use the simplified predicate referencing roles and
def.
In `@packages/identity/src/schema.ts`:
- Line 23: The schema change set role: text("role").default("member") requires a
matching Drizzle migration because the existing migration snapshot
0001_workable_santa_claus still sets the default to "user"; create a new
migration that alters the users table to change the role column default from
'user' to 'member' (e.g., ALTER TABLE users ALTER COLUMN role SET DEFAULT
'member' for Postgres or the DB-specific equivalent), add the migration file to
your migrations folder and run the Drizzle migration command to apply it to the
database so the schema and migration history stay in sync.
| createdAt: timestamp("createdAt").notNull(), | ||
| updatedAt: timestamp("updatedAt").notNull(), | ||
| role: text("role").default("user"), | ||
| role: text("role").default("member"), |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for migration files in common locations
echo "=== Looking for migration files ==="
find . -type f \( -name "*migration*" -o -name "*migrate*" \) | head -30
echo ""
echo "=== Looking for SQL files in drizzle or migrations directories ==="
find . -type d \( -name "*migration*" -o -name "*migrate*" -o -name "*drizzle*" \) 2>/dev/null
echo ""
echo "=== Searching for SQL files ==="
fd --type f '\.sql$' | head -30
echo ""
echo "=== Searching for files referencing role default or member ==="
rg "role.*member|member.*role" --type sql 2>/dev/null | head -20Repository: pramodnarayana/nexiom
Length of output: 914
🏁 Script executed:
#!/bin/bash
# Check the migration files in packages/identity/drizzle
echo "=== Migration files in packages/identity/drizzle/ ==="
ls -la packages/identity/drizzle/
echo ""
echo "=== Content of 0000_sudden_silver_surfer.sql ==="
cat packages/identity/drizzle/0000_sudden_silver_surfer.sql
echo ""
echo "=== Content of 0001_workable_santa_claus.sql ==="
cat packages/identity/drizzle/0001_workable_santa_claus.sqlRepository: pramodnarayana/nexiom
Length of output: 2345
🏁 Script executed:
#!/bin/bash
# Read the schema.ts file around line 23
echo "=== Content of packages/identity/src/schema.ts around line 23 ==="
cat -n packages/identity/src/schema.ts | sed -n '1,50p'Repository: pramodnarayana/nexiom
Length of output: 2060
🏁 Script executed:
#!/bin/bash
# Check the drizzle meta directory for metadata about schema changes
echo "=== Content of packages/identity/drizzle/meta ==="
ls -la packages/identity/drizzle/meta/
echo ""
echo "=== Check git status for schema changes ==="
cd packages/identity && git diff src/schema.ts 2>/dev/null || echo "Git diff not available"
echo ""
echo "=== Check if schema file was recently modified ==="
cd packages/identity && git log --oneline -5 -- src/schema.ts 2>/dev/null || echo "Git log not available"
echo ""
echo "=== Check migration meta files ==="
cat packages/identity/drizzle/meta/_journal.json 2>/dev/null || echo "Journal file not found"Repository: pramodnarayana/nexiom
Length of output: 694
🏁 Script executed:
#!/bin/bash
# Read the journal file directly
echo "=== Content of packages/identity/drizzle/meta/_journal.json ==="
cat packages/identity/drizzle/meta/_journal.json
echo ""
echo "=== Check if latest snapshot (0001) contains role default ==="
grep -A 2 -B 2 '"role"' packages/identity/drizzle/meta/0001_snapshot.json | head -20Repository: pramodnarayana/nexiom
Length of output: 1000
🏁 Script executed:
#!/bin/bash
# Extract and format the user table definition from the latest snapshot
echo "=== User table definition from 0001_snapshot.json ==="
python3 << 'PYTHON_EOF'
import json
with open('packages/identity/drizzle/meta/0001_snapshot.json', 'r') as f:
snapshot = json.load(f)
# Find the user table
if 'public.user' in snapshot['schema']['tables']:
user_table = snapshot['schema']['tables']['public.user']
print(json.dumps(user_table, indent=2))
else:
print("User table not found in snapshot")
PYTHON_EOFRepository: pramodnarayana/nexiom
Length of output: 227
🏁 Script executed:
#!/bin/bash
# First, let's see the actual structure of the snapshot
echo "=== Structure of 0001_snapshot.json ==="
python3 << 'PYTHON_EOF'
import json
with open('packages/identity/drizzle/meta/0001_snapshot.json', 'r') as f:
snapshot = json.load(f)
# Print top-level keys
print("Top-level keys:", list(snapshot.keys()))
print()
# Look for user table
for key in snapshot.keys():
if 'user' in str(snapshot[key]).lower() and isinstance(snapshot[key], dict):
print(f"Found 'user' in key: {key}")
# Try to find the user table definition
tables = snapshot.get('tables', {})
if not tables:
# Try other possible keys
for key in snapshot:
if isinstance(snapshot[key], dict) and any('user' in str(k).lower() for k in snapshot[key].keys()):
tables = snapshot[key]
print(f"Found tables under key: {key}")
break
# Search for user table
for table_name, table_def in tables.items():
if 'user' in table_name.lower():
print(f"\nUser table: {table_name}")
if 'columns' in table_def:
for col_name, col_def in table_def['columns'].items():
if col_name == 'role':
print(f" {col_name}: {col_def}")
PYTHON_EOFRepository: pramodnarayana/nexiom
Length of output: 396
A database migration is required for the role default change. The schema.ts file has been updated to set the user role default to "member", but the latest migration snapshot (0001_workable_santa_claus) still reflects the previous default of "user".
Generate a new Drizzle migration to alter the user table and update the role column default value from "user" to "member", then apply it to the database.
🤖 Prompt for AI Agents
In `@packages/identity/src/schema.ts` at line 23, The schema change set role:
text("role").default("member") requires a matching Drizzle migration because the
existing migration snapshot 0001_workable_santa_claus still sets the default to
"user"; create a new migration that alters the users table to change the role
column default from 'user' to 'member' (e.g., ALTER TABLE users ALTER COLUMN
role SET DEFAULT 'member' for Postgres or the DB-specific equivalent), add the
migration file to your migrations folder and run the Drizzle migration command
to apply it to the database so the schema and migration history stay in sync.
- Fix duplicate role check in InviteUserDialog (r.id === 'member' || r.id === 'member' → r.id === 'member') - Add migration file 0002_update_role_default.sql to update user table role default from 'user' to 'member' The migration syncs the database schema with the code changes made in the unified role implementation.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@packages/identity/drizzle/0002_update_role_default.sql`:
- Line 4: The migration only sets a new default for the "role" column but
doesn't update existing rows still containing 'user', so current users will
break; modify the migration to first backfill existing rows by updating the
"user" table to set role = 'member' where role = 'user' (run this UPDATE before
or alongside the ALTER TABLE statement that sets DEFAULT 'member') so all
pre-existing rows match the new expected value and then apply ALTER TABLE "user"
ALTER COLUMN "role" SET DEFAULT 'member'.
Update migration to backfill existing rows with role='user' to role='member' before setting new default. This prevents breaking existing users who have the old 'user' role value. Changes: - Add UPDATE statement to convert existing 'user' roles to 'member' - Keep ALTER TABLE to set default for new rows - Re-applied migration successfully
Replace 'user' role with 'member' across entire codebase to align with unified RBAC system using owner/admin/member roles with isSystem flag.
Changes:
Verification:
✅ All identity tests passing (70/70)
✅ All API tests passing (190/190)
✅ Unified role system: owner, admin, member (with isSystem flag)
This completes the role unification, ensuring Nexiom operates as a tenant in its own system with the same role structure as all other tenants.
Summary by CodeRabbit