xgit simple git

nexus

nexus

clone git clone https://kb.hax.al/nexus

api/reply.php


1<?php
2require_once __DIR__ . '/../includes/bootstrap.php';
3if (!$USER) json_out(['error'=>'Not logged in'],401);
4if ($_SERVER['REQUEST_METHOD']!=='POST') json_out(['error'=>'Method not allowed'],405);
5if (!csrf_ok()) json_out(['error'=>'Invalid CSRF token'],403);
6if ($USER['silenced']) json_out(['error'=>'You are silenced and cannot post'],403);
7 
8// ── Rate limit check ─────────────────────────────────────────
9$rl = rate_check((int)$USER['id'], 'post');
10if (!$rl['ok']) {
11    $wait = (int)$rl['wait'];
12    json_out(['error'=>"You're posting too fast. Please wait {$wait} second".($wait!==1?'s':'').' before posting again.','rate_limited'=>true,'wait'=>$wait], 429);
13}
14 
15// ── Post captcha check ───────────────────────────────────────
16if (post_captcha_enabled()) {
17    $cap = post('post_captcha');
18    if (!post_captcha_verify($cap)) {
19        json_out(['error'=>'Incorrect security answer.','captcha_failed'=>true], 403);
20    }
21}
22 
23$slug    = sanitise(post('slug'));
24$content = sanitise(post('content'));
25$replyTo = (int)post('reply_to');
26 
27if (!$slug || !$content) json_out(['error'=>'Missing required fields'],400);
28if (strlen($content) < 1)     json_out(['error'=>'Reply cannot be empty'],400);
29if (strlen($content) > 20000) json_out(['error'=>'Post too long (max 20,000 chars)'],400);
30 
31$topic = DB::row('SELECT * FROM topics WHERE slug=?', [$slug]);
32if (!$topic) json_out(['error'=>'Topic not found'],404);
33if ($topic['closed']) json_out(['error'=>'This topic is closed'],403);
34$replyCat = DB::row('SELECT * FROM categories WHERE id=?',[$topic['category_id']]);
35if (!can_reply_topic($replyCat ?? [])) json_out(['error'=>'No permission to reply in this category'],403);
36 
37$last = DB::row('SELECT post_num FROM posts WHERE topic_id=? ORDER BY post_num DESC LIMIT 1',[$topic['id']]);
38$num  = ($last ? (int)$last['post_num'] : 0) + 1;
39 
40$pid = DB::insert(
41    'INSERT INTO posts (topic_id,user_id,content,post_num,reply_to) VALUES (?,?,?,?,?)',
42    [$topic['id'], $USER['id'], $content, $num, $replyTo ?: null]
43);
44 
45// Record rate limit event AFTER successful post
46rate_record((int)$USER['id'], 'post');
47 
48$now = DB::now();
49DB::run("UPDATE topics SET last_post_at=$now,reply_count=reply_count+1 WHERE id=?",[$topic['id']]);
50DB::run('UPDATE users SET post_count=post_count+1 WHERE id=?',[$USER['id']]);
51DB::run('UPDATE categories SET post_count=post_count+1 WHERE id=?',[$topic['category_id']]);
52 
53if ($topic['user_id'] !== $USER['id']) {
54    add_notification((int)$topic['user_id'],'reply',[
55        'slug'=>$topic['slug'],'title'=>$topic['title'],'from'=>$USER['username']
56    ]);
57}
58process_mentions($content, $pid, $topic['slug'], (int)$USER['id'], $USER['username']);
59 
60$post = DB::row("
61    SELECT p.*,u.username,u.avatar,u.role,u.post_count,0 AS likes
62    FROM posts p JOIN users u ON u.id=p.user_id WHERE p.id=?
63",[$pid]);
64 
65// Send new captcha if post captcha enabled
66$newCaptcha = post_captcha_enabled() ? post_captcha_generate() : null;
67 
68addon_hook('after_reply_saved', ['post_id'=>$pid,'topic_id'=>$topic['id'],'user_id'=>(int)$USER['id']]);
69json_out(['ok'=>true,'post'=>$post,'new_captcha'=>$newCaptcha]);