baraqex 1.0.70 → 1.0.72
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.
- package/dist/index.js +5 -1
- package/dist/index.js.map +3 -3
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +5 -1
- package/dist/server/index.js.map +3 -3
- package/package.json +1 -1
- package/templates/fullstack-template/package-lock.json +338 -5
- package/templates/fullstack-template/package.json +3 -2
- package/templates/fullstack-template/server.js +15 -1
- package/templates/fullstack-template/src/server.ts +5 -5
- package/templates/fullstack-template/vite.config.ts +56 -0
- package/templates/ssr-template/src/App.tsx +29 -20
- package/templates/fullstack-template/src/App.tsx +0 -271
- package/templates/fullstack-template/src/api/auth/login.ts +0 -75
- package/templates/fullstack-template/src/api/auth/me.ts +0 -66
- package/templates/fullstack-template/src/api/auth/register.ts +0 -59
- package/templates/fullstack-template/src/api/hello.js +0 -19
- package/templates/fullstack-template/src/api/todos/[id].ts +0 -108
- package/templates/fullstack-template/src/api/todos/index.ts +0 -79
- package/templates/fullstack-template/src/api/users.js +0 -39
- package/templates/fullstack-template/src/components/Dashboard.tsx +0 -129
- package/templates/fullstack-template/src/components/Header.tsx +0 -76
- package/templates/fullstack-template/src/contexts/ApiContext.tsx +0 -69
- package/templates/fullstack-template/src/contexts/AuthContext.tsx +0 -155
- package/templates/fullstack-template/src/pages/index.tsx +0 -200
- package/templates/fullstack-template/src/styles/App.css +0 -446
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { sendSuccess, sendError } from 'baraqex';
|
|
2
|
-
|
|
3
|
-
// Import the todos array from the index file
|
|
4
|
-
// In a real app, this would be in a database
|
|
5
|
-
let todos = [
|
|
6
|
-
{ id: 1, text: 'Learn Baraqex Framework', completed: false, userId: 1, createdAt: new Date().toISOString() },
|
|
7
|
-
{ id: 2, text: 'Build awesome fullstack app', completed: false, userId: 1, createdAt: new Date().toISOString() },
|
|
8
|
-
{ id: 3, text: 'Deploy to production', completed: false, userId: 1, createdAt: new Date().toISOString() }
|
|
9
|
-
];
|
|
10
|
-
|
|
11
|
-
// Require authentication
|
|
12
|
-
export const middleware = [
|
|
13
|
-
(req: any, res: any, next: any) => {
|
|
14
|
-
if (!req.user) {
|
|
15
|
-
return sendError(res, 'Authentication required', 401);
|
|
16
|
-
}
|
|
17
|
-
next();
|
|
18
|
-
}
|
|
19
|
-
];
|
|
20
|
-
|
|
21
|
-
// Get specific todo
|
|
22
|
-
export async function get(req: any, res: any) {
|
|
23
|
-
try {
|
|
24
|
-
const todoId = parseInt(req.params.id);
|
|
25
|
-
const userId = req.user.id;
|
|
26
|
-
|
|
27
|
-
if (isNaN(todoId)) {
|
|
28
|
-
return sendError(res, 'Invalid todo ID', 400);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const todo = todos.find(t => t.id === todoId && t.userId === userId);
|
|
32
|
-
if (!todo) {
|
|
33
|
-
return sendError(res, 'Todo not found', 404);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return sendSuccess(res, { todo }, 'Todo retrieved successfully');
|
|
37
|
-
|
|
38
|
-
} catch (error: any) {
|
|
39
|
-
console.error('Get todo error:', error);
|
|
40
|
-
return sendError(res, 'Failed to get todo', 500);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Update todo
|
|
45
|
-
export async function put(req: any, res: any) {
|
|
46
|
-
try {
|
|
47
|
-
const todoId = parseInt(req.params.id);
|
|
48
|
-
const userId = req.user.id;
|
|
49
|
-
const { text, completed } = req.body;
|
|
50
|
-
|
|
51
|
-
if (isNaN(todoId)) {
|
|
52
|
-
return sendError(res, 'Invalid todo ID', 400);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const todoIndex = todos.findIndex(t => t.id === todoId && t.userId === userId);
|
|
56
|
-
if (todoIndex === -1) {
|
|
57
|
-
return sendError(res, 'Todo not found', 404);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Update todo
|
|
61
|
-
if (text !== undefined) {
|
|
62
|
-
if (text.trim().length === 0) {
|
|
63
|
-
return sendError(res, 'Todo text cannot be empty', 400);
|
|
64
|
-
}
|
|
65
|
-
todos[todoIndex].text = text.trim();
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (completed !== undefined) {
|
|
69
|
-
todos[todoIndex].completed = Boolean(completed);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
todos[todoIndex].updatedAt = new Date().toISOString();
|
|
73
|
-
|
|
74
|
-
return sendSuccess(res, { todo: todos[todoIndex] }, 'Todo updated successfully');
|
|
75
|
-
|
|
76
|
-
} catch (error: any) {
|
|
77
|
-
console.error('Update todo error:', error);
|
|
78
|
-
return sendError(res, 'Failed to update todo', 500);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Delete todo
|
|
83
|
-
export async function del(req: any, res: any) {
|
|
84
|
-
try {
|
|
85
|
-
const todoId = parseInt(req.params.id);
|
|
86
|
-
const userId = req.user.id;
|
|
87
|
-
|
|
88
|
-
if (isNaN(todoId)) {
|
|
89
|
-
return sendError(res, 'Invalid todo ID', 400);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const todoIndex = todos.findIndex(t => t.id === todoId && t.userId === userId);
|
|
93
|
-
if (todoIndex === -1) {
|
|
94
|
-
return sendError(res, 'Todo not found', 404);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const deletedTodo = todos.splice(todoIndex, 1)[0];
|
|
98
|
-
|
|
99
|
-
return sendSuccess(res, { todo: deletedTodo }, 'Todo deleted successfully');
|
|
100
|
-
|
|
101
|
-
} catch (error: any) {
|
|
102
|
-
console.error('Delete todo error:', error);
|
|
103
|
-
return sendError(res, 'Failed to delete todo', 500);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Export delete function with correct name
|
|
108
|
-
export { del as delete };
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { sendSuccess, sendError, validateFields, getPagination } from 'baraqex';
|
|
2
|
-
|
|
3
|
-
// In-memory storage for demo (use database in real app)
|
|
4
|
-
let todos = [
|
|
5
|
-
{ id: 1, text: 'Learn Baraqex Framework', completed: false, userId: 1, createdAt: new Date().toISOString() },
|
|
6
|
-
{ id: 2, text: 'Build awesome fullstack app', completed: false, userId: 1, createdAt: new Date().toISOString() },
|
|
7
|
-
{ id: 3, text: 'Deploy to production', completed: false, userId: 1, createdAt: new Date().toISOString() }
|
|
8
|
-
];
|
|
9
|
-
|
|
10
|
-
// Require authentication for all todo endpoints
|
|
11
|
-
export const middleware = [
|
|
12
|
-
(req: any, res: any, next: any) => {
|
|
13
|
-
if (!req.user) {
|
|
14
|
-
return sendError(res, 'Authentication required', 401);
|
|
15
|
-
}
|
|
16
|
-
next();
|
|
17
|
-
}
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
// Get todos for current user
|
|
21
|
-
export async function get(req: any, res: any) {
|
|
22
|
-
try {
|
|
23
|
-
const userId = req.user.id;
|
|
24
|
-
const { page, limit, skip } = getPagination(req);
|
|
25
|
-
|
|
26
|
-
// Filter todos for current user
|
|
27
|
-
const userTodos = todos.filter(todo => todo.userId === userId);
|
|
28
|
-
|
|
29
|
-
// Apply pagination
|
|
30
|
-
const paginatedTodos = userTodos.slice(skip, skip + limit);
|
|
31
|
-
|
|
32
|
-
return sendSuccess(res, {
|
|
33
|
-
todos: paginatedTodos,
|
|
34
|
-
pagination: {
|
|
35
|
-
page,
|
|
36
|
-
limit,
|
|
37
|
-
total: userTodos.length,
|
|
38
|
-
pages: Math.ceil(userTodos.length / limit)
|
|
39
|
-
}
|
|
40
|
-
}, 'Todos retrieved successfully');
|
|
41
|
-
|
|
42
|
-
} catch (error: any) {
|
|
43
|
-
console.error('Get todos error:', error);
|
|
44
|
-
return sendError(res, 'Failed to get todos', 500);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Create new todo
|
|
49
|
-
export async function post(req: any, res: any) {
|
|
50
|
-
try {
|
|
51
|
-
const validation = validateFields(req, ['text']);
|
|
52
|
-
if (!validation.valid) {
|
|
53
|
-
return sendError(res, 'Missing required fields', 400, { missing: validation.missing });
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const { text } = req.body;
|
|
57
|
-
const userId = req.user.id;
|
|
58
|
-
|
|
59
|
-
if (text.trim().length === 0) {
|
|
60
|
-
return sendError(res, 'Todo text cannot be empty', 400);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const newTodo = {
|
|
64
|
-
id: Date.now(),
|
|
65
|
-
text: text.trim(),
|
|
66
|
-
completed: false,
|
|
67
|
-
userId,
|
|
68
|
-
createdAt: new Date().toISOString()
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
todos.push(newTodo);
|
|
72
|
-
|
|
73
|
-
return sendSuccess(res, { todo: newTodo }, 'Todo created successfully');
|
|
74
|
-
|
|
75
|
-
} catch (error: any) {
|
|
76
|
-
console.error('Create todo error:', error);
|
|
77
|
-
return sendError(res, 'Failed to create todo', 500);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
// In-memory user storage for demo
|
|
2
|
-
let users = [
|
|
3
|
-
{ id: 1, name: 'John Doe', email: 'john@example.com' },
|
|
4
|
-
{ id: 2, name: 'Jane Smith', email: 'jane@example.com' }
|
|
5
|
-
];
|
|
6
|
-
|
|
7
|
-
// GET /api/users
|
|
8
|
-
export async function get(req, res) {
|
|
9
|
-
res.json({
|
|
10
|
-
success: true,
|
|
11
|
-
users,
|
|
12
|
-
count: users.length
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// POST /api/users
|
|
17
|
-
export async function post(req, res) {
|
|
18
|
-
const { name, email } = req.body;
|
|
19
|
-
|
|
20
|
-
if (!name || !email) {
|
|
21
|
-
return res.status(400).json({
|
|
22
|
-
success: false,
|
|
23
|
-
error: 'Name and email are required'
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const newUser = {
|
|
28
|
-
id: users.length + 1,
|
|
29
|
-
name,
|
|
30
|
-
email
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
users.push(newUser);
|
|
34
|
-
|
|
35
|
-
res.status(201).json({
|
|
36
|
-
success: true,
|
|
37
|
-
user: newUser
|
|
38
|
-
});
|
|
39
|
-
}
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { jsx, useState, useEffect } from 'frontend-hamroun';
|
|
2
|
-
import { useAuth } from '../contexts/AuthContext';
|
|
3
|
-
import { useApi } from '../contexts/ApiContext';
|
|
4
|
-
|
|
5
|
-
export function Dashboard() {
|
|
6
|
-
const { user } = useAuth();
|
|
7
|
-
const api = useApi();
|
|
8
|
-
const [stats, setStats] = useState({
|
|
9
|
-
totalTodos: 0,
|
|
10
|
-
completedTodos: 0,
|
|
11
|
-
pendingTodos: 0
|
|
12
|
-
});
|
|
13
|
-
const [loading, setLoading] = useState(true);
|
|
14
|
-
|
|
15
|
-
useEffect(() => {
|
|
16
|
-
loadDashboardData();
|
|
17
|
-
}, []);
|
|
18
|
-
|
|
19
|
-
const loadDashboardData = async () => {
|
|
20
|
-
try {
|
|
21
|
-
const response = await api.get('/api/todos');
|
|
22
|
-
const todos = response.data.todos;
|
|
23
|
-
|
|
24
|
-
setStats({
|
|
25
|
-
totalTodos: todos.length,
|
|
26
|
-
completedTodos: todos.filter((todo: any) => todo.completed).length,
|
|
27
|
-
pendingTodos: todos.filter((todo: any) => !todo.completed).length
|
|
28
|
-
});
|
|
29
|
-
} catch (error) {
|
|
30
|
-
console.error('Failed to load dashboard data:', error);
|
|
31
|
-
} finally {
|
|
32
|
-
setLoading(false);
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
if (loading) {
|
|
37
|
-
return (
|
|
38
|
-
<div className="dashboard">
|
|
39
|
-
<div className="dashboard-loading">
|
|
40
|
-
<div className="spinner"></div>
|
|
41
|
-
<p>Loading dashboard...</p>
|
|
42
|
-
</div>
|
|
43
|
-
</div>
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<div className="dashboard">
|
|
49
|
-
<div className="dashboard-header">
|
|
50
|
-
<h2>Welcome back, {user?.name || user?.username}! 👋</h2>
|
|
51
|
-
<p className="dashboard-subtitle">Here's your productivity overview</p>
|
|
52
|
-
</div>
|
|
53
|
-
|
|
54
|
-
<div className="stats-grid">
|
|
55
|
-
<div className="stat-card">
|
|
56
|
-
<div className="stat-icon">📝</div>
|
|
57
|
-
<div className="stat-content">
|
|
58
|
-
<h3 className="stat-number">{stats.totalTodos}</h3>
|
|
59
|
-
<p className="stat-label">Total Todos</p>
|
|
60
|
-
</div>
|
|
61
|
-
</div>
|
|
62
|
-
|
|
63
|
-
<div className="stat-card">
|
|
64
|
-
<div className="stat-icon">✅</div>
|
|
65
|
-
<div className="stat-content">
|
|
66
|
-
<h3 className="stat-number">{stats.completedTodos}</h3>
|
|
67
|
-
<p className="stat-label">Completed</p>
|
|
68
|
-
</div>
|
|
69
|
-
</div>
|
|
70
|
-
|
|
71
|
-
<div className="stat-card">
|
|
72
|
-
<div className="stat-icon">⏳</div>
|
|
73
|
-
<div className="stat-content">
|
|
74
|
-
<h3 className="stat-number">{stats.pendingTodos}</h3>
|
|
75
|
-
<p className="stat-label">Pending</p>
|
|
76
|
-
</div>
|
|
77
|
-
</div>
|
|
78
|
-
|
|
79
|
-
<div className="stat-card">
|
|
80
|
-
<div className="stat-icon">📈</div>
|
|
81
|
-
<div className="stat-content">
|
|
82
|
-
<h3 className="stat-number">
|
|
83
|
-
{stats.totalTodos > 0 ? Math.round((stats.completedTodos / stats.totalTodos) * 100) : 0}%
|
|
84
|
-
</h3>
|
|
85
|
-
<p className="stat-label">Completion Rate</p>
|
|
86
|
-
</div>
|
|
87
|
-
</div>
|
|
88
|
-
</div>
|
|
89
|
-
|
|
90
|
-
<div className="dashboard-actions">
|
|
91
|
-
<div className="action-card">
|
|
92
|
-
<h3>🚀 Quick Actions</h3>
|
|
93
|
-
<div className="action-buttons">
|
|
94
|
-
<button className="action-btn primary">
|
|
95
|
-
➕ Add New Todo
|
|
96
|
-
</button>
|
|
97
|
-
<button className="action-btn secondary">
|
|
98
|
-
📊 View Reports
|
|
99
|
-
</button>
|
|
100
|
-
<button className="action-btn secondary">
|
|
101
|
-
⚙️ Settings
|
|
102
|
-
</button>
|
|
103
|
-
</div>
|
|
104
|
-
</div>
|
|
105
|
-
|
|
106
|
-
<div className="recent-activity">
|
|
107
|
-
<h3>📅 Recent Activity</h3>
|
|
108
|
-
<div className="activity-list">
|
|
109
|
-
<div className="activity-item">
|
|
110
|
-
<span className="activity-icon">✅</span>
|
|
111
|
-
<span className="activity-text">Completed todo: "Learn Baraqex"</span>
|
|
112
|
-
<span className="activity-time">2 hours ago</span>
|
|
113
|
-
</div>
|
|
114
|
-
<div className="activity-item">
|
|
115
|
-
<span className="activity-icon">➕</span>
|
|
116
|
-
<span className="activity-text">Added new todo</span>
|
|
117
|
-
<span className="activity-time">1 day ago</span>
|
|
118
|
-
</div>
|
|
119
|
-
<div className="activity-item">
|
|
120
|
-
<span className="activity-icon">👤</span>
|
|
121
|
-
<span className="activity-text">Updated profile</span>
|
|
122
|
-
<span className="activity-time">3 days ago</span>
|
|
123
|
-
</div>
|
|
124
|
-
</div>
|
|
125
|
-
</div>
|
|
126
|
-
</div>
|
|
127
|
-
</div>
|
|
128
|
-
);
|
|
129
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { jsx } from 'frontend-hamroun';
|
|
2
|
-
import { useAuth } from '../contexts/AuthContext';
|
|
3
|
-
|
|
4
|
-
interface User {
|
|
5
|
-
id: number;
|
|
6
|
-
username: string;
|
|
7
|
-
name: string;
|
|
8
|
-
roles: string[];
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
interface HeaderProps {
|
|
12
|
-
user: User | null;
|
|
13
|
-
onNavigate: (view: string) => void;
|
|
14
|
-
currentView: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function Header({ user, onNavigate, currentView }: HeaderProps) {
|
|
18
|
-
const { logout } = useAuth();
|
|
19
|
-
|
|
20
|
-
const handleLogout = () => {
|
|
21
|
-
logout();
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
return (
|
|
25
|
-
<header className="app-header">
|
|
26
|
-
<div className="header-content">
|
|
27
|
-
<div className="header-left">
|
|
28
|
-
<h1 className="app-title">
|
|
29
|
-
<span className="logo">⚡</span>
|
|
30
|
-
Fullstack App
|
|
31
|
-
</h1>
|
|
32
|
-
</div>
|
|
33
|
-
|
|
34
|
-
{user && (
|
|
35
|
-
<nav className="header-nav">
|
|
36
|
-
<button
|
|
37
|
-
className={`nav-button ${currentView === 'dashboard' ? 'active' : ''}`}
|
|
38
|
-
onClick={() => onNavigate('dashboard')}
|
|
39
|
-
>
|
|
40
|
-
📊 Dashboard
|
|
41
|
-
</button>
|
|
42
|
-
<button
|
|
43
|
-
className={`nav-button ${currentView === 'todos' ? 'active' : ''}`}
|
|
44
|
-
onClick={() => onNavigate('todos')}
|
|
45
|
-
>
|
|
46
|
-
✅ Todos
|
|
47
|
-
</button>
|
|
48
|
-
<button
|
|
49
|
-
className={`nav-button ${currentView === 'profile' ? 'active' : ''}`}
|
|
50
|
-
onClick={() => onNavigate('profile')}
|
|
51
|
-
>
|
|
52
|
-
👤 Profile
|
|
53
|
-
</button>
|
|
54
|
-
</nav>
|
|
55
|
-
)}
|
|
56
|
-
|
|
57
|
-
<div className="header-right">
|
|
58
|
-
{user ? (
|
|
59
|
-
<div className="user-menu">
|
|
60
|
-
<span className="user-greeting">
|
|
61
|
-
Hello, {user.name || user.username}
|
|
62
|
-
</span>
|
|
63
|
-
<button className="logout-button" onClick={handleLogout}>
|
|
64
|
-
🚪 Logout
|
|
65
|
-
</button>
|
|
66
|
-
</div>
|
|
67
|
-
) : (
|
|
68
|
-
<div className="auth-status">
|
|
69
|
-
Please log in to continue
|
|
70
|
-
</div>
|
|
71
|
-
)}
|
|
72
|
-
</div>
|
|
73
|
-
</div>
|
|
74
|
-
</header>
|
|
75
|
-
);
|
|
76
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { jsx, createContext, useContext } from 'frontend-hamroun';
|
|
2
|
-
|
|
3
|
-
interface ApiContextType {
|
|
4
|
-
request: (url: string, options?: RequestInit) => Promise<any>;
|
|
5
|
-
get: (url: string) => Promise<any>;
|
|
6
|
-
post: (url: string, data?: any) => Promise<any>;
|
|
7
|
-
put: (url: string, data?: any) => Promise<any>;
|
|
8
|
-
delete: (url: string) => Promise<any>;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const ApiContext = createContext<ApiContextType | undefined>(undefined);
|
|
12
|
-
|
|
13
|
-
export function ApiProvider({ children }: { children: any }) {
|
|
14
|
-
const request = async (url: string, options: RequestInit = {}) => {
|
|
15
|
-
const token = localStorage.getItem('auth_token');
|
|
16
|
-
|
|
17
|
-
const config: RequestInit = {
|
|
18
|
-
...options,
|
|
19
|
-
headers: {
|
|
20
|
-
'Content-Type': 'application/json',
|
|
21
|
-
...options.headers,
|
|
22
|
-
...(token && { 'Authorization': `Bearer ${token}` })
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const response = await fetch(url, config);
|
|
27
|
-
const data = await response.json();
|
|
28
|
-
|
|
29
|
-
if (!response.ok) {
|
|
30
|
-
throw new Error(data.message || `HTTP error! status: ${response.status}`);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return data;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const get = (url: string) => request(url, { method: 'GET' });
|
|
37
|
-
|
|
38
|
-
const post = (url: string, data?: any) => request(url, {
|
|
39
|
-
method: 'POST',
|
|
40
|
-
body: data ? JSON.stringify(data) : undefined
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
const put = (url: string, data?: any) => request(url, {
|
|
44
|
-
method: 'PUT',
|
|
45
|
-
body: data ? JSON.stringify(data) : undefined
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
const del = (url: string) => request(url, { method: 'DELETE' });
|
|
49
|
-
|
|
50
|
-
return (
|
|
51
|
-
<ApiContext.Provider value={{
|
|
52
|
-
request,
|
|
53
|
-
get,
|
|
54
|
-
post,
|
|
55
|
-
put,
|
|
56
|
-
delete: del
|
|
57
|
-
}}>
|
|
58
|
-
{children}
|
|
59
|
-
</ApiContext.Provider>
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function useApi() {
|
|
64
|
-
const context = useContext(ApiContext);
|
|
65
|
-
if (context === undefined) {
|
|
66
|
-
throw new Error('useApi must be used within an ApiProvider');
|
|
67
|
-
}
|
|
68
|
-
return context;
|
|
69
|
-
}
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import { jsx, createContext, useContext, useState, useEffect } from 'frontend-hamroun';
|
|
2
|
-
|
|
3
|
-
interface User {
|
|
4
|
-
id: number;
|
|
5
|
-
username: string;
|
|
6
|
-
email: string;
|
|
7
|
-
name: string;
|
|
8
|
-
roles: string[];
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
interface AuthContextType {
|
|
12
|
-
user: User | null;
|
|
13
|
-
login: (credentials: LoginCredentials) => Promise<void>;
|
|
14
|
-
register: (userData: RegisterData) => Promise<void>;
|
|
15
|
-
logout: () => void;
|
|
16
|
-
loading: boolean;
|
|
17
|
-
error: string | null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
interface LoginCredentials {
|
|
21
|
-
username: string;
|
|
22
|
-
password: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface RegisterData {
|
|
26
|
-
username: string;
|
|
27
|
-
password: string;
|
|
28
|
-
email: string;
|
|
29
|
-
name: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
|
33
|
-
|
|
34
|
-
export function AuthProvider({ children }: { children: any }) {
|
|
35
|
-
const [user, setUser] = useState<User | null>(null);
|
|
36
|
-
const [loading, setLoading] = useState(true);
|
|
37
|
-
const [error, setError] = useState<string | null>(null);
|
|
38
|
-
|
|
39
|
-
// Check for existing token on mount
|
|
40
|
-
useEffect(() => {
|
|
41
|
-
const token = localStorage.getItem('auth_token');
|
|
42
|
-
if (token) {
|
|
43
|
-
validateToken(token);
|
|
44
|
-
} else {
|
|
45
|
-
setLoading(false);
|
|
46
|
-
}
|
|
47
|
-
}, []);
|
|
48
|
-
|
|
49
|
-
const validateToken = async (token: string) => {
|
|
50
|
-
try {
|
|
51
|
-
const response = await fetch('/api/auth/me', {
|
|
52
|
-
headers: {
|
|
53
|
-
'Authorization': `Bearer ${token}`
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
if (response.ok) {
|
|
58
|
-
const data = await response.json();
|
|
59
|
-
setUser(data.data.user);
|
|
60
|
-
} else {
|
|
61
|
-
localStorage.removeItem('auth_token');
|
|
62
|
-
}
|
|
63
|
-
} catch (error) {
|
|
64
|
-
console.error('Token validation failed:', error);
|
|
65
|
-
localStorage.removeItem('auth_token');
|
|
66
|
-
} finally {
|
|
67
|
-
setLoading(false);
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const login = async (credentials: LoginCredentials) => {
|
|
72
|
-
try {
|
|
73
|
-
setLoading(true);
|
|
74
|
-
setError(null);
|
|
75
|
-
|
|
76
|
-
const response = await fetch('/api/auth/login', {
|
|
77
|
-
method: 'POST',
|
|
78
|
-
headers: {
|
|
79
|
-
'Content-Type': 'application/json'
|
|
80
|
-
},
|
|
81
|
-
body: JSON.stringify(credentials)
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
const data = await response.json();
|
|
85
|
-
|
|
86
|
-
if (response.ok) {
|
|
87
|
-
localStorage.setItem('auth_token', data.data.token);
|
|
88
|
-
setUser(data.data.user);
|
|
89
|
-
} else {
|
|
90
|
-
throw new Error(data.message || 'Login failed');
|
|
91
|
-
}
|
|
92
|
-
} catch (error: any) {
|
|
93
|
-
setError(error.message);
|
|
94
|
-
throw error;
|
|
95
|
-
} finally {
|
|
96
|
-
setLoading(false);
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const register = async (userData: RegisterData) => {
|
|
101
|
-
try {
|
|
102
|
-
setLoading(true);
|
|
103
|
-
setError(null);
|
|
104
|
-
|
|
105
|
-
const response = await fetch('/api/auth/register', {
|
|
106
|
-
method: 'POST',
|
|
107
|
-
headers: {
|
|
108
|
-
'Content-Type': 'application/json'
|
|
109
|
-
},
|
|
110
|
-
body: JSON.stringify(userData)
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
const data = await response.json();
|
|
114
|
-
|
|
115
|
-
if (response.ok) {
|
|
116
|
-
localStorage.setItem('auth_token', data.data.token);
|
|
117
|
-
setUser(data.data.user);
|
|
118
|
-
} else {
|
|
119
|
-
throw new Error(data.message || 'Registration failed');
|
|
120
|
-
}
|
|
121
|
-
} catch (error: any) {
|
|
122
|
-
setError(error.message);
|
|
123
|
-
throw error;
|
|
124
|
-
} finally {
|
|
125
|
-
setLoading(false);
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
const logout = () => {
|
|
130
|
-
localStorage.removeItem('auth_token');
|
|
131
|
-
setUser(null);
|
|
132
|
-
setError(null);
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
return (
|
|
136
|
-
<AuthContext.Provider value={{
|
|
137
|
-
user,
|
|
138
|
-
login,
|
|
139
|
-
register,
|
|
140
|
-
logout,
|
|
141
|
-
loading,
|
|
142
|
-
error
|
|
143
|
-
}}>
|
|
144
|
-
{children}
|
|
145
|
-
</AuthContext.Provider>
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export function useAuth() {
|
|
150
|
-
const context = useContext(AuthContext);
|
|
151
|
-
if (context === undefined) {
|
|
152
|
-
throw new Error('useAuth must be used within an AuthProvider');
|
|
153
|
-
}
|
|
154
|
-
return context;
|
|
155
|
-
}
|