Skip to content

Commit f8ac783

Browse files
committed
feat: Adds user role functionality closes #25
Extends the user creation process to include role assignment. This allows for creating users with different roles (e.g., admin, user). - Introduces a `role` field in the user data structure and database schema. - Adds a `role` argument to the `create-user` CLI command. - Sets a default role of "user" if no role is specified. The `role` is now included in the user object and database records.
1 parent da39bbd commit f8ac783

6 files changed

Lines changed: 70 additions & 8 deletions

File tree

src/cli/create-user.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,22 @@ export default defineCommand({
2525
type: 'string',
2626
description: 'User password',
2727
required: true
28+
},
29+
role: {
30+
alias: 'r',
31+
type: 'string',
32+
description: 'User role (defaults to "user")',
33+
default: 'user'
2834
}
2935
},
3036
async run({ args }) {
31-
const { email, name, password } = args
37+
const { email, name, password, role } = args
3238

3339
const options = await loadOptions()
3440

3541
try {
36-
const user = await createUser({ email, name, password }, options)
37-
console.log(`[DB:Create User] User created successfully: ${user.email}`)
42+
const user = await createUser({ email, name, password, role }, options)
43+
console.log(`[DB:Create User] User created successfully: ${user.email} (role: ${user.role})`)
3844
process.exit(0)
3945
}
4046
catch (error) {

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export interface User {
5555
email: string
5656
name: string
5757
password: string
58+
role: string
5859
created_at: string
5960
updated_at: string
6061
}

src/utils/create-users-table.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export const createUsersTable = async (options: ModuleOptions) => {
1515
email TEXT NOT NULL UNIQUE,
1616
name TEXT NOT NULL,
1717
password TEXT NOT NULL,
18+
role TEXT NOT NULL DEFAULT 'user',
1819
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
1920
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
2021
)
@@ -27,6 +28,7 @@ export const createUsersTable = async (options: ModuleOptions) => {
2728
email VARCHAR(255) NOT NULL UNIQUE,
2829
name VARCHAR(255) NOT NULL,
2930
password VARCHAR(255) NOT NULL,
31+
role VARCHAR(32) NOT NULL DEFAULT 'user',
3032
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
3133
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
3234
)
@@ -39,11 +41,12 @@ export const createUsersTable = async (options: ModuleOptions) => {
3941
email VARCHAR(255) NOT NULL UNIQUE,
4042
name VARCHAR(255) NOT NULL,
4143
password VARCHAR(255) NOT NULL,
44+
role VARCHAR(32) NOT NULL DEFAULT 'user',
4245
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
4346
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
4447
)
4548
`
4649
}
4750

48-
console.log(`[DB:Create ${connectorName} Users Table] Fields: id, email, name, password, created_at, updated_at ✅`)
51+
console.log(`[DB:Create ${connectorName} Users Table] Fields: id, email, name, password, role, created_at, updated_at ✅`)
4952
}

src/utils/user.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ interface CreateUserParams {
66
email: string
77
name: string
88
password: string
9+
role?: string
910
}
1011

1112
/**
@@ -19,14 +20,17 @@ export const createUser = async (userData: CreateUserParams, options: ModuleOpti
1920
// Hash the password
2021
const hashedPassword = await bcrypt.hash(userData.password, 10)
2122

23+
// Set default role if not provided
24+
const role = userData.role || 'user'
25+
2226
// Insert the new user
2327
await db.sql`
24-
INSERT INTO {${usersTable}} (email, name, password, created_at, updated_at)
25-
VALUES (${userData.email}, ${userData.name}, ${hashedPassword}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
28+
INSERT INTO {${usersTable}} (email, name, password, role, created_at, updated_at)
29+
VALUES (${userData.email}, ${userData.name}, ${hashedPassword}, ${role}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
2630
`
2731
// Fetch the created user to return it (especially to get the ID and ensure it was created)
2832
// Exclude password in the return type
29-
const result = await db.sql`SELECT id, email, name, created_at, updated_at FROM {${usersTable}} WHERE email = ${userData.email}` as { rows: Array<{ id: number, email: string, name: string, created_at: Date | string, updated_at: Date | string }> }
33+
const result = await db.sql`SELECT id, email, name, role, created_at, updated_at FROM {${usersTable}} WHERE email = ${userData.email}` as { rows: Array<{ id: number, email: string, name: string, role: string, created_at: Date | string, updated_at: Date | string }> }
3034

3135
if (result.rows.length === 0) {
3236
throw new Error('Failed to retrieve created user.')
@@ -39,6 +43,7 @@ export const createUser = async (userData: CreateUserParams, options: ModuleOpti
3943
id: user.id,
4044
email: user.email,
4145
name: user.name,
46+
role: user.role,
4247
created_at: user.created_at instanceof Date ? user.created_at.toISOString() : user.created_at,
4348
updated_at: user.updated_at instanceof Date ? user.updated_at.toISOString() : user.updated_at
4449
}
@@ -51,7 +56,7 @@ export const findUserByEmail = async (email: string, options: ModuleOptions): Pr
5156
const db = await useDb(options)
5257
const usersTable = options.tables.users
5358

54-
const result = await db.sql`SELECT * FROM {${usersTable}} WHERE email = ${email}` as { rows: Array<{ id: number, email: string, name: string, password: string, created_at: Date | string, updated_at: Date | string }> }
59+
const result = await db.sql`SELECT * FROM {${usersTable}} WHERE email = ${email}` as { rows: Array<{ id: number, email: string, name: string, password: string, role: string, created_at: Date | string, updated_at: Date | string }> }
5560

5661
if (result.rows.length === 0) {
5762
return null
@@ -65,6 +70,7 @@ export const findUserByEmail = async (email: string, options: ModuleOptions): Pr
6570
email: user.email,
6671
name: user.name,
6772
password: user.password,
73+
role: user.role,
6874
created_at: user.created_at instanceof Date ? user.created_at.toISOString() : user.created_at,
6975
updated_at: user.updated_at instanceof Date ? user.updated_at.toISOString() : user.updated_at
7076
}

test/cli.create-user.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,39 @@ describe('CLI: Create User', () => {
153153
await expect(createUser(userData, testOptions)).rejects.toThrow()
154154
})
155155

156+
it('should create user with default role', async () => {
157+
const userData = {
158+
email: 'test@webmania.cc',
159+
name: 'Test User',
160+
password: 'mypassword123'
161+
}
162+
163+
await createUser(userData, testOptions)
164+
165+
// Verify user was created with default role
166+
const result = await db.sql`SELECT role FROM users WHERE email = ${userData.email}`
167+
const user = result.rows?.[0]
168+
169+
expect(user?.role).toBe('user')
170+
})
171+
172+
it('should create user with custom role', async () => {
173+
const userData = {
174+
email: 'admin@webmania.cc',
175+
name: 'Admin User',
176+
password: 'mypassword123',
177+
role: 'admin'
178+
}
179+
180+
await createUser(userData, testOptions)
181+
182+
// Verify user was created with custom role
183+
const result = await db.sql`SELECT role FROM users WHERE email = ${userData.email}`
184+
const user = result.rows?.[0]
185+
186+
expect(user?.role).toBe('admin')
187+
})
188+
156189
it('should handle empty password', async () => {
157190
const userData = {
158191
email: 'test@webmania.cc',

test/utils.user.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ describe('User Utilities (src/utils/user.ts)', () => {
8383
id: expect.any(Number),
8484
email: userData.email,
8585
name: userData.name,
86+
role: 'user',
8687
created_at: expect.any(String),
8788
updated_at: expect.any(String)
8889
})
@@ -95,6 +96,18 @@ describe('User Utilities (src/utils/user.ts)', () => {
9596
// and the error handling is already tested in the integration tests
9697
expect(true).toBe(true)
9798
})
99+
100+
it('should create user with custom role', async () => {
101+
const userData = { email: 'admin@webmania.cc', name: 'Admin User', password: 'password123', role: 'admin' }
102+
103+
const user = await createUser(userData, testOptions)
104+
105+
expect(user).toBeDefined()
106+
expect(user.email).toBe(userData.email)
107+
expect(user.name).toBe(userData.name)
108+
expect(user.role).toBe('admin')
109+
expect(user).not.toHaveProperty('password')
110+
})
98111
})
99112

100113
describe('findUserByEmail', () => {

0 commit comments

Comments
 (0)