Fix mobile scroll jump, add comments, rename to Котограм

This commit is contained in:
2026-05-29 13:58:42 +03:00
parent 4bf9ea22dd
commit 3664b60f10
16 changed files with 352 additions and 34 deletions

View File

@@ -62,6 +62,16 @@ export async function initDb(): Promise<SqlJsDatabase> {
)
`);
db.run(`
CREATE TABLE IF NOT EXISTS comments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
cat_id INTEGER NOT NULL REFERENCES cats(id) ON DELETE CASCADE,
user_id INTEGER NOT NULL REFERENCES users(id),
text TEXT NOT NULL,
created_at TEXT DEFAULT (datetime('now'))
)
`);
saveDb();
return db;
}

View File

@@ -8,6 +8,7 @@ import authRoutes from './routes/auth.js';
import catRoutes from './routes/cats.js';
import likeRoutes from './routes/likes.js';
import adminRoutes from './routes/admin.js';
import commentRoutes from './routes/comments.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -25,14 +26,15 @@ async function main() {
app.use('/api/auth', authRoutes);
app.use('/api/cats', catRoutes);
app.use('/api/likes', likeRoutes);
app.use('/api/admin', adminRoutes);
app.use('/api/comments', commentRoutes);
app.use('/api/admin', adminRoutes);
app.get('/api/health', (_req, res) => {
res.json({ status: 'ok' });
});
app.listen(PORT, () => {
console.log(`Catstagram server running on http://localhost:${PORT}`);
console.log(`Котограм server running on http://localhost:${PORT}`);
});
}

View File

@@ -36,7 +36,8 @@ router.get('/', (req: AuthRequest, res: Response) => {
const cats = queryAll(
`SELECT c.id, c.image_url, c.caption, c.created_at, c.user_id,
u.username, u.points AS user_points,
(SELECT COUNT(*) FROM likes WHERE cat_id = c.id) AS likes_count
(SELECT COUNT(*) FROM likes WHERE cat_id = c.id) AS likes_count,
(SELECT COUNT(*) FROM comments WHERE cat_id = c.id) AS comments_count
FROM cats c
JOIN users u ON u.id = c.user_id
ORDER BY c.created_at DESC
@@ -55,7 +56,8 @@ router.get('/user/:userId', (req: AuthRequest, res: Response) => {
const cats = queryAll(
`SELECT c.id, c.image_url, c.caption, c.created_at, c.user_id,
u.username, u.points AS user_points,
(SELECT COUNT(*) FROM likes WHERE cat_id = c.id) AS likes_count
(SELECT COUNT(*) FROM likes WHERE cat_id = c.id) AS likes_count,
(SELECT COUNT(*) FROM comments WHERE cat_id = c.id) AS comments_count
FROM cats c
JOIN users u ON u.id = c.user_id
WHERE c.user_id = ?
@@ -70,7 +72,8 @@ router.get('/:id', (req: AuthRequest, res: Response) => {
const cat = queryOne(
`SELECT c.id, c.image_url, c.caption, c.created_at, c.user_id,
u.username, u.points AS user_points,
(SELECT COUNT(*) FROM likes WHERE cat_id = c.id) AS likes_count
(SELECT COUNT(*) FROM likes WHERE cat_id = c.id) AS likes_count,
(SELECT COUNT(*) FROM comments WHERE cat_id = c.id) AS comments_count
FROM cats c
JOIN users u ON u.id = c.user_id
WHERE c.id = ?`,
@@ -104,7 +107,8 @@ router.post('/', authMiddleware, upload.single('image'), (req: AuthRequest, res:
const cat = queryOne(
`SELECT c.id, c.image_url, c.caption, c.created_at, c.user_id,
u.username, u.points AS user_points,
0 AS likes_count
0 AS likes_count,
0 AS comments_count
FROM cats c
JOIN users u ON u.id = c.user_id
WHERE c.id = (SELECT MAX(id) FROM cats WHERE user_id = ?)`,
@@ -125,6 +129,7 @@ router.delete('/:id', authMiddleware, (req: AuthRequest, res: Response) => {
return;
}
execute('DELETE FROM comments WHERE cat_id = ?', [req.params.id]);
execute('DELETE FROM likes WHERE cat_id = ?', [req.params.id]);
execute('DELETE FROM cats WHERE id = ?', [req.params.id]);
res.json({ ok: true });

View File

@@ -0,0 +1,74 @@
import { Router, Response } from 'express';
import { queryAll, queryOne, execute } from '../db';
import { authMiddleware, AuthRequest } from '../middleware/auth.js';
const router = Router();
router.get('/:catId', (req: AuthRequest, res: Response) => {
const catId = parseInt(req.params.catId);
const comments = queryAll(
`SELECT c.id, c.text, c.created_at, c.user_id, u.username
FROM comments c
JOIN users u ON u.id = c.user_id
WHERE c.cat_id = ?
ORDER BY c.created_at ASC`,
[catId]
);
res.json({ comments });
});
router.post('/:catId', authMiddleware, (req: AuthRequest, res: Response) => {
const catId = parseInt(req.params.catId);
const userId = req.userId!;
const text = (req.body.text || '').trim();
if (!text) {
res.status(400).json({ error: 'Комментарий не может быть пустым' });
return;
}
if (text.length > 500) {
res.status(400).json({ error: 'Комментарий слишком длинный' });
return;
}
const cat = queryOne('SELECT id FROM cats WHERE id = ?', [catId]);
if (!cat) {
res.status(404).json({ error: 'Пост не найден' });
return;
}
const result = execute(
'INSERT INTO comments (cat_id, user_id, text) VALUES (?, ?, ?)',
[catId, userId, text]
);
const comment = queryOne(
`SELECT c.id, c.text, c.created_at, c.user_id, u.username
FROM comments c
JOIN users u ON u.id = c.user_id
WHERE c.id = ?`,
[result.lastInsertRowid]
);
res.status(201).json({ comment });
});
router.delete('/:commentId', authMiddleware, (req: AuthRequest, res: Response) => {
const commentId = parseInt(req.params.commentId);
const comment = queryOne('SELECT user_id FROM comments WHERE id = ?', [commentId]);
if (!comment) {
res.status(404).json({ error: 'Комментарий не найден' });
return;
}
if (comment.user_id !== req.userId && !req.isAdmin) {
res.status(403).json({ error: 'Нельзя удалить чужой комментарий' });
return;
}
execute('DELETE FROM comments WHERE id = ?', [commentId]);
res.json({ ok: true });
});
export default router;