Files
uno-click/bff/routes/auth.routes.js.bak.20260423_095808
T
2026-05-13 14:20:41 +00:00

213 lines
7.1 KiB
Plaintext

import { Router } from 'express';
import { env } from '../config/env.js';
import { loginUser, logoutUser, refreshUserSession, registerUser, loginWithTelegram } from '../services/auth.service.js';
import { authRequired } from '../middleware/authRequired.js';
import { findUserById, updateUserBalance } from '../repositories/user.repository.js';
const router = Router();
function buildAccessCookieOptions() {
return {
httpOnly: true,
secure: env.COOKIE_SECURE,
sameSite: env.COOKIE_SAME_SITE,
// Domain не указывается для __Host- префикса (требование RFC 6265)
path: '/',
maxAge: env.ACCESS_TOKEN_TTL_SEC * 1000,
};
}
function buildRefreshCookieOptions() {
return {
httpOnly: true,
secure: env.COOKIE_SECURE,
sameSite: env.COOKIE_SAME_SITE,
// Domain не указывается для __Host- префикса (требование RFC 6265)
path: '/',
maxAge: env.REFRESH_TOKEN_TTL_SEC * 1000,
};
}
function buildCsrfCookieOptions() {
return {
httpOnly: false, // фронту надо прочитать и отправить в header
secure: env.COOKIE_SECURE,
sameSite: env.COOKIE_SAME_SITE,
// Domain не указывается для работы с __Host- префиксом
path: '/',
maxAge: env.REFRESH_TOKEN_TTL_SEC * 1000,
};
}
router.post('/login', async (req, res, next) => {
try {
const { email, password } = req.body;
const result = await loginUser({
email,
password,
userAgent: req.get('user-agent') || null,
ipAddress: req.ip || null,
});
res.cookie(env.COOKIE_ACCESS_NAME, result.accessToken, buildAccessCookieOptions());
res.cookie(env.COOKIE_REFRESH_NAME, result.refreshToken, buildRefreshCookieOptions());
res.cookie(env.COOKIE_CSRF_NAME, result.csrfToken, buildCsrfCookieOptions());
res.json({
ok: true,
accessToken: result.accessToken,
user: result.user,
});
} catch (err) {
next(err);
}
});
router.post('/logout', authRequired, async (req, res, next) => {
try {
await logoutUser({ sessionId: req.user.sessionId });
res.clearCookie(env.COOKIE_ACCESS_NAME, { path: '/' });
res.clearCookie(env.COOKIE_REFRESH_NAME, { path: '/' });
res.clearCookie(env.COOKIE_CSRF_NAME, { path: '/' });
res.json({ ok: true });
} catch (err) {
next(err);
}
});
router.post('/refresh', async (req, res, next) => {
try {
const refreshToken = req.cookies?.[env.COOKIE_REFRESH_NAME];
if (!refreshToken) {
return res.status(401).json({
error: 'REFRESH_TOKEN_MISSING',
message: 'Refresh token is missing',
});
}
const result = await refreshUserSession({
refreshToken,
userAgent: req.get('user-agent') || null,
ipAddress: req.ip || null,
});
res.cookie(env.COOKIE_ACCESS_NAME, result.accessToken, buildAccessCookieOptions());
res.cookie(env.COOKIE_REFRESH_NAME, result.refreshToken, buildRefreshCookieOptions());
res.cookie(env.COOKIE_CSRF_NAME, result.csrfToken, buildCsrfCookieOptions());
res.json({
ok: true,
user: result.user,
});
} catch (err) {
next(err);
}
});
router.get('/me', authRequired, async (req, res) => {
try {
const dbUser = await findUserById(req.user.id);
if (!dbUser) return res.status(401).json({ error: 'USER_NOT_FOUND' });
res.json({
ok: true,
user: {
id: dbUser.id,
email: dbUser.email,
displayName: dbUser.display_name,
role: dbUser.role,
status: dbUser.status,
balance: Number(dbUser.balance || 0),
createdAt: dbUser.created_at,
sessionId: req.user.sessionId,
},
});
} catch (err) {
res.status(500).json({ error: err.message });
}
});
router.post('/balance/update', authRequired, async (req, res) => {
const delta = Number(req.body.delta);
if (!delta || isNaN(delta)) return res.status(400).json({ error: 'delta required' });
try {
const newBalance = await updateUserBalance(req.user.id, delta);
if (newBalance === null) return res.status(404).json({ error: 'User not found' });
res.json({ ok: true, balance: Number(newBalance) });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
router.get('/csrf', authRequired, async (req, res) => {
res.json({
ok: true,
csrfToken: req.cookies?.[env.COOKIE_CSRF_NAME] || null,
});
});
router.post('/register', async (req, res, next) => {
try {
const { email, password, name } = req.body;
const result = await registerUser({
email,
password,
displayName: name || null,
userAgent: req.get('user-agent') || null,
ipAddress: req.ip || null,
});
res.cookie(env.COOKIE_ACCESS_NAME, result.accessToken, buildAccessCookieOptions());
res.cookie(env.COOKIE_REFRESH_NAME, result.refreshToken, buildRefreshCookieOptions());
res.cookie(env.COOKIE_CSRF_NAME, result.csrfToken, buildCsrfCookieOptions());
res.status(201).json({
ok: true,
accessToken: result.accessToken,
user: result.user,
});
} catch (err) {
if (err.statusCode === 409) {
return res.status(409).json({ ok: false, error: err.code, message: err.message });
}
if (err.statusCode === 400) {
return res.status(400).json({ ok: false, error: err.code, message: err.message });
}
next(err);
}
});
router.post('/telegram', async (req, res, next) => {
try {
const result = await loginWithTelegram({
telegramData: req.body,
userAgent: req.get('user-agent') || null,
ipAddress: req.ip || null,
});
res.cookie(env.COOKIE_ACCESS_NAME, result.accessToken, buildAccessCookieOptions());
res.cookie(env.COOKIE_REFRESH_NAME, result.refreshToken, buildRefreshCookieOptions());
res.cookie(env.COOKIE_CSRF_NAME, result.csrfToken, buildCsrfCookieOptions());
res.status(200).json({ ok: true, accessToken: result.accessToken, user: result.user });
} catch (err) {
const status = err.statusCode || 500;
if (status < 500) {
return res.status(status).json({ ok: false, error: err.code, message: err.message });
}
next(err);
}
});
router.get('/telegram-widget', (req, res) => {
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.send('<!DOCTYPE html>\n<html lang="ru"><head><meta charset="utf-8"/><title>Telegram Auth</title><style>*{margin:0;padding:0}body{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;background:#1a1a2e;font-family:sans-serif;color:#fff}h2{font-size:24px;font-weight:700;margin-bottom:6px}p{font-size:13px;color:#aaa;margin-bottom:28px}.wrap{min-height:48px;display:flex;align-items:center}</style></head><body><h2>One Click</h2><p>Войдите через Telegram</p><div class="wrap" id="tg"></div><script>window.onTelegramAuth=function(u){if(window.opener){window.opener.postMessage({type:"telegram_auth",data:u},"*");}window.close();};</script><script async src="https://telegram.org/js/telegram-widget.js?22" data-telegram-login="generatet_bot" data-size="large" data-radius="12" data-onauth="onTelegramAuth(user)" data-request-access="write"></script></body></html>');
});
export default router;