baraqex 1.0.64 → 1.0.66
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/bin/baraqex.js +1013 -1126
- package/dist/api/hello.d.ts +3 -0
- package/dist/api/hello.d.ts.map +1 -0
- package/dist/api/users/[id].d.ts +5 -0
- package/dist/api/users/[id].d.ts.map +1 -0
- package/dist/browser.d.ts +40 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +407 -1
- package/dist/browser.js.map +4 -4
- package/dist/browser.package.json +4 -0
- package/dist/forms/index.d.ts +2 -0
- package/dist/forms/index.d.ts.map +1 -0
- package/dist/forms/types.d.ts +2 -0
- package/dist/forms/types.d.ts.map +1 -0
- package/dist/frontend/components/WasmLoader.d.ts +2 -0
- package/dist/frontend/components/WasmLoader.d.ts.map +1 -0
- package/dist/frontend/hooks/useWasm.d.ts +2 -0
- package/dist/frontend/hooks/useWasm.d.ts.map +1 -0
- package/dist/frontend/index.d.ts +2 -0
- package/dist/frontend/index.d.ts.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +678 -317
- package/dist/index.js.map +4 -4
- package/dist/renderComponent.d.ts +9 -0
- package/dist/renderComponent.d.ts.map +1 -0
- package/dist/router/index.d.ts +2 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/types.d.ts +2 -0
- package/dist/router/types.d.ts.map +1 -0
- package/dist/server/api-router.d.ts +16 -0
- package/dist/server/api-router.d.ts.map +1 -0
- package/dist/server/auth.d.ts +25 -0
- package/dist/server/auth.d.ts.map +1 -0
- package/dist/server/database.d.ts +22 -0
- package/dist/server/database.d.ts.map +1 -0
- package/dist/server/index.d.ts +116 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/{server.js → server/index.js} +120 -23
- package/dist/server/index.js.map +7 -0
- package/dist/server/middleware.d.ts +11 -0
- package/dist/server/middleware.d.ts.map +1 -0
- package/dist/server/package.json +4 -0
- package/dist/server/templates.d.ts +31 -0
- package/dist/server/templates.d.ts.map +1 -0
- package/dist/server/types.d.ts +69 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/utils.d.ts +70 -0
- package/dist/server/utils.d.ts.map +1 -0
- package/dist/server/wasm.d.ts +29 -0
- package/dist/server/wasm.d.ts.map +1 -0
- package/dist/server-renderer.d.ts +5 -0
- package/dist/server-renderer.d.ts.map +1 -0
- package/dist/store/index.d.ts +2 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/types.d.ts +2 -0
- package/dist/store/types.d.ts.map +1 -0
- package/dist/wasm.d.ts +51 -0
- package/dist/wasm.d.ts.map +1 -0
- package/package.json +65 -56
- package/templates/basic-app/src/main.tsx +1 -0
- package/templates/fullstack-template/.env.example +42 -0
- package/templates/fullstack-template/README.md +38 -42
- package/templates/fullstack-template/package-lock.json +7932 -0
- package/templates/fullstack-template/package.json +58 -6
- package/templates/fullstack-template/server.js +28 -9
- package/templates/fullstack-template/src/App.tsx +94 -0
- package/templates/fullstack-template/src/api/auth/login.ts +75 -0
- package/templates/fullstack-template/src/api/auth/me.ts +66 -0
- package/templates/fullstack-template/src/api/auth/register.ts +59 -0
- package/templates/fullstack-template/src/api/todos/[id].ts +108 -0
- package/templates/fullstack-template/src/api/todos/index.ts +79 -0
- package/templates/fullstack-template/src/components/Dashboard.tsx +129 -0
- package/templates/fullstack-template/src/components/Header.tsx +76 -0
- package/templates/fullstack-template/src/contexts/ApiContext.tsx +69 -0
- package/templates/fullstack-template/src/contexts/AuthContext.tsx +155 -0
- package/templates/fullstack-template/src/server.ts +167 -0
- package/templates/fullstack-template/src/styles/App.css +446 -0
- package/templates/ssr-template/package-lock.json +337 -60
- package/templates/ssr-template/package.json +1 -1
- package/templates/ssr-template/src/App.tsx +2 -2
- package/templates/wasm/App.tsx +564 -0
- package/templates/wasm/client.tsx +220 -0
- package/templates/wasm/index.tsx +21 -0
- package/templates/wasm/package-lock.json +5347 -5307
- package/templates/wasm/package.json +43 -25
- package/templates/wasm/public/example.wasm +0 -0
- package/templates/wasm/public/wasm_exec.js +1 -12
- package/templates/wasm/server.ts +145 -0
- package/templates/wasm/vite.config.ts +42 -24
- package/README_WASM.md +0 -472
- package/dist/server.js.map +0 -7
- package/templates/basic-app/src/App.jsx +0 -0
- package/templates/basic-app/src/App.tsx +0 -0
- package/templates/fullstack-template/src/api/hello.js +0 -17
- package/templates/fullstack-template/src/pages/index.js +0 -206
- /package/templates/{basic-app/server.js → wasm/go/wasm_exec.js} +0 -0
|
@@ -0,0 +1,129 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { createServer, AuthService, Database } from 'baraqex/server';
|
|
2
|
+
import helmet from 'helmet';
|
|
3
|
+
import compression from 'compression';
|
|
4
|
+
import rateLimit from 'express-rate-limit';
|
|
5
|
+
import morgan from 'morgan';
|
|
6
|
+
import dotenv from 'dotenv';
|
|
7
|
+
|
|
8
|
+
// Load environment variables
|
|
9
|
+
dotenv.config();
|
|
10
|
+
|
|
11
|
+
// Server configuration
|
|
12
|
+
const config = {
|
|
13
|
+
port: parseInt(process.env.PORT || '3000'),
|
|
14
|
+
apiDir: './src/api',
|
|
15
|
+
pagesDir: './src/pages',
|
|
16
|
+
staticDir: './public',
|
|
17
|
+
enableCors: true,
|
|
18
|
+
corsOptions: {
|
|
19
|
+
origin: process.env.NODE_ENV === 'production'
|
|
20
|
+
? process.env.FRONTEND_URL || false
|
|
21
|
+
: true,
|
|
22
|
+
credentials: true
|
|
23
|
+
},
|
|
24
|
+
db: process.env.DATABASE_URL ? {
|
|
25
|
+
url: process.env.DATABASE_URL,
|
|
26
|
+
type: (process.env.DATABASE_TYPE as 'mongodb' | 'mysql' | 'postgres') || 'mongodb'
|
|
27
|
+
} : undefined,
|
|
28
|
+
auth: {
|
|
29
|
+
secret: process.env.JWT_SECRET || 'your-secret-key-change-in-production',
|
|
30
|
+
expiresIn: process.env.JWT_EXPIRES_IN || '7d'
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Create server instance
|
|
35
|
+
const server = createServer(config);
|
|
36
|
+
|
|
37
|
+
// Get Express app for additional middleware
|
|
38
|
+
const app = server.getExpressApp();
|
|
39
|
+
|
|
40
|
+
// Security middleware
|
|
41
|
+
app.use(helmet({
|
|
42
|
+
contentSecurityPolicy: {
|
|
43
|
+
directives: {
|
|
44
|
+
defaultSrc: ["'self'"],
|
|
45
|
+
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
46
|
+
scriptSrc: ["'self'"],
|
|
47
|
+
imgSrc: ["'self'", "data:", "https:"],
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
// Compression middleware
|
|
53
|
+
app.use(compression());
|
|
54
|
+
|
|
55
|
+
// Request logging
|
|
56
|
+
if (process.env.NODE_ENV !== 'test') {
|
|
57
|
+
app.use(morgan(process.env.NODE_ENV === 'production' ? 'combined' : 'dev'));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Rate limiting
|
|
61
|
+
const limiter = rateLimit({
|
|
62
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
63
|
+
max: process.env.NODE_ENV === 'production' ? 100 : 1000, // limit each IP to 100 requests per windowMs in production
|
|
64
|
+
message: {
|
|
65
|
+
error: 'Too many requests from this IP, please try again later.'
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
app.use('/api', limiter);
|
|
69
|
+
|
|
70
|
+
// Health check endpoint
|
|
71
|
+
app.get('/health', (req, res) => {
|
|
72
|
+
res.json({
|
|
73
|
+
status: 'ok',
|
|
74
|
+
timestamp: new Date().toISOString(),
|
|
75
|
+
version: process.env.npm_package_version || '1.0.0',
|
|
76
|
+
environment: process.env.NODE_ENV || 'development'
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// API status endpoint
|
|
81
|
+
app.get('/api/status', (req, res) => {
|
|
82
|
+
const db = server.getDatabase();
|
|
83
|
+
res.json({
|
|
84
|
+
api: 'online',
|
|
85
|
+
database: db ? 'connected' : 'not configured',
|
|
86
|
+
authentication: server.getAuth() ? 'enabled' : 'disabled',
|
|
87
|
+
timestamp: new Date().toISOString()
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Enable SSR with hydration
|
|
92
|
+
server.enableSSR({ hydratable: true });
|
|
93
|
+
|
|
94
|
+
// Database connection
|
|
95
|
+
async function connectDatabase() {
|
|
96
|
+
const db = server.getDatabase();
|
|
97
|
+
if (db) {
|
|
98
|
+
try {
|
|
99
|
+
await db.connect();
|
|
100
|
+
console.log('✅ Database connected successfully');
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.error('❌ Database connection failed:', error);
|
|
103
|
+
if (process.env.NODE_ENV === 'production') {
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
console.log('⚠️ No database configured');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Graceful shutdown
|
|
113
|
+
process.on('SIGTERM', async () => {
|
|
114
|
+
console.log('🔄 SIGTERM received, shutting down gracefully...');
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
await server.stop();
|
|
118
|
+
const db = server.getDatabase();
|
|
119
|
+
if (db) {
|
|
120
|
+
await db.disconnect();
|
|
121
|
+
}
|
|
122
|
+
console.log('✅ Server shut down complete');
|
|
123
|
+
process.exit(0);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error('❌ Error during shutdown:', error);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Start server
|
|
131
|
+
async function startServer() {
|
|
132
|
+
try {
|
|
133
|
+
// Connect to database first
|
|
134
|
+
await connectDatabase();
|
|
135
|
+
|
|
136
|
+
// Start the server
|
|
137
|
+
await server.start();
|
|
138
|
+
|
|
139
|
+
console.log(`🚀 Fullstack server running on http://localhost:${config.port}`);
|
|
140
|
+
console.log(`📊 Environment: ${process.env.NODE_ENV || 'development'}`);
|
|
141
|
+
console.log(`🔑 Auth: ${server.getAuth() ? 'enabled' : 'disabled'}`);
|
|
142
|
+
console.log(`💾 Database: ${server.getDatabase() ? 'connected' : 'not configured'}`);
|
|
143
|
+
console.log(`🌐 API docs: http://localhost:${config.port}/api-docs`);
|
|
144
|
+
console.log(`❤️ Health check: http://localhost:${config.port}/health`);
|
|
145
|
+
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error('❌ Failed to start server:', error);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Handle unhandled promise rejections
|
|
153
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
154
|
+
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
155
|
+
process.exit(1);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Handle uncaught exceptions
|
|
159
|
+
process.on('uncaughtException', (error) => {
|
|
160
|
+
console.error('Uncaught Exception:', error);
|
|
161
|
+
process.exit(1);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Start the server
|
|
165
|
+
startServer();
|
|
166
|
+
|
|
167
|
+
export default server;
|