Primakov Alexandr Alexandrovich debd28905a 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.
2025-11-17 13:53:25 +03:00

195 lines
4.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const router = require('express').Router();
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;