File size: 12,752 Bytes
8275526
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
import os
import datetime
from pymongo import MongoClient
from bson.objectid import ObjectId
import bcrypt
import logging
from dotenv import load_dotenv

# Cấu hình logging
logger = logging.getLogger(__name__)

# Tải biến môi trường
load_dotenv()

# Kết nối MongoDB
MONGO_URI = os.getenv("MONGO_URI", "mongodb://localhost:27017/")
DATABASE_NAME = os.getenv("MONGO_DB_NAME", "nutribot_db")

# Singleton pattern cho kết nối MongoDB
_mongo_client = None
_db = None

def get_db():
    """Trả về instance của MongoDB database (singleton pattern)"""
    global _mongo_client, _db
    if _mongo_client is None:
        try:
            _mongo_client = MongoClient(MONGO_URI)
            _db = _mongo_client[DATABASE_NAME]
            logger.info(f"Đã kết nối đến database: {DATABASE_NAME}")
        except Exception as e:
            logger.error(f"Lỗi kết nối MongoDB: {e}")
            raise
    return _db

class User:
    def __init__(self, name, email, password, gender=None, role="user", permissions=None,
                 user_id=None, created_at=None, updated_at=None, last_login=None):
        self.user_id = user_id
        self.name = name
        self.email = email
        self.password = password  # Mật khẩu đã mã hóa
        self.gender = gender
        self.role = role  # "user" hoặc "admin"
        self.permissions = permissions or self._get_default_permissions(role)
        self.created_at = created_at or datetime.datetime.now()
        self.updated_at = updated_at or datetime.datetime.now()
        self.last_login = last_login

    def _get_default_permissions(self, role):
        """Lấy quyền mặc định theo role"""
        if role == "admin":
            return {
                "users": {"read": True, "write": True, "delete": True},
                "documents": {"read": True, "write": True, "delete": True},
                "conversations": {"read": True, "write": True, "delete": True},
                "system": {"read": True, "write": True, "delete": False},
                "analytics": {"read": True, "write": False, "delete": False}
            }
        else:  # role == "user"
            return {
                "conversations": {"read": True, "write": True, "delete": True},
                "profile": {"read": True, "write": True, "delete": False}
            }

    @staticmethod
    def hash_password(password):
        """Mã hóa mật khẩu sử dụng bcrypt"""
        salt = bcrypt.gensalt()
        hashed_pw = bcrypt.hashpw(password.encode('utf-8'), salt)
        return hashed_pw

    @staticmethod
    def check_password(hashed_password, password):
        """Kiểm tra mật khẩu"""
        return bcrypt.checkpw(password.encode('utf-8'), hashed_password)

    def is_admin(self):
        """Kiểm tra xem user có phải admin không"""
        return self.role == "admin"

    def has_permission(self, resource, action):
        """Kiểm tra quyền truy cập"""
        if not self.permissions:
            return False
        
        resource_perms = self.permissions.get(resource, {})
        return resource_perms.get(action, False)

    def to_dict(self):
        """Chuyển đổi thông tin user thành dictionary"""
        user_dict = {
            "name": self.name,
            "email": self.email,
            "password": self.password,
            "gender": self.gender,
            "role": self.role,
            "permissions": self.permissions,
            "created_at": self.created_at,
            "updated_at": self.updated_at,
            "last_login": self.last_login
        }
        if self.user_id:
            user_dict["_id"] = self.user_id
        return user_dict

    @classmethod
    def from_dict(cls, user_dict):
        """Tạo đối tượng User từ dictionary"""
        if not user_dict:
            return None
            
        return cls(
            user_id=user_dict.get("_id"),
            name=user_dict.get("name"),
            email=user_dict.get("email"),
            password=user_dict.get("password"),
            gender=user_dict.get("gender"),
            role=user_dict.get("role", "user"),  # Mặc định là user
            permissions=user_dict.get("permissions"),
            created_at=user_dict.get("created_at"),
            updated_at=user_dict.get("updated_at"),
            last_login=user_dict.get("last_login")
        )

    @classmethod
    def find_by_email(cls, email):
        """Tìm người dùng theo email"""
        try:
            db = get_db()
            users_collection = db.users
            user_data = users_collection.find_one({"email": email})
            return cls.from_dict(user_data)
        except Exception as e:
            logger.error(f"Lỗi tìm người dùng theo email: {e}")
            return None

    @classmethod
    def find_by_id(cls, user_id):
        """Tìm người dùng theo ID"""
        try:
            db = get_db()
            users_collection = db.users
            user_data = users_collection.find_one({"_id": ObjectId(user_id)})
            return cls.from_dict(user_data)
        except Exception as e:
            logger.error(f"Lỗi tìm người dùng theo ID: {e}")
            return None

    @classmethod
    def get_all_admins(cls, page=1, per_page=10):
        """Lấy danh sách tất cả admin với phân trang"""
        try:
            db = get_db()
            users_collection = db.users
            
            skip = (page - 1) * per_page
            admins_data = users_collection.find(
                {"role": "admin"}
            ).sort("created_at", -1).skip(skip).limit(per_page)
            
            total = users_collection.count_documents({"role": "admin"})
            
            return [cls.from_dict(admin) for admin in admins_data], total
        except Exception as e:
            logger.error(f"Lỗi lấy danh sách admin: {e}")
            return [], 0

    def save(self):
        """Lưu thông tin người dùng vào database"""
        try:
            db = get_db()
            users_collection = db.users
            
            if not self.user_id:
                # Đây là người dùng mới
                insert_result = users_collection.insert_one(self.to_dict())
                self.user_id = insert_result.inserted_id
                logger.info(f"Đã tạo người dùng mới với ID: {self.user_id}")
                return self.user_id
            else:
                # Cập nhật người dùng đã tồn tại
                self.updated_at = datetime.datetime.now()
                users_collection.update_one(
                    {"_id": self.user_id}, 
                    {"$set": self.to_dict()}
                )
                logger.info(f"Đã cập nhật thông tin người dùng: {self.user_id}")
                return self.user_id
        except Exception as e:
            logger.error(f"Lỗi khi lưu thông tin người dùng: {e}")
            raise

    def update_last_login(self):
        """Cập nhật thời gian đăng nhập cuối"""
        try:
            self.last_login = datetime.datetime.now()
            self.save()
        except Exception as e:
            logger.error(f"Lỗi cập nhật last_login: {e}")

    def delete(self):
        """Xóa người dùng từ database"""
        try:
            if self.user_id:
                db = get_db()
                users_collection = db.users
                users_collection.delete_one({"_id": self.user_id})
                logger.info(f"Đã xóa người dùng: {self.user_id}")
                return True
            return False
        except Exception as e:
            logger.error(f"Lỗi khi xóa người dùng: {e}")
            return False

    @staticmethod
    def register(name, email, password, gender=None, role="user"):
        """Đăng ký người dùng mới"""
        try:
            # Kiểm tra email đã tồn tại chưa
            existing_user = User.find_by_email(email)
            if existing_user:
                return False, "Email đã được sử dụng"
            
            # Mã hóa mật khẩu
            hashed_password = User.hash_password(password)
            
            # Tạo người dùng mới
            new_user = User(
                name=name,
                email=email,
                password=hashed_password,
                gender=gender,
                role=role
            )
            
            # Lưu vào database
            user_id = new_user.save()
            
            return True, {"user_id": str(user_id)}
        except Exception as e:
            logger.error(f"Lỗi đăng ký người dùng: {e}")
            return False, f"Lỗi đăng ký: {str(e)}"

    @staticmethod
    def login(email, password):
        """Đăng nhập người dùng"""
        try:
            if not email or not password:
                return False, "Vui lòng nhập đầy đủ thông tin đăng nhập"
            
            user = User.find_by_email(email)
            if not user:
                return False, "Email không tồn tại"
            
            if not User.check_password(user.password, password):
                return False, "Mật khẩu không chính xác"
            
            # Cập nhật thời gian đăng nhập
            user.update_last_login()
            
            return True, user
        except Exception as e:
            logger.error(f"Lỗi đăng nhập: {e}")
            return False, f"Lỗi đăng nhập: {str(e)}"

    @staticmethod
    def create_admin(name, email, password, gender=None):
        """Tạo admin mới"""
        return User.register(name, email, password, gender, role="admin")

    @staticmethod
    def create_default_admin():
        """Tạo admin mặc định nếu chưa có"""
        try:
            db = get_db()
            users_collection = db.users
            
            # Kiểm tra xem đã có admin chưa
            existing_admin = users_collection.find_one({"role": "admin"})
            
            if not existing_admin:
                # Tạo admin mặc định
                default_email = "admin@nutribot.com"
                default_password = "NutribotAdmin2024!"
                
                success, result = User.create_admin(
                    name="Administrator",
                    email=default_email,
                    password=default_password
                )
                
                if success:
                    logger.info(f"Đã tạo admin mặc định: {default_email}")
                    logger.info(f"Mật khẩu mặc định: {default_password}")
                    return True, {
                        "email": default_email,
                        "password": default_password,
                        "user_id": result["user_id"]
                    }
                else:
                    logger.error(f"Lỗi tạo admin mặc định: {result}")
                    return False, result
            else:
                logger.info("Admin đã tồn tại")
                return True, {"message": "Admin đã tồn tại"}
                
        except Exception as e:
            logger.error(f"Lỗi tạo admin mặc định: {e}")
            return False, str(e)

    @staticmethod
    def get_stats():
        """Lấy thống kê về users"""
        try:
            db = get_db()
            users_collection = db.users
            
            # Tổng số users
            total_users = users_collection.count_documents({})
            
            # Số admin
            total_admins = users_collection.count_documents({"role": "admin"})
            
            # Users đăng ký hôm nay
            today_start = datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
            new_users_today = users_collection.count_documents({
                "created_at": {"$gte": today_start}
            })
            
            # Users có hoạt động (có last_login)
            active_users = users_collection.count_documents({
                "last_login": {"$exists": True}
            })
            
            return {
                "total_users": total_users,
                "total_admins": total_admins,
                "active_users": active_users,
                "new_users_today": new_users_today
            }
        except Exception as e:
            logger.error(f"Lỗi lấy thống kê users: {e}")
            return {
                "total_users": 0,
                "total_admins": 0,
                "active_users": 0,
                "new_users_today": 0
            }