Add authentication and tracking features with API integration
- Implemented user authentication with signup and signin functionality. - Created a context for managing authentication state. - Added protected routes for accessing the dashboard and tracker pages. - Developed a tracker page for logging cigarette usage with optional notes and timestamps. - Introduced a statistics page to visualize daily smoking habits using charts. - Integrated Axios for API requests and error handling. - Updated package dependencies including React Hook Form and Zod for form validation. - Enhanced UI components for better user experience with Chakra UI. - Added routing for authentication and tracking pages.
This commit is contained in:
@@ -4,5 +4,191 @@ const timer = (time = 300) => (req, res, next) => setTimeout(next, time);
|
||||
|
||||
router.use(timer());
|
||||
|
||||
// In-memory storage for demo
|
||||
const users = [];
|
||||
const cigarettes = [];
|
||||
let userIdCounter = 1;
|
||||
let cigaretteIdCounter = 1;
|
||||
|
||||
// Simple token generation (for demo purposes only)
|
||||
const generateToken = (userId) => {
|
||||
return Buffer.from(JSON.stringify({ userId, exp: Date.now() + 12 * 60 * 60 * 1000 })).toString('base64');
|
||||
};
|
||||
|
||||
const verifyToken = (token) => {
|
||||
try {
|
||||
const payload = JSON.parse(Buffer.from(token, 'base64').toString());
|
||||
if (payload.exp > Date.now()) {
|
||||
return payload.userId;
|
||||
}
|
||||
return null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// Auth middleware
|
||||
const authMiddleware = (req, res, next) => {
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
return res.status(401).json({ success: false, errors: 'Требуется авторизация' });
|
||||
}
|
||||
|
||||
const token = authHeader.substring(7);
|
||||
const userId = verifyToken(token);
|
||||
|
||||
if (!userId) {
|
||||
return res.status(401).json({ success: false, errors: 'Неверный или истекший токен авторизации' });
|
||||
}
|
||||
|
||||
req.userId = userId;
|
||||
next();
|
||||
};
|
||||
|
||||
// POST /auth/signup
|
||||
router.post('/auth/signup', (req, res) => {
|
||||
const { login, password } = req.body;
|
||||
|
||||
if (!login || !password) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
errors: 'Не все поля заполнены: login, password'
|
||||
});
|
||||
}
|
||||
|
||||
const existingUser = users.find(u => u.login === login);
|
||||
if (existingUser) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
errors: 'Пользователь с таким логином уже существует'
|
||||
});
|
||||
}
|
||||
|
||||
const user = {
|
||||
id: String(userIdCounter++),
|
||||
login,
|
||||
password, // In real app, hash this!
|
||||
created: new Date().toISOString()
|
||||
};
|
||||
|
||||
users.push(user);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
body: { ok: true }
|
||||
});
|
||||
});
|
||||
|
||||
// POST /auth/signin
|
||||
router.post('/auth/signin', (req, res) => {
|
||||
const { login, password } = req.body;
|
||||
|
||||
if (!login || !password) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
errors: 'Не все поля заполнены: login, password'
|
||||
});
|
||||
}
|
||||
|
||||
const user = users.find(u => u.login === login && u.password === password);
|
||||
|
||||
if (!user) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
errors: 'Неверный логин или пароль'
|
||||
});
|
||||
}
|
||||
|
||||
const token = generateToken(user.id);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
body: {
|
||||
user: {
|
||||
id: user.id,
|
||||
login: user.login,
|
||||
created: user.created
|
||||
},
|
||||
token
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// POST /cigarettes
|
||||
router.post('/cigarettes', authMiddleware, (req, res) => {
|
||||
const { smokedAt, note } = req.body;
|
||||
|
||||
const cigarette = {
|
||||
id: String(cigaretteIdCounter++),
|
||||
userId: req.userId,
|
||||
smokedAt: smokedAt || new Date().toISOString(),
|
||||
note: note || '',
|
||||
created: new Date().toISOString()
|
||||
};
|
||||
|
||||
cigarettes.push(cigarette);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
body: cigarette
|
||||
});
|
||||
});
|
||||
|
||||
// GET /cigarettes
|
||||
router.get('/cigarettes', authMiddleware, (req, res) => {
|
||||
const { from, to } = req.query;
|
||||
|
||||
let userCigarettes = cigarettes.filter(c => c.userId === req.userId);
|
||||
|
||||
if (from) {
|
||||
const fromDate = new Date(from);
|
||||
userCigarettes = userCigarettes.filter(c => new Date(c.smokedAt) >= fromDate);
|
||||
}
|
||||
|
||||
if (to) {
|
||||
const toDate = new Date(to);
|
||||
userCigarettes = userCigarettes.filter(c => new Date(c.smokedAt) <= toDate);
|
||||
}
|
||||
|
||||
// Sort by smokedAt (oldest to newest)
|
||||
userCigarettes.sort((a, b) => new Date(a.smokedAt).getTime() - new Date(b.smokedAt).getTime());
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
body: userCigarettes
|
||||
});
|
||||
});
|
||||
|
||||
// GET /stats/daily
|
||||
router.get('/stats/daily', authMiddleware, (req, res) => {
|
||||
const { from, to } = req.query;
|
||||
|
||||
// Default: 30 days ago to now
|
||||
const fromDate = from ? new Date(from) : new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
|
||||
const toDate = to ? new Date(to) : new Date();
|
||||
|
||||
let userCigarettes = cigarettes.filter(c => {
|
||||
if (c.userId !== req.userId) return false;
|
||||
const smokedDate = new Date(c.smokedAt);
|
||||
return smokedDate >= fromDate && smokedDate <= toDate;
|
||||
});
|
||||
|
||||
// Group by date
|
||||
const dailyStats = {};
|
||||
userCigarettes.forEach(c => {
|
||||
const date = c.smokedAt.split('T')[0]; // YYYY-MM-DD
|
||||
dailyStats[date] = (dailyStats[date] || 0) + 1;
|
||||
});
|
||||
|
||||
// Convert to array and sort
|
||||
const result = Object.entries(dailyStats)
|
||||
.map(([date, count]) => ({ date, count }))
|
||||
.sort((a, b) => a.date.localeCompare(b.date));
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
body: result
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
Reference in New Issue
Block a user