create-rex-app 1.0.0
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/index.js +148 -0
- package/package.json +31 -0
- package/template/README.md +71 -0
- package/template/_gitignore +25 -0
- package/template/api/routes.js +9 -0
- package/template/eslint.config.js +29 -0
- package/template/index.html +13 -0
- package/template/package-lock.json +4667 -0
- package/template/package.json +33 -0
- package/template/public/rex logo.png +0 -0
- package/template/public/rex.png +0 -0
- package/template/server.js +77 -0
- package/template/src/App.css +42 -0
- package/template/src/App.jsx +326 -0
- package/template/src/assets/react.svg +1 -0
- package/template/src/hooks/useTest.js +18 -0
- package/template/src/index.css +1 -0
- package/template/src/main.jsx +14 -0
- package/template/vercel.json +8 -0
- package/template/vite.config.js +7 -0
- package/template-mongo/README.md +96 -0
- package/template-mongo/api/db.js +17 -0
- package/template-mongo/api/middleware/authMiddleware.js +31 -0
- package/template-mongo/api/models/User.js +28 -0
- package/template-mongo/api/routes.js +99 -0
- package/template-mongo/env-sample.txt +3 -0
- package/template-mongo/eslint.config.js +29 -0
- package/template-mongo/index.html +13 -0
- package/template-mongo/package-lock.json +5076 -0
- package/template-mongo/package.json +38 -0
- package/template-mongo/public/rex logo.png +0 -0
- package/template-mongo/public/rex.png +0 -0
- package/template-mongo/public/vite.svg +1 -0
- package/template-mongo/server.js +80 -0
- package/template-mongo/src/App.css +42 -0
- package/template-mongo/src/App.jsx +42 -0
- package/template-mongo/src/assets/react.svg +1 -0
- package/template-mongo/src/components/Layout.jsx +85 -0
- package/template-mongo/src/components/PublicOnly.jsx +17 -0
- package/template-mongo/src/components/RequireAuth.jsx +20 -0
- package/template-mongo/src/context/AuthContext.jsx +35 -0
- package/template-mongo/src/hooks/useAuthContext.js +11 -0
- package/template-mongo/src/hooks/useLogin.js +42 -0
- package/template-mongo/src/hooks/useSignup.js +23 -0
- package/template-mongo/src/hooks/useTest.js +18 -0
- package/template-mongo/src/index.css +1 -0
- package/template-mongo/src/main.jsx +18 -0
- package/template-mongo/src/pages/Docs.jsx +131 -0
- package/template-mongo/src/pages/Home.jsx +93 -0
- package/template-mongo/src/pages/Login.jsx +97 -0
- package/template-mongo/src/pages/Signup.jsx +112 -0
- package/template-mongo/vercel.json +13 -0
- package/template-mongo/vite.config.js +7 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import jwt from 'jsonwebtoken';
|
|
2
|
+
import User from '../models/User.js'; // Ensure this path matches your models folder
|
|
3
|
+
|
|
4
|
+
const protect = async (req, res, next) => {
|
|
5
|
+
let token;
|
|
6
|
+
|
|
7
|
+
// 1. Check if the "Authorization" header exists and starts with "Bearer"
|
|
8
|
+
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer')) {
|
|
9
|
+
try {
|
|
10
|
+
// 2. Get the token from the string "Bearer <token>"
|
|
11
|
+
token = req.headers.authorization.split(' ')[1];
|
|
12
|
+
|
|
13
|
+
// 3. Verify the token using the Secret Key
|
|
14
|
+
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
|
15
|
+
|
|
16
|
+
// 4. Find the user (exclude password) and attach to 'req'
|
|
17
|
+
req.user = await User.findById(decoded.id).select('-password');
|
|
18
|
+
|
|
19
|
+
next(); // Pass the user to the next step
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.error(error);
|
|
22
|
+
res.status(401).json({ message: 'Not authorized, token failed' });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!token) {
|
|
27
|
+
res.status(401).json({ message: 'Not authorized, no token' });
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default protect;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import mongoose from 'mongoose';
|
|
2
|
+
|
|
3
|
+
const userSchema = new mongoose.Schema({
|
|
4
|
+
name: {
|
|
5
|
+
type: String,
|
|
6
|
+
required: true,
|
|
7
|
+
},
|
|
8
|
+
email: {
|
|
9
|
+
type: String,
|
|
10
|
+
required: true,
|
|
11
|
+
unique: true,
|
|
12
|
+
},
|
|
13
|
+
password: {
|
|
14
|
+
type: String,
|
|
15
|
+
required: true
|
|
16
|
+
}, // <--- Added this!
|
|
17
|
+
role: {
|
|
18
|
+
type: String,
|
|
19
|
+
default: 'developer',
|
|
20
|
+
},
|
|
21
|
+
createdAt: {
|
|
22
|
+
type: Date,
|
|
23
|
+
default: Date.now,
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const User = mongoose.model('User', userSchema);
|
|
28
|
+
export default User;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import bcrypt from 'bcryptjs'; // <--- Import this
|
|
3
|
+
import User from './models/User.js';
|
|
4
|
+
import jwt from 'jsonwebtoken';
|
|
5
|
+
import protect from './middleware/authMiddleware.js';
|
|
6
|
+
const router = express.Router();
|
|
7
|
+
|
|
8
|
+
// 1. GET ALL USERS
|
|
9
|
+
router.get('/users', async (req, res) => {
|
|
10
|
+
try {
|
|
11
|
+
// Return everything BUT the password (security best practice)
|
|
12
|
+
const users = await User.find().select('-password');
|
|
13
|
+
res.json(users);
|
|
14
|
+
} catch (error) {
|
|
15
|
+
res.status(500).json({ message: error.message });
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// 2. CREATE A USER (Sign Up)
|
|
20
|
+
router.post('/users', async (req, res) => {
|
|
21
|
+
try {
|
|
22
|
+
const { name, email, password } = req.body;
|
|
23
|
+
|
|
24
|
+
// 1. Check if user exists
|
|
25
|
+
const userExists = await User.findOne({ email });
|
|
26
|
+
if (userExists) {
|
|
27
|
+
return res.status(400).json({ message: 'User already exists' });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 2. Hash the password
|
|
31
|
+
const salt = await bcrypt.genSalt(10);
|
|
32
|
+
const hashedPassword = await bcrypt.hash(password, salt);
|
|
33
|
+
|
|
34
|
+
// 3. Create user with hashed password
|
|
35
|
+
const newUser = new User({
|
|
36
|
+
name,
|
|
37
|
+
email,
|
|
38
|
+
password: hashedPassword
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const savedUser = await newUser.save();
|
|
42
|
+
|
|
43
|
+
// 4. Respond (don't send back the password!)
|
|
44
|
+
res.status(201).json({
|
|
45
|
+
_id: savedUser._id,
|
|
46
|
+
name: savedUser.name,
|
|
47
|
+
email: savedUser.email,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
} catch (error) {
|
|
51
|
+
res.status(400).json({ message: error.message });
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// 3. LOGIN USER (Generate Token)
|
|
56
|
+
router.post('/login', async (req, res) => {
|
|
57
|
+
try {
|
|
58
|
+
const { email, password } = req.body;
|
|
59
|
+
|
|
60
|
+
// A. Validate User
|
|
61
|
+
const user = await User.findOne({ email });
|
|
62
|
+
if (!user) return res.status(400).json({ message: 'Invalid credentials' });
|
|
63
|
+
|
|
64
|
+
// B. Validate Password
|
|
65
|
+
const isMatch = await bcrypt.compare(password, user.password);
|
|
66
|
+
if (!isMatch) return res.status(400).json({ message: 'Invalid credentials' });
|
|
67
|
+
|
|
68
|
+
// C. Generate Token
|
|
69
|
+
const token = jwt.sign(
|
|
70
|
+
{ id: user._id },
|
|
71
|
+
process.env.JWT_SECRET,
|
|
72
|
+
{ expiresIn: '30d' }
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
// D. Response: STRICTLY TOKEN ONLY
|
|
76
|
+
res.json({
|
|
77
|
+
message: "Login successful",
|
|
78
|
+
token: token
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
} catch (error) {
|
|
82
|
+
res.status(500).json({ message: error.message });
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// 4. Profile - Middleware
|
|
87
|
+
router.get('/profile', protect, (req, res) => {
|
|
88
|
+
// The 'protect' middleware has already run.
|
|
89
|
+
// It found the user and put it in 'req.user'.
|
|
90
|
+
|
|
91
|
+
res.json({
|
|
92
|
+
_id: req.user._id,
|
|
93
|
+
name: req.user.name,
|
|
94
|
+
email: req.user.email,
|
|
95
|
+
role: req.user.role,
|
|
96
|
+
message: `Verification Successful. Welcome, ${req.user.name}!`
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
export default router;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import js from '@eslint/js'
|
|
2
|
+
import globals from 'globals'
|
|
3
|
+
import reactHooks from 'eslint-plugin-react-hooks'
|
|
4
|
+
import reactRefresh from 'eslint-plugin-react-refresh'
|
|
5
|
+
import { defineConfig, globalIgnores } from 'eslint/config'
|
|
6
|
+
|
|
7
|
+
export default defineConfig([
|
|
8
|
+
globalIgnores(['dist']),
|
|
9
|
+
{
|
|
10
|
+
files: ['**/*.{js,jsx}'],
|
|
11
|
+
extends: [
|
|
12
|
+
js.configs.recommended,
|
|
13
|
+
reactHooks.configs.flat.recommended,
|
|
14
|
+
reactRefresh.configs.vite,
|
|
15
|
+
],
|
|
16
|
+
languageOptions: {
|
|
17
|
+
ecmaVersion: 2020,
|
|
18
|
+
globals: globals.browser,
|
|
19
|
+
parserOptions: {
|
|
20
|
+
ecmaVersion: 'latest',
|
|
21
|
+
ecmaFeatures: { jsx: true },
|
|
22
|
+
sourceType: 'module',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
rules: {
|
|
26
|
+
'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
])
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/rex.png" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>Rex - One House Partner</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="root"></div>
|
|
11
|
+
<script type="module" src="/src/main.jsx"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|