mb672038 commited on
Commit
aa06156
·
verified ·
1 Parent(s): b3c8917

می خوام که واقعا برنامه لاگین داشته باشه و ورود همچنین با توضیحا نی که دادم نمی خوام نمایشی باشه می خوام واقعی کار کنه

Browse files
client/src/components/PrivateRoute.jsx ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useAuth } from '../context/AuthContext';
2
+ import { Navigate, Outlet } from 'react-router-dom';
3
+ import { toast } from 'react-toastify';
4
+
5
+ export default function PrivateRoute({ roles, children }) {
6
+ const { user, isAuthenticated, hasRole } = useAuth();
7
+
8
+ if (!isAuthenticated()) {
9
+ toast.error('لطفا ابتدا وارد شوید');
10
+ return <Navigate to="/login" />;
11
+ }
12
+
13
+ if (roles && !roles.some(role => hasRole(role))) {
14
+ toast.error('شما مجوز دسترسی به این صفحه را ندارید');
15
+ return <Navigate to="/" />;
16
+ }
17
+
18
+ return children ? children : <Outlet />;
19
+ }
client/src/context/AuthContext.jsx CHANGED
@@ -1,70 +1,105 @@
 
1
  import { createContext, useContext, useState, useEffect } from 'react';
2
  import axios from 'axios';
3
  import { useNavigate } from 'react-router-dom';
 
4
 
5
  const AuthContext = createContext();
6
 
7
  export function AuthProvider({ children }) {
8
  const [user, setUser] = useState(null);
9
- const [token, setToken] = useState(localStorage.getItem('token'));
10
  const [loading, setLoading] = useState(true);
11
  const navigate = useNavigate();
12
 
13
- // Check for token and load user
 
 
 
14
  useEffect(() => {
15
- if (token) {
16
- axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
17
- getUser();
18
- } else {
19
- setLoading(false);
20
- }
21
- }, [token]);
 
 
 
22
 
23
- const getUser = async () => {
24
- try {
25
- const res = await axios.get('/api/auth/me');
26
- setUser(res.data);
27
- } catch (err) {
28
- logout();
29
- } finally {
30
- setLoading(false);
31
- }
32
- };
33
 
 
34
  const register = async (formData) => {
35
  try {
 
36
  const res = await axios.post('/api/auth/register', formData);
37
- setToken(res.data.token);
38
- localStorage.setItem('token', res.data.token);
39
  setUser(res.data.user);
 
40
  navigate('/dashboard');
 
41
  } catch (err) {
42
- throw err.response.data;
 
 
 
 
43
  }
44
  };
45
 
 
46
  const login = async (formData) => {
47
  try {
 
48
  const res = await axios.post('/api/auth/login', formData);
49
- setToken(res.data.token);
50
- localStorage.setItem('token', res.data.token);
51
  setUser(res.data.user);
 
52
  navigate('/dashboard');
 
53
  } catch (err) {
54
- throw err.response.data;
 
 
 
 
55
  }
56
  };
57
 
58
- const logout = () => {
59
- localStorage.removeItem('token');
60
- delete axios.defaults.headers.common['Authorization'];
61
- setToken(null);
62
- setUser(null);
63
- navigate('/');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  };
65
 
66
  return (
67
- <AuthContext.Provider value={{ user, token, loading, register, login, logout }}>
 
 
 
 
 
 
 
 
 
 
68
  {children}
69
  </AuthContext.Provider>
70
  );
@@ -72,4 +107,4 @@ export function AuthProvider({ children }) {
72
 
73
  export function useAuth() {
74
  return useContext(AuthContext);
75
- }
 
1
+
2
  import { createContext, useContext, useState, useEffect } from 'react';
3
  import axios from 'axios';
4
  import { useNavigate } from 'react-router-dom';
5
+ import { toast } from 'react-toastify';
6
 
7
  const AuthContext = createContext();
8
 
9
  export function AuthProvider({ children }) {
10
  const [user, setUser] = useState(null);
 
11
  const [loading, setLoading] = useState(true);
12
  const navigate = useNavigate();
13
 
14
+ // Set axios defaults
15
+ axios.defaults.withCredentials = true;
16
+
17
+ // Check auth status on app load
18
  useEffect(() => {
19
+ const checkAuth = async () => {
20
+ try {
21
+ const res = await axios.get('/api/auth/me');
22
+ setUser(res.data.user);
23
+ } catch (err) {
24
+ setUser(null);
25
+ } finally {
26
+ setLoading(false);
27
+ }
28
+ };
29
 
30
+ checkAuth();
31
+ }, []);
 
 
 
 
 
 
 
 
32
 
33
+ // Register user
34
  const register = async (formData) => {
35
  try {
36
+ setLoading(true);
37
  const res = await axios.post('/api/auth/register', formData);
 
 
38
  setUser(res.data.user);
39
+ toast.success('ثبت‌نام با موفقیت انجام شد');
40
  navigate('/dashboard');
41
+ return res.data;
42
  } catch (err) {
43
+ const errorMessage = err.response?.data?.message || 'خطا در ثبت‌نام';
44
+ toast.error(errorMessage);
45
+ throw errorMessage;
46
+ } finally {
47
+ setLoading(false);
48
  }
49
  };
50
 
51
+ // Login user
52
  const login = async (formData) => {
53
  try {
54
+ setLoading(true);
55
  const res = await axios.post('/api/auth/login', formData);
 
 
56
  setUser(res.data.user);
57
+ toast.success('ورود با موفقیت انجام شد');
58
  navigate('/dashboard');
59
+ return res.data;
60
  } catch (err) {
61
+ const errorMessage = err.response?.data?.message || 'خطا در ورود';
62
+ toast.error(errorMessage);
63
+ throw errorMessage;
64
+ } finally {
65
+ setLoading(false);
66
  }
67
  };
68
 
69
+ // Logout user
70
+ const logout = async () => {
71
+ try {
72
+ await axios.get('/api/auth/logout');
73
+ setUser(null);
74
+ toast.success('خروج با موفقیت انجام شد');
75
+ navigate('/');
76
+ } catch (err) {
77
+ toast.error('خطا در خروج از سیستم');
78
+ }
79
+ };
80
+
81
+ // Check if user is authenticated
82
+ const isAuthenticated = () => {
83
+ return !!user;
84
+ };
85
+
86
+ // Check if user has specific role
87
+ const hasRole = (role) => {
88
+ return user?.role === role;
89
  };
90
 
91
  return (
92
+ <AuthContext.Provider
93
+ value={{
94
+ user,
95
+ loading,
96
+ register,
97
+ login,
98
+ logout,
99
+ isAuthenticated,
100
+ hasRole
101
+ }}
102
+ >
103
  {children}
104
  </AuthContext.Provider>
105
  );
 
107
 
108
  export function useAuth() {
109
  return useContext(AuthContext);
110
+ }
client/src/pages/Login.jsx ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState } from 'react';
2
+ import { useNavigate } from 'react-router-dom';
3
+ import { useAuth } from '../context/AuthContext';
4
+ import { toast } from 'react-toastify';
5
+
6
+ export default function Login() {
7
+ const [formData, setFormData] = useState({
8
+ email: '',
9
+ password: ''
10
+ });
11
+ const [loading, setLoading] = useState(false);
12
+ const { login } = useAuth();
13
+ const navigate = useNavigate();
14
+
15
+ const handleChange = (e) => {
16
+ setFormData({
17
+ ...formData,
18
+ [e.target.name]: e.target.value
19
+ });
20
+ };
21
+
22
+ const handleSubmit = async (e) => {
23
+ e.preventDefault();
24
+ setLoading(true);
25
+
26
+ try {
27
+ await login(formData);
28
+ toast.success('ورود با موفقیت انجام شد');
29
+ navigate('/dashboard');
30
+ } catch (err) {
31
+ toast.error(err.message || 'خطا در ورود');
32
+ } finally {
33
+ setLoading(false);
34
+ }
35
+ };
36
+
37
+ return (
38
+ <div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
39
+ <div className="max-w-md w-full space-y-8 bg-white p-8 rounded-lg shadow-md">
40
+ <div className="text-center">
41
+ <h2 className="mt-6 text-3xl font-extrabold text-gray-900">ورود به حساب کاربری</h2>
42
+ </div>
43
+ <form className="mt-8 space-y-6" onSubmit={handleSubmit}>
44
+ <div className="rounded-md shadow-sm space-y-4">
45
+ <div>
46
+ <label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-1">
47
+ آدرس ایمیل
48
+ </label>
49
+ <input
50
+ id="email"
51
+ name="email"
52
+ type="email"
53
+ autoComplete="email"
54
+ required
55
+ className="appearance-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-md focus:outline-none focus:ring-primary focus:border-primary focus:z-10 sm:text-sm"
56
+ placeholder="example@domain.com"
57
+ value={formData.email}
58
+ onChange={handleChange}
59
+ />
60
+ </div>
61
+ <div>
62
+ <label htmlFor="password" className="block text-sm font-medium text-gray-700 mb-1">
63
+ رمز عبور
64
+ </label>
65
+ <input
66
+ id="password"
67
+ name="password"
68
+ type="password"
69
+ autoComplete="current-password"
70
+ required
71
+ className="appearance-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-md focus:outline-none focus:ring-primary focus:border-primary focus:z-10 sm:text-sm"
72
+ placeholder="••••••••"
73
+ value={formData.password}
74
+ onChange={handleChange}
75
+ />
76
+ </div>
77
+ </div>
78
+
79
+ <div className="flex items-center justify-between">
80
+ <div className="flex items-center">
81
+ <input
82
+ id="remember-me"
83
+ name="remember-me"
84
+ type="checkbox"
85
+ className="h-4 w-4 text-primary focus:ring-primary border-gray-300 rounded"
86
+ />
87
+ <label htmlFor="remember-me" className="mr-2 block text-sm text-gray-900">
88
+ مرا به خاطر بسپار
89
+ </label>
90
+ </div>
91
+
92
+ <div className="text-sm">
93
+ <a href="/forgot-password" className="font-medium text-primary hover:text-primary-600">
94
+ فراموشی رمز عبور؟
95
+ </a>
96
+ </div>
97
+ </div>
98
+
99
+ <div>
100
+ <button
101
+ type="submit"
102
+ disabled={loading}
103
+ className={`group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-primary hover:bg-primary-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary ${loading ? 'opacity-70 cursor-not-allowed' : ''}`}
104
+ >
105
+ {loading ? 'در حال ورود...' : 'ورود'}
106
+ </button>
107
+ </div>
108
+ </form>
109
+
110
+ <div className="text-center text-sm">
111
+ <p className="text-gray-600">
112
+ حساب کاربری ندارید؟{' '}
113
+ <a href="/register" className="font-medium text-primary hover:text-primary-600">
114
+ ثبت‌نام کنید
115
+ </a>
116
+ </p>
117
+ </div>
118
+ </div>
119
+ </div>
120
+ );
121
+ }
client/src/pages/Register.jsx ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState } from 'react';
2
+ import { useNavigate } from 'react-router-dom';
3
+ import { useAuth } from '../context/AuthContext';
4
+ import { toast } from 'react-toastify';
5
+
6
+ export default function Register() {
7
+ const [formData, setFormData] = useState({
8
+ username: '',
9
+ email: '',
10
+ fullName: '',
11
+ password: '',
12
+ confirmPassword: ''
13
+ });
14
+ const [loading, setLoading] = useState(false);
15
+ const { register } = useAuth();
16
+ const navigate = useNavigate();
17
+
18
+ const handleChange = (e) => {
19
+ setFormData({
20
+ ...formData,
21
+ [e.target.name]: e.target.value
22
+ });
23
+ };
24
+
25
+ const handleSubmit = async (e) => {
26
+ e.preventDefault();
27
+
28
+ if (formData.password !== formData.confirmPassword) {
29
+ toast.error('رمز عبور و تکرار آن مطابقت ندارند');
30
+ return;
31
+ }
32
+
33
+ setLoading(true);
34
+
35
+ try {
36
+ await register({
37
+ username: formData.username,
38
+ email: formData.email,
39
+ password: formData.password,
40
+ fullName: formData.fullName
41
+ });
42
+ toast.success('ثبت‌نام با موفقیت انجام شد');
43
+ navigate('/dashboard');
44
+ } catch (err) {
45
+ toast.error(err.message || 'خطا در ثبت‌نام');
46
+ } finally {
47
+ setLoading(false);
48
+ }
49
+ };
50
+
51
+ return (
52
+ <div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
53
+ <div className="max-w-md w-full space-y-8 bg-white p-8 rounded-lg shadow-md">
54
+ <div className="text-center">
55
+ <h2 className="mt-6 text-3xl font-extrabold text-gray-900">ثبت‌نام در پایتون مستری</h2>
56
+ </div>
57
+ <form className="mt-8 space-y-6" onSubmit={handleSubmit}>
58
+ <div className="rounded-md shadow-sm space-y-4">
59
+ <div>
60
+ <label htmlFor="fullName" className="block text-sm font-medium text-gray-700 mb-1">
61
+ نام کامل
62
+ </label>
63
+ <input
64
+ id="fullName"
65
+ name="fullName"
66
+ type="text"
67
+ required
68
+ className="appearance-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-md focus:outline-none focus:ring-primary focus:border-primary focus:z-10 sm:text-sm"
69
+ placeholder="نام و نام خانوادگی"
70
+ value={formData.fullName}
71
+ onChange={handleChange}
72
+ />
73
+ </div>
74
+ <div>
75
+ <label htmlFor="username" className="block text-sm font-medium text-gray-700 mb-1">
76
+ نام کاربری
77
+ </label>
78
+ <input
79
+ id="username"
80
+ name="username"
81
+ type="text"
82
+ required
83
+ className="appearance-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-md focus:outline-none focus:ring-primary focus:border-primary focus:z-10 sm:text-sm"
84
+ placeholder="نام کاربری دلخواه"
85
+ value={formData.username}
86
+ onChange={handleChange}
87
+ />
88
+ </div>
89
+ <div>
90
+ <label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-1">
91
+ آدرس ایمیل
92
+ </label>
93
+ <input
94
+ id="email"
95
+ name="email"
96
+ type="email"
97
+ required
98
+ className="appearance-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-md focus:outline-none focus:ring-primary focus:border-primary focus:z-10 sm:text-sm"
99
+ placeholder="example@domain.com"
100
+ value={formData.email}
101
+ onChange={handleChange}
102
+ />
103
+ </div>
104
+ <div>
105
+ <label htmlFor="password" className="block text-sm font-medium text-gray-700 mb-1">
106
+ رمز عبور
107
+ </label>
108
+ <input
109
+ id="password"
110
+ name="password"
111
+ type="password"
112
+ required
113
+ className="appearance-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-md focus:outline-none focus:ring-primary focus:border-primary focus:z-10 sm:text-sm"
114
+ placeholder="••••••••"
115
+ value={formData.password}
116
+ onChange={handleChange}
117
+ />
118
+ </div>
119
+ <div>
120
+ <label htmlFor="confirmPassword" className="block text-sm font-medium text-gray-700 mb-1">
121
+ تکرار رمز عبور
122
+ </label>
123
+ <input
124
+ id="confirmPassword"
125
+ name="confirmPassword"
126
+ type="password"
127
+ required
128
+ className="appearance-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-md focus:outline-none focus:ring-primary focus:border-primary focus:z-10 sm:text-sm"
129
+ placeholder="••••••••"
130
+ value={formData.confirmPassword}
131
+ onChange={handleChange}
132
+ />
133
+ </div>
134
+ </div>
135
+
136
+ <div>
137
+ <button
138
+ type="submit"
139
+ disabled={loading}
140
+ className={`group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-primary hover:bg-primary-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary ${loading ? 'opacity-70 cursor-not-allowed' : ''}`}
141
+ >
142
+ {loading ? 'در حال ثبت‌نام...' : 'ثبت‌نام'}
143
+ </button>
144
+ </div>
145
+ </form>
146
+
147
+ <div className="text-center text-sm">
148
+ <p className="text-gray-600">
149
+ قبلا ثبت‌نام کرده‌اید؟{' '}
150
+ <a href="/login" className="font-medium text-primary hover:text-primary-600">
151
+ ورود به حساب کاربری
152
+ </a>
153
+ </p>
154
+ </div>
155
+ </div>
156
+ </div>
157
+ );
158
+ }
server/routes/auth.js CHANGED
@@ -3,25 +3,70 @@ const jwt = require('jsonwebtoken');
3
  const bcrypt = require('bcryptjs');
4
  const User = require('../models/User');
5
  const router = express.Router();
6
-
7
  // Register
8
  router.post('/register', async (req, res) => {
9
  try {
10
- const { username, email, password } = req.body;
11
 
12
- let user = await User.findOne({ email });
13
- if (user) return res.status(400).json({ message: 'User already exists' });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
- user = new User({ username, email, password });
16
  await user.save();
17
 
 
18
  const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, {
19
  expiresIn: '30d'
20
  });
21
 
22
- res.status(201).json({ token, user: { id: user._id, username, email, role: user.role } });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  } catch (err) {
24
- res.status(500).json({ message: 'Server error' });
 
 
 
 
25
  }
26
  });
27
 
@@ -29,28 +74,130 @@ router.post('/register', async (req, res) => {
29
  router.post('/login', async (req, res) => {
30
  try {
31
  const { email, password } = req.body;
32
- const user = await User.findOne({ email });
33
 
34
- if (!user || !(await user.comparePassword(password))) {
35
- return res.status(400).json({ message: 'Invalid credentials' });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  }
37
 
 
38
  const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, {
39
  expiresIn: '30d'
40
  });
41
 
 
 
 
 
 
 
 
 
42
  res.json({
 
43
  token,
44
  user: {
45
  id: user._id,
46
  username: user.username,
47
  email: user.email,
48
- role: user.role
 
49
  }
50
  });
51
  } catch (err) {
52
- res.status(500).json({ message: 'Server error' });
 
 
 
 
53
  }
54
  });
55
 
56
- module.exports = router;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  const bcrypt = require('bcryptjs');
4
  const User = require('../models/User');
5
  const router = express.Router();
 
6
  // Register
7
  router.post('/register', async (req, res) => {
8
  try {
9
+ const { username, email, password, fullName } = req.body;
10
 
11
+ // Validate input
12
+ if (!username || !email || !password || !fullName) {
13
+ return res.status(400).json({ message: 'همه فیلدهای ضروری را پر کنید' });
14
+ }
15
+
16
+ // Validate email format
17
+ const emailRegex = /\S+@\S+\.\S+/;
18
+ if (!emailRegex.test(email)) {
19
+ return res.status(400).json({ message: 'فرمت ایمیل نامعتبر است' });
20
+ }
21
+
22
+ // Check if user exists
23
+ let user = await User.findOne({ $or: [{ email }, { username }] });
24
+ if (user) {
25
+ return res.status(400).json({
26
+ message: 'کاربر با این ایمیل یا نام کاربری وجود دارد'
27
+ });
28
+ }
29
+
30
+ // Create new user
31
+ user = new User({
32
+ username,
33
+ email,
34
+ password,
35
+ profile: { fullName }
36
+ });
37
 
 
38
  await user.save();
39
 
40
+ // Generate JWT token
41
  const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, {
42
  expiresIn: '30d'
43
  });
44
 
45
+ // Set cookie
46
+ res.cookie('token', token, {
47
+ httpOnly: true,
48
+ secure: process.env.NODE_ENV === 'production',
49
+ maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days
50
+ sameSite: 'strict'
51
+ });
52
+
53
+ res.status(201).json({
54
+ success: true,
55
+ token,
56
+ user: {
57
+ id: user._id,
58
+ username,
59
+ email,
60
+ role: user.role,
61
+ fullName
62
+ }
63
+ });
64
  } catch (err) {
65
+ console.error('Registration error:', err);
66
+ res.status(500).json({
67
+ success: false,
68
+ message: 'خطای سرور. لطفا دوباره تلاش کنید'
69
+ });
70
  }
71
  });
72
 
 
74
  router.post('/login', async (req, res) => {
75
  try {
76
  const { email, password } = req.body;
 
77
 
78
+ // Validate input
79
+ if (!email || !password) {
80
+ return res.status(400).json({ message: 'ایمیل و رمز عبور را وارد کنید' });
81
+ }
82
+
83
+ // Find user
84
+ const user = await User.findOne({ email });
85
+ if (!user) {
86
+ return res.status(401).json({
87
+ success: false,
88
+ message: 'ایمیل یا رمز عبور اشتباه است'
89
+ });
90
+ }
91
+
92
+ // Check password
93
+ const isMatch = await user.comparePassword(password);
94
+ if (!isMatch) {
95
+ return res.status(401).json({
96
+ success: false,
97
+ message: 'ایمیل یا رمز عبور اشتباه است'
98
+ });
99
  }
100
 
101
+ // Generate JWT token
102
  const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, {
103
  expiresIn: '30d'
104
  });
105
 
106
+ // Set cookie
107
+ res.cookie('token', token, {
108
+ httpOnly: true,
109
+ secure: process.env.NODE_ENV === 'production',
110
+ maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days
111
+ sameSite: 'strict'
112
+ });
113
+
114
  res.json({
115
+ success: true,
116
  token,
117
  user: {
118
  id: user._id,
119
  username: user.username,
120
  email: user.email,
121
+ role: user.role,
122
+ fullName: user.profile?.fullName || ''
123
  }
124
  });
125
  } catch (err) {
126
+ console.error('Login error:', err);
127
+ res.status(500).json({
128
+ success: false,
129
+ message: 'خطای سرور. لطفا دوباره تلاش کنید'
130
+ });
131
  }
132
  });
133
 
134
+ // Get current user
135
+ router.get('/me', async (req, res) => {
136
+ try {
137
+ const token = req.cookies.token || req.headers.authorization?.split(' ')[1];
138
+ if (!token) {
139
+ return res.status(401).json({
140
+ success: false,
141
+ message: 'Not authorized'
142
+ });
143
+ }
144
+
145
+ const decoded = jwt.verify(token, process.env.JWT_SECRET);
146
+ const user = await User.findById(decoded.id).select('-password');
147
+
148
+ if (!user) {
149
+ return res.status(404).json({
150
+ success: false,
151
+ message: 'User not found'
152
+ });
153
+ }
154
+
155
+ res.json({
156
+ success: true,
157
+ user
158
+ });
159
+ } catch (err) {
160
+ console.error('Auth error:', err);
161
+ res.status(401).json({
162
+ success: false,
163
+ message: 'Not authorized'
164
+ });
165
+ }
166
+ });
167
+
168
+ // Logout
169
+ router.get('/logout', (req, res) => {
170
+ res.clearCookie('token');
171
+ res.json({
172
+ success: true,
173
+ message: 'Logged out successfully'
174
+ });
175
+ });
176
+ // Auth middleware
177
+ function auth(req, res, next) {
178
+ const token = req.cookies.token || req.headers.authorization?.split(' ')[1];
179
+
180
+ if (!token) {
181
+ return res.status(401).json({
182
+ success: false,
183
+ message: 'Not authorized'
184
+ });
185
+ }
186
+
187
+ try {
188
+ const decoded = jwt.verify(token, process.env.JWT_SECRET);
189
+ req.user = decoded;
190
+ next();
191
+ } catch (err) {
192
+ console.error('Auth error:', err);
193
+ res.status(401).json({
194
+ success: false,
195
+ message: 'Not authorized'
196
+ });
197
+ }
198
+ }
199
+
200
+ module.exports = {
201
+ router,
202
+ auth
203
+ };
server/server.js CHANGED
@@ -20,7 +20,7 @@ mongoose.connect(process.env.MONGODB_URI, {
20
  .catch(err => console.error('MongoDB connection error:', err));
21
 
22
  // Routes
23
- const authRoutes = require('./routes/auth');
24
  const courseRoutes = require('./routes/courses');
25
  const userRoutes = require('./routes/users');
26
 
 
20
  .catch(err => console.error('MongoDB connection error:', err));
21
 
22
  // Routes
23
+ const { router: authRoutes, auth } = require('./routes/auth');
24
  const courseRoutes = require('./routes/courses');
25
  const userRoutes = require('./routes/users');
26