create-nara 1.0.21 → 1.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of create-nara might be problematic. Click here for more details.

package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-nara",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
4
4
  "description": "CLI to scaffold NARA projects",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,21 +1,71 @@
1
- import { BaseController, jsonSuccess, ValidationError } from '@nara-web/core';
1
+ import { BaseController, jsonSuccess, jsonError, ValidationError } from '@nara-web/core';
2
2
  import type { NaraRequest, NaraResponse } from '@nara-web/core';
3
+ import { UserModel } from '../models/User.js';
4
+ import { db } from '../config/database.js';
3
5
  import bcrypt from 'bcrypt';
4
6
 
5
7
  export class UserController extends BaseController {
6
8
  async index(req: NaraRequest, res: NaraResponse) {
7
- // TODO: Implement pagination and filtering
8
- // const { page = 1, limit = 10, search, filter } = req.query;
9
- // const users = await UserModel.paginate({ page, limit, search, filter });
9
+ // Get query parameters
10
+ const page = parseInt(req.query?.page as string) || 1;
11
+ const limit = parseInt(req.query?.limit as string) || 10;
12
+ const search = (req.query?.search as string) || '';
13
+ const filter = (req.query?.filter as string) || 'all';
14
+ const offset = (page - 1) * limit;
15
+
16
+ // Build query
17
+ let query = db('users').select(
18
+ 'id', 'name', 'email', 'phone', 'avatar', 'role',
19
+ 'email_verified_at', 'created_at', 'updated_at'
20
+ );
21
+
22
+ // Apply search filter
23
+ if (search) {
24
+ query = query.where(function() {
25
+ this.where('name', 'like', `%${search}%`)
26
+ .orWhere('email', 'like', `%${search}%`);
27
+ });
28
+ }
29
+
30
+ // Apply role filter
31
+ if (filter === 'admin') {
32
+ query = query.where('role', 'admin');
33
+ } else if (filter === 'user') {
34
+ query = query.where('role', 'user');
35
+ } else if (filter === 'verified') {
36
+ query = query.whereNotNull('email_verified_at');
37
+ } else if (filter === 'unverified') {
38
+ query = query.whereNull('email_verified_at');
39
+ }
40
+
41
+ // Get total count for pagination
42
+ const countQuery = query.clone();
43
+ const [{ count: totalCount }] = await countQuery.count('* as count');
44
+ const total = Number(totalCount);
45
+
46
+ // Apply pagination and ordering
47
+ const users = await query
48
+ .orderBy('created_at', 'desc')
49
+ .limit(limit)
50
+ .offset(offset);
51
+
52
+ // Transform users to include is_admin and is_verified flags
53
+ const transformedUsers = users.map(user => ({
54
+ ...user,
55
+ is_admin: user.role === 'admin',
56
+ is_verified: !!user.email_verified_at
57
+ }));
58
+
59
+ const totalPages = Math.ceil(total / limit);
10
60
 
11
61
  return jsonSuccess(res, {
12
- users: [],
13
- total: 0,
14
- page: 1,
15
- limit: 10,
16
- totalPages: 0,
17
- hasNext: false,
18
- hasPrev: false,
62
+ users: transformedUsers,
63
+ total,
64
+ page,
65
+ limit,
66
+ totalPages,
67
+ hasNext: page < totalPages,
68
+ hasPrev: page > 1,
19
69
  });
20
70
  }
21
71
 
@@ -29,12 +79,40 @@ export class UserController extends BaseController {
29
79
  });
30
80
  }
31
81
 
32
- // TODO: Create user in database
33
- // const hashedPassword = password ? await bcrypt.hash(password, 10) : null;
34
- // const user = await UserModel.create({ name, email, password: hashedPassword, phone, is_admin, is_verified });
82
+ // Check if email already exists
83
+ const existing = await UserModel.findByEmail(email);
84
+ if (existing) {
85
+ throw new ValidationError({ email: ['Email already registered'] });
86
+ }
87
+
88
+ // Hash password if provided, otherwise generate random
89
+ const hashedPassword = password
90
+ ? await bcrypt.hash(password, 10)
91
+ : await bcrypt.hash(Math.random().toString(36).slice(-8), 10);
92
+
93
+ // Create user in database
94
+ const [userId] = await UserModel.create({
95
+ name,
96
+ email,
97
+ password: hashedPassword,
98
+ phone: phone || null,
99
+ role: is_admin ? 'admin' : 'user',
100
+ email_verified_at: is_verified ? new Date().toISOString() : null
101
+ });
102
+
103
+ // Fetch created user
104
+ const user = await UserModel.findById(userId);
35
105
 
36
106
  return jsonSuccess(res, {
37
- user: { id: '1', name, email, phone, is_admin, is_verified }
107
+ user: {
108
+ id: user?.id,
109
+ name: user?.name,
110
+ email: user?.email,
111
+ phone: user?.phone,
112
+ avatar: user?.avatar,
113
+ is_admin: user?.role === 'admin',
114
+ is_verified: !!user?.email_verified_at
115
+ }
38
116
  }, 'User created successfully');
39
117
  }
40
118
 
@@ -49,15 +127,49 @@ export class UserController extends BaseController {
49
127
  });
50
128
  }
51
129
 
52
- // TODO: Update user in database
53
- // const updateData: any = { name, email, phone, is_admin, is_verified };
54
- // if (password) {
55
- // updateData.password = await bcrypt.hash(password, 10);
56
- // }
57
- // await UserModel.update(id, updateData);
130
+ // Check if user exists
131
+ const existingUser = await UserModel.findById(Number(id));
132
+ if (!existingUser) {
133
+ return jsonError(res, 'User not found', 404);
134
+ }
135
+
136
+ // Check if email is taken by another user
137
+ const emailUser = await UserModel.findByEmail(email);
138
+ if (emailUser && emailUser.id !== Number(id)) {
139
+ throw new ValidationError({ email: ['Email already registered'] });
140
+ }
141
+
142
+ // Build update data
143
+ const updateData: Record<string, any> = {
144
+ name,
145
+ email,
146
+ phone: phone || null,
147
+ role: is_admin ? 'admin' : 'user',
148
+ email_verified_at: is_verified ? (existingUser.email_verified_at || new Date().toISOString()) : null,
149
+ updated_at: new Date().toISOString()
150
+ };
151
+
152
+ // Hash new password if provided
153
+ if (password) {
154
+ updateData.password = await bcrypt.hash(password, 10);
155
+ }
156
+
157
+ // Update user in database
158
+ await UserModel.update(Number(id), updateData);
159
+
160
+ // Fetch updated user
161
+ const user = await UserModel.findById(Number(id));
58
162
 
59
163
  return jsonSuccess(res, {
60
- user: { id, name, email, phone, is_admin, is_verified }
164
+ user: {
165
+ id: user?.id,
166
+ name: user?.name,
167
+ email: user?.email,
168
+ phone: user?.phone,
169
+ avatar: user?.avatar,
170
+ is_admin: user?.role === 'admin',
171
+ is_verified: !!user?.email_verified_at
172
+ }
61
173
  }, 'User updated successfully');
62
174
  }
63
175
 
@@ -70,9 +182,15 @@ export class UserController extends BaseController {
70
182
  });
71
183
  }
72
184
 
73
- // TODO: Delete users from database
74
- // await UserModel.deleteMany(ids);
185
+ // Prevent deleting current user
186
+ const currentUserId = req.user?.id;
187
+ if (currentUserId && ids.includes(currentUserId)) {
188
+ return jsonError(res, 'Cannot delete your own account', 400);
189
+ }
190
+
191
+ // Delete users from database
192
+ const deleted = await db('users').whereIn('id', ids).delete();
75
193
 
76
- return jsonSuccess(res, {}, `${ids.length} user(s) deleted successfully`);
194
+ return jsonSuccess(res, { deleted }, `${deleted} user(s) deleted successfully`);
77
195
  }
78
196
  }