feat: initial JaniChat messenger — PWA, WebSocket, admin panel
This commit is contained in:
76
backend/src/routes/auth.ts
Normal file
76
backend/src/routes/auth.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { FastifyInstance } from 'fastify';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import { pool } from '../db.js';
|
||||
|
||||
export default async function authRoutes(app: FastifyInstance) {
|
||||
app.post('/login', async (req, reply) => {
|
||||
const { username, password } = req.body as { username: string; password: string };
|
||||
|
||||
const { rows: [user] } = await pool.query(
|
||||
'SELECT * FROM users WHERE username = $1 AND is_active = TRUE', [username]
|
||||
);
|
||||
if (!user) return reply.status(401).send({ error: 'Неверный логин или пароль' });
|
||||
|
||||
const ok = await bcrypt.compare(password, user.password_hash);
|
||||
if (!ok) return reply.status(401).send({ error: 'Неверный логин или пароль' });
|
||||
|
||||
await pool.query('UPDATE users SET last_seen = NOW() WHERE id = $1', [user.id]);
|
||||
|
||||
const token = app.jwt.sign({ id: user.id, isAdmin: user.is_admin }, { expiresIn: '30d' });
|
||||
|
||||
return {
|
||||
token,
|
||||
user: {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
displayName: user.display_name,
|
||||
avatarColor: user.avatar_color,
|
||||
bio: user.bio,
|
||||
isAdmin: user.is_admin,
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
app.get('/me', { preHandler: [app.authenticate] }, async (req) => {
|
||||
const { id } = req.user as { id: string };
|
||||
const { rows: [user] } = await pool.query(
|
||||
'SELECT id, username, display_name, avatar_color, bio, phone, is_admin, last_seen FROM users WHERE id = $1', [id]
|
||||
);
|
||||
return {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
displayName: user.display_name,
|
||||
avatarColor: user.avatar_color,
|
||||
bio: user.bio,
|
||||
phone: user.phone,
|
||||
isAdmin: user.is_admin,
|
||||
lastSeen: user.last_seen,
|
||||
};
|
||||
});
|
||||
|
||||
app.put('/me', { preHandler: [app.authenticate] }, async (req, reply) => {
|
||||
const { id } = req.user as { id: string };
|
||||
const { displayName, bio, phone, password, newPassword } = req.body as any;
|
||||
|
||||
if (newPassword) {
|
||||
const { rows: [user] } = await pool.query('SELECT password_hash FROM users WHERE id = $1', [id]);
|
||||
const ok = await bcrypt.compare(password, user.password_hash);
|
||||
if (!ok) return reply.status(400).send({ error: 'Неверный текущий пароль' });
|
||||
const hash = await bcrypt.hash(newPassword, 10);
|
||||
await pool.query('UPDATE users SET password_hash = $1 WHERE id = $2', [hash, id]);
|
||||
}
|
||||
|
||||
await pool.query(
|
||||
'UPDATE users SET display_name = COALESCE($1, display_name), bio = COALESCE($2, bio), phone = COALESCE($3, phone) WHERE id = $4',
|
||||
[displayName, bio, phone, id]
|
||||
);
|
||||
|
||||
const { rows: [user] } = await pool.query(
|
||||
'SELECT id, username, display_name, avatar_color, bio, phone, is_admin FROM users WHERE id = $1', [id]
|
||||
);
|
||||
return {
|
||||
id: user.id, username: user.username, displayName: user.display_name,
|
||||
avatarColor: user.avatar_color, bio: user.bio, phone: user.phone, isAdmin: user.is_admin,
|
||||
};
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user