Skip to content

Commit 7cd8cbb

Browse files
committed
refactor: update user model to contain new fields, and omitting username for unnecessary use
1 parent 391fcac commit 7cd8cbb

6 files changed

Lines changed: 50 additions & 5 deletions

File tree

README.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,14 +188,22 @@ Content-Type: application/json
188188
"fname": "Test",
189189
"lname": "User"
190190
},
191-
"username": "Userrr",
191+
"username": "custom-username",
192192
"email": "user@example.com",
193193
"password": "Pa$$word!",
194194
"phoneNumber": "+1234567890",
195-
"avatar": "https://example.com/avatar.jpg"
195+
"avatar": "https://example.com/avatar.jpg",
196+
"jobTitle": "Software Engineer",
197+
"company": "Ciscode"
196198
}
197199
```
198200

201+
**Notes:**
202+
203+
- `username` is now **optional**. If not provided, it will be auto-generated as `fname-lname` (e.g., `test-user`)
204+
- `jobTitle` and `company` are **optional** profile fields
205+
- All other fields work as before
206+
199207
**Response:**
200208

201209
```json
@@ -387,10 +395,12 @@ All permissions are assigned to the `admin` role.
387395
fname: string,
388396
lname: string
389397
},
390-
username: string (unique, 3-30 chars),
398+
username: string (unique, 3-30 chars, auto-generated as fname-lname if not provided),
391399
email: string (unique, validated),
392400
phoneNumber?: string (unique, 10-14 digits),
393401
avatar?: string (default: 'default.jpg'),
402+
jobTitle?: string,
403+
company?: string,
394404
password: string (hashed, min 6 chars),
395405
roles: ObjectId[] (references Role),
396406
isVerified: boolean (default: false),

src/dtos/auth/register.dto.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ export class RegisterDto {
1111
@Type(() => FullNameDto)
1212
fullname!: FullNameDto;
1313

14+
@IsOptional()
1415
@IsString()
1516
@MinLength(3)
16-
username!: string;
17+
username?: string;
1718

1819
@IsEmail()
1920
email!: string;
@@ -29,4 +30,12 @@ export class RegisterDto {
2930
@IsOptional()
3031
@IsString()
3132
avatar?: string;
33+
34+
@IsOptional()
35+
@IsString()
36+
jobTitle?: string;
37+
38+
@IsOptional()
39+
@IsString()
40+
company?: string;
3241
}

src/models/user.model.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export class User {
4747

4848
@Prop({ default: Date.now })
4949
passwordChangedAt!: Date;
50-
50+
5151
@Prop({ type: [{ type: Types.ObjectId, ref: 'Role' }], required: true })
5252
roles!: Types.ObjectId[];
5353

@@ -57,6 +57,12 @@ export class User {
5757
@Prop({ default: false })
5858
isBanned!: boolean;
5959

60+
@Prop({ trim: true, sparse: true })
61+
jobTitle?: string;
62+
63+
@Prop({ trim: true, sparse: true })
64+
company?: string;
65+
6066
}
6167

6268
export const UserSchema = SchemaFactory.createForClass(User);

src/services/auth.service.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { RegisterDto } from '@dtos/auth/register.dto';
77
import { LoginDto } from '@dtos/auth/login.dto';
88
import { MailService } from '@services/mail.service';
99
import { RoleRepository } from '@repos/role.repository';
10+
import { generateUsernameFromName } from '@utils/helper';
1011

1112
type JwtExpiry = SignOptions['expiresIn'];
1213

@@ -70,6 +71,11 @@ export class AuthService {
7071

7172

7273
async register(dto: RegisterDto) {
74+
// Generate username from fname-lname if not provided
75+
if (!dto.username || dto.username.trim() === '') {
76+
dto.username = generateUsernameFromName(dto.fullname.fname, dto.fullname.lname);
77+
}
78+
7379
if (await this.users.findByEmail(dto.email)) throw new Error('Email already in use.');
7480
if (await this.users.findByUsername(dto.username)) throw new Error('Username already in use.');
7581
if (dto.phoneNumber && (await this.users.findByPhone(dto.phoneNumber))) {
@@ -89,6 +95,8 @@ export class AuthService {
8995
email: dto.email,
9096
phoneNumber: dto.phoneNumber,
9197
avatar: dto.avatar,
98+
jobTitle: dto.jobTitle,
99+
company: dto.company,
92100
password: hashed,
93101
roles: [userRole._id],
94102
isVerified: false,

src/services/users.service.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { UserRepository } from '@repos/user.repository';
44
import { RoleRepository } from '@repos/role.repository';
55
import { RegisterDto } from '@dtos/auth/register.dto';
66
import { Types } from 'mongoose';
7+
import { generateUsernameFromName } from '@utils/helper';
78

89
@Injectable()
910
export class UsersService {
@@ -13,6 +14,11 @@ export class UsersService {
1314
) { }
1415

1516
async create(dto: RegisterDto) {
17+
// Generate username from fname-lname if not provided
18+
if (!dto.username || dto.username.trim() === '') {
19+
dto.username = generateUsernameFromName(dto.fullname.fname, dto.fullname.lname);
20+
}
21+
1622
if (await this.users.findByEmail(dto.email)) throw new Error('Email already in use.');
1723
if (await this.users.findByUsername(dto.username)) throw new Error('Username already in use.');
1824
if (dto.phoneNumber && (await this.users.findByPhone(dto.phoneNumber))) {
@@ -28,6 +34,8 @@ export class UsersService {
2834
email: dto.email,
2935
phoneNumber: dto.phoneNumber,
3036
avatar: dto.avatar,
37+
jobTitle: dto.jobTitle,
38+
company: dto.company,
3139
password: hashed,
3240
roles: [],
3341
isVerified: true,

src/utils/helper.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,7 @@ export function getMillisecondsFromExpiry(expiry: string | number): number {
1919
return 0;
2020
}
2121
}
22+
23+
export function generateUsernameFromName(fname: string, lname: string): string {
24+
return `${fname.toLowerCase()}-${lname.toLowerCase()}`;
25+
}

0 commit comments

Comments
 (0)