From 68972bbe6c2473c4c035c7afe1a3cb5361d077bc Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Thu, 19 Feb 2026 13:22:08 +0000 Subject: [PATCH] Projet final V5 + Forum V2 --- api_v1_messages.php | 8 ++- api_v1_roles.php | 7 +- api_v1_tags.php | 2 +- api_v1_threads.php | 25 +++++-- assets/js/main.js | 9 ++- includes/permissions.php | 141 ++++++++++++++++++------------------- index.php | 145 ++++++++++++++++++++++++++++++++------- requests.log | 111 ++++++++++++++++++++++++++++++ 8 files changed, 337 insertions(+), 111 deletions(-) diff --git a/api_v1_messages.php b/api_v1_messages.php index 1332033..c2ec195 100644 --- a/api_v1_messages.php +++ b/api_v1_messages.php @@ -227,7 +227,13 @@ if (empty($content) && empty($attachment_url)) { } // Check granular permissions -if (!Permissions::canSendInChannel($user_id, $channel_id)) { +$can_send = Permissions::canSendInChannel($user_id, $channel_id); +if ($thread_id) { + // For threads, we check the specific thread permission instead of the general channel permission + $can_send = Permissions::canDoInChannel($user_id, $channel_id, Permissions::SEND_MESSAGES_IN_THREADS); +} + +if (!$can_send) { echo json_encode(['success' => false, 'error' => 'You do not have permission to send messages in this channel.']); exit; } diff --git a/api_v1_roles.php b/api_v1_roles.php index 84539f7..2b7feae 100644 --- a/api_v1_roles.php +++ b/api_v1_roles.php @@ -65,7 +65,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { ['value' => 4, 'name' => 'Manage Messages'], ['value' => 8, 'name' => 'Manage Channels'], ['value' => 16, 'name' => 'Manage Server'], - ['value' => 32, 'name' => 'Administrator'] + ['value' => 32, 'name' => 'Administrator'], + ['value' => 64, 'name' => 'Create Thread'], + ['value' => 128, 'name' => 'Manage Tags'], + ['value' => 256, 'name' => 'Pin Threads'], + ['value' => 512, 'name' => 'Lock Threads'], + ['value' => 1024, 'name' => 'Send Messages in Threads'] ] ]); exit; diff --git a/api_v1_tags.php b/api_v1_tags.php index 19d0026..ae4e341 100644 --- a/api_v1_tags.php +++ b/api_v1_tags.php @@ -29,7 +29,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $stmt = db()->prepare("SELECT server_id FROM channels WHERE id = ?"); $stmt->execute([$channel_id]); $chan = $stmt->fetch(); - if (!$chan || !Permissions::hasPermission($user_id, $chan['server_id'], Permissions::MANAGE_CHANNELS)) { + if (!$chan || !Permissions::canDoInChannel($user_id, $channel_id, Permissions::MANAGE_TAGS)) { echo json_encode(['success' => false, 'error' => 'Unauthorized']); exit; } diff --git a/api_v1_threads.php b/api_v1_threads.php index 49e7855..2903d23 100644 --- a/api_v1_threads.php +++ b/api_v1_threads.php @@ -14,7 +14,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { exit; } - if (!Permissions::canSendInChannel($user_id, $channel_id)) { + if (!Permissions::canDoInChannel($user_id, $channel_id, Permissions::CREATE_THREAD)) { echo json_encode(['success' => false, 'error' => 'You do not have permission to create threads in this channel.']); exit; } @@ -81,28 +81,41 @@ if ($_SERVER['REQUEST_METHOD'] === 'PATCH' || $_SERVER['REQUEST_METHOD'] === 'DE Permissions::hasPermission($user_id, $thread['server_id'], Permissions::MANAGE_MESSAGES) || $server['owner_id'] == $user_id; - if ($thread['user_id'] != $user_id && !$is_admin) { - echo json_encode(['success' => false, 'error' => 'Unauthorized']); - exit; - } - try { if ($action === 'solve') { + if ($thread['user_id'] != $user_id && !$is_admin) { + echo json_encode(['success' => false, 'error' => 'Unauthorized']); exit; + } $stmt = db()->prepare("UPDATE forum_threads SET solution_message_id = ? WHERE id = ?"); $stmt->execute([$message_id, $thread_id]); } elseif ($action === 'pin') { + if (!Permissions::canDoInChannel($user_id, $thread['channel_id'], Permissions::PIN_THREADS)) { + echo json_encode(['success' => false, 'error' => 'You do not have permission to pin threads.']); exit; + } $stmt = db()->prepare("UPDATE forum_threads SET is_pinned = 1 WHERE id = ?"); $stmt->execute([$thread_id]); } elseif ($action === 'unpin') { + if (!Permissions::canDoInChannel($user_id, $thread['channel_id'], Permissions::PIN_THREADS)) { + echo json_encode(['success' => false, 'error' => 'You do not have permission to unpin threads.']); exit; + } $stmt = db()->prepare("UPDATE forum_threads SET is_pinned = 0 WHERE id = ?"); $stmt->execute([$thread_id]); } elseif ($action === 'lock') { + if (!Permissions::canDoInChannel($user_id, $thread['channel_id'], Permissions::LOCK_THREADS)) { + echo json_encode(['success' => false, 'error' => 'You do not have permission to lock threads.']); exit; + } $stmt = db()->prepare("UPDATE forum_threads SET is_locked = 1 WHERE id = ?"); $stmt->execute([$thread_id]); } elseif ($action === 'unlock') { + if (!Permissions::canDoInChannel($user_id, $thread['channel_id'], Permissions::LOCK_THREADS)) { + echo json_encode(['success' => false, 'error' => 'You do not have permission to unlock threads.']); exit; + } $stmt = db()->prepare("UPDATE forum_threads SET is_locked = 0 WHERE id = ?"); $stmt->execute([$thread_id]); } elseif ($action === 'delete') { + if ($thread['user_id'] != $user_id && !$is_admin) { + echo json_encode(['success' => false, 'error' => 'Unauthorized']); exit; + } db()->beginTransaction(); // Delete associated tags $stmt = db()->prepare("DELETE FROM thread_tags WHERE thread_id = ?"); diff --git a/assets/js/main.js b/assets/js/main.js index ae289f6..e75def6 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -1256,8 +1256,10 @@ document.addEventListener('DOMContentLoaded', () => { return perm.user_id == id && perm.type === 'member'; }) || { allow_permissions: 0, deny_permissions: 0 }; - updateToggleUI(1, p.allow_permissions, p.deny_permissions); - updateToggleUI(2, p.allow_permissions, p.deny_permissions); + document.querySelectorAll('.perm-tri-state').forEach(group => { + const bit = parseInt(group.dataset.permBit); + updateToggleUI(bit, p.allow_permissions, p.deny_permissions); + }); } function updateToggleUI(bit, allowPerms, denyPerms) { @@ -1311,8 +1313,9 @@ document.addEventListener('DOMContentLoaded', () => { }); if (!p) { - p = { channel_id: channelId, allow_permissions: 0, deny_permissions: 0 }; + p = { channel_id: channelId, allow_permissions: 0, deny_permissions: 0, type: type }; if (type === 'role') p.role_id = id; else p.user_id = id; + channelPermissionsData.push(p); } let allow = parseInt(p.allow_permissions); diff --git a/includes/permissions.php b/includes/permissions.php index ab0b5ba..ea5a9ca 100644 --- a/includes/permissions.php +++ b/includes/permissions.php @@ -7,6 +7,11 @@ class Permissions { const MANAGE_CHANNELS = 8; const MANAGE_SERVER = 16; const ADMINISTRATOR = 32; + const CREATE_THREAD = 64; + const MANAGE_TAGS = 128; + const PIN_THREADS = 256; + const LOCK_THREADS = 512; + const SEND_MESSAGES_IN_THREADS = 1024; public static function hasPermission($user_id, $server_id, $permission) { $stmt = db()->prepare("SELECT is_admin FROM users WHERE id = ?"); @@ -19,11 +24,12 @@ class Permissions { $server = $stmt->fetch(); if ($server && $server['owner_id'] == $user_id) return true; + // Aggregate permissions from user's roles AND the @everyone role $stmt = db()->prepare(" - SELECT SUM(r.permissions) as total_perms + SELECT BIT_OR(r.permissions) as total_perms FROM roles r - JOIN user_roles ur ON r.id = ur.role_id - WHERE ur.user_id = ? AND r.server_id = ? + LEFT JOIN user_roles ur ON r.id = ur.role_id AND ur.user_id = ? + WHERE r.server_id = ? AND (ur.user_id IS NOT NULL OR r.name = '@everyone' OR r.name = 'Everyone') "); $stmt->execute([$user_id, $server_id]); $row = $stmt->fetch(); @@ -34,56 +40,14 @@ class Permissions { } public static function canViewChannel($user_id, $channel_id) { - $stmt = db()->prepare("SELECT server_id FROM channels WHERE id = ?"); - $stmt->execute([$channel_id]); - $c = $stmt->fetch(); - if (!$c) return false; - $server_id = $c['server_id']; - - // Check if owner or admin - if (self::hasPermission($user_id, $server_id, self::ADMINISTRATOR)) return true; - - // Fetch overrides for all roles the user has in this server - $stmt = db()->prepare(" - SELECT cp.allow_permissions, cp.deny_permissions - FROM channel_permissions cp - JOIN user_roles ur ON cp.role_id = ur.role_id - WHERE ur.user_id = ? AND cp.channel_id = ? - "); - $stmt->execute([$user_id, $channel_id]); - $overrides = $stmt->fetchAll(); - - // Check @everyone override specifically (even if user has no roles assigned) - $stmt = db()->prepare("SELECT id FROM roles WHERE server_id = ? AND (name = '@everyone' OR name = 'Everyone') LIMIT 1"); - $stmt->execute([$server_id]); - $everyone_role = $stmt->fetch(); - if ($everyone_role) { - $stmt = db()->prepare("SELECT allow_permissions, deny_permissions FROM channel_permissions WHERE channel_id = ? AND role_id = ?"); - $stmt->execute([$channel_id, $everyone_role['id']]); - $eo = $stmt->fetch(); - if ($eo) { - $overrides[] = $eo; - } - } - - if (empty($overrides)) { - return true; // Default to yes - } - - $allow = false; - $deny = false; - foreach($overrides as $o) { - if ($o['allow_permissions'] & self::VIEW_CHANNEL) $allow = true; - if ($o['deny_permissions'] & self::VIEW_CHANNEL) $deny = true; - } - - if ($allow) return true; - if ($deny) return false; - - return true; // Default to yes + return self::canDoInChannel($user_id, $channel_id, self::VIEW_CHANNEL); } public static function canSendInChannel($user_id, $channel_id) { + return self::canDoInChannel($user_id, $channel_id, self::SEND_MESSAGES); + } + + public static function canDoInChannel($user_id, $channel_id, $permission) { $stmt = db()->prepare("SELECT server_id FROM channels WHERE id = ?"); $stmt->execute([$channel_id]); $c = $stmt->fetch(); @@ -93,39 +57,68 @@ class Permissions { // Check if owner or admin if (self::hasPermission($user_id, $server_id, self::ADMINISTRATOR)) return true; - // Check overrides + // Fetch all relevant overrides + // 1. @everyone role + // 2. User's roles + // 3. User specifically + + // Use a single query to get all relevant overrides $stmt = db()->prepare(" - SELECT cp.allow_permissions, cp.deny_permissions - FROM channel_permissions cp - JOIN user_roles ur ON cp.role_id = ur.role_id - WHERE ur.user_id = ? AND cp.channel_id = ? + SELECT cp.user_id, cp.role_id, cp.allow_permissions, cp.deny_permissions, r.name as role_name + FROM channel_permissions cp + LEFT JOIN roles r ON cp.role_id = r.id + LEFT JOIN user_roles ur ON cp.role_id = ur.role_id AND ur.user_id = ? + WHERE cp.channel_id = ? AND ( + cp.user_id = ? OR + ur.user_id IS NOT NULL OR + r.name = '@everyone' OR + r.name = 'Everyone' + ) "); - $stmt->execute([$user_id, $channel_id]); + $stmt->execute([$user_id, $channel_id, $user_id]); $overrides = $stmt->fetchAll(); - // Check @everyone override - $stmt = db()->prepare("SELECT id FROM roles WHERE server_id = ? AND (name = '@everyone' OR name = 'Everyone') LIMIT 1"); - $stmt->execute([$server_id]); - $everyone_role = $stmt->fetch(); - if ($everyone_role) { - $stmt = db()->prepare("SELECT allow_permissions, deny_permissions FROM channel_permissions WHERE channel_id = ? AND role_id = ?"); - $stmt->execute([$channel_id, $everyone_role['id']]); - $eo = $stmt->fetch(); - if ($eo) { - $overrides[] = $eo; + if (empty($overrides)) { + // No overrides, fallback to global permissions + return self::hasPermission($user_id, $server_id, $permission); + } + + // Resolution order (simplified but effective): + // User overrides > Role overrides > @everyone + + $user_override = null; + $role_allow = 0; + $role_deny = 0; + $everyone_override = null; + + foreach($overrides as $o) { + if ($o['user_id'] == $user_id) { + $user_override = $o; + } elseif ($o['role_name'] === '@everyone' || $o['role_name'] === 'Everyone') { + $everyone_override = $o; + } else { + $role_allow |= (int)$o['allow_permissions']; + $role_deny |= (int)$o['deny_permissions']; } } - $allow = false; - $deny = false; - foreach($overrides as $o) { - if ($o['allow_permissions'] & self::SEND_MESSAGES) $allow = true; - if ($o['deny_permissions'] & self::SEND_MESSAGES) $deny = true; + // 1. User specifically + if ($user_override) { + if ($user_override['allow_permissions'] & $permission) return true; + if ($user_override['deny_permissions'] & $permission) return false; } - if ($allow) return true; - if ($deny) return false; + // 2. Roles + if ($role_allow & $permission) return true; + if ($role_deny & $permission) return false; - return self::hasPermission($user_id, $server_id, self::SEND_MESSAGES); + // 3. @everyone + if ($everyone_override) { + if ($everyone_override['allow_permissions'] & $permission) return true; + if ($everyone_override['deny_permissions'] & $permission) return false; + } + + // Fallback to base permissions + return self::hasPermission($user_id, $server_id, $permission); } } diff --git a/index.php b/index.php index cbae0e4..1f226b3 100644 --- a/index.php +++ b/index.php @@ -719,19 +719,23 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
← Back to Forum - -
+
+ + + + + -
- + +

@@ -934,10 +938,12 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';

- + - + + +
@@ -1104,7 +1110,11 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
-
-
-
-
Send Messages
-
Allows members to send messages in this channel.
-
-
- - - - - - - - -
-
-
+
+
+
+
Send Messages
+
Allows members to send messages in this channel.
+
+
+ + + + + + + + +
+
+
+ +
+
+
+
Create Thread
+
Allows members to create new discussions.
+
+
+ + + + + + +
+
+
+ +
+
+
+
Manage Tags
+
Allows adding/modifying forum tags.
+
+
+ + + + + + +
+
+
+ +
+
+
+
Pin Threads
+
Allows pinning/unpinning threads.
+
+
+ + + + + + +
+
+
+ +
+
+
+
Lock Threads
+
Allows locking/unlocking threads.
+
+
+ + + + + + +
+
+
+ +
+
+
+
Send Messages in Threads
+
Allows posting in unlocked threads.
+
+
+ + + + + + +
+
+
diff --git a/requests.log b/requests.log index c9cab83..1815bf2 100644 --- a/requests.log +++ b/requests.log @@ -807,3 +807,114 @@ 2026-02-19 12:41:48 - GET /index.php?server_id=1&channel_id=13&thread_id=4 - POST: [] 2026-02-19 12:41:52 - GET /index.php?server_id=1&channel_id=13 - POST: [] 2026-02-19 12:41:56 - GET /index.php?server_id=1&channel_id=13&thread_id=3 - POST: [] +2026-02-19 12:43:11 - GET /index.php?server_id=1&channel_id=13&thread_id=3 - POST: [] +2026-02-19 12:43:25 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 12:43:45 - GET /index.php?server_id=1&channel_id=13&thread_id=2 - POST: [] +2026-02-19 12:44:28 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 12:44:41 - GET /index.php?server_id=1&channel_id=13&thread_id=2 - POST: [] +2026-02-19 12:52:27 - GET /?fl_project=38443 - POST: [] +2026-02-19 12:52:31 - GET /index.php?server_id=1&channel_id=13&thread_id=2 - POST: [] +2026-02-19 12:53:05 - GET /index.php - POST: [] +2026-02-19 12:53:07 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 12:53:11 - GET /index.php?server_id=1&channel_id=13&thread_id=2 - POST: [] +2026-02-19 12:53:22 - GET /index.php?server_id=1&channel_id=13&thread_id=2 - POST: [] +2026-02-19 12:53:31 - GET /index.php?server_id=1&channel_id=13&thread_id=2 - POST: [] +2026-02-19 12:53:37 - GET /index.php?server_id=1&channel_id=13&thread_id=2 - POST: [] +2026-02-19 12:53:43 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 12:53:46 - GET /index.php?server_id=1&channel_id=13&thread_id=2 - POST: [] +2026-02-19 12:53:51 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 12:53:53 - GET /index.php?server_id=1&channel_id=13&thread_id=4 - POST: [] +2026-02-19 12:54:03 - GET /index.php?server_id=1&channel_id=13&thread_id=4 - POST: [] +2026-02-19 12:54:07 - GET /index.php?server_id=1&channel_id=13&thread_id=4 - POST: [] +2026-02-19 12:54:10 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 12:54:12 - GET /index.php?server_id=1&channel_id=13&thread_id=2 - POST: [] +2026-02-19 12:54:22 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 12:54:23 - GET /index.php?server_id=1&channel_id=13&thread_id=3 - POST: [] +2026-02-19 12:54:30 - GET /index.php?server_id=1&channel_id=13&thread_id=3 - POST: [] +2026-02-19 12:54:32 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 12:54:35 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 12:54:45 - GET /index.php?server_id=1&channel_id=13&thread_id=3 - POST: [] +2026-02-19 12:54:51 - GET /index.php?server_id=1&channel_id=13&thread_id=3 - POST: [] +2026-02-19 12:54:56 - GET /index.php?server_id=1&channel_id=13&thread_id=3 - POST: [] +2026-02-19 12:55:11 - GET /index.php?server_id=1&channel_id=13&thread_id=3 - POST: [] +2026-02-19 12:55:21 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 12:55:23 - GET /index.php?server_id=1&channel_id=13&status=unresolved - POST: [] +2026-02-19 12:55:25 - GET /index.php?server_id=1&channel_id=13&status=resolved - POST: [] +2026-02-19 12:55:27 - GET /index.php?server_id=1&channel_id=13&status=all - POST: [] +2026-02-19 12:55:44 - GET /index.php?server_id=1&channel_id=13&thread_id=2&status=all - POST: [] +2026-02-19 12:56:08 - GET /index.php?server_id=1&channel_id=13&thread_id=2&status=all - POST: [] +2026-02-19 12:56:11 - GET /index.php?server_id=1&channel_id=13&thread_id=2&status=all - POST: [] +2026-02-19 12:56:13 - GET /index.php?server_id=1&channel_id=13&status=all - POST: [] +2026-02-19 12:56:19 - GET /index.php?server_id=1&channel_id=13&thread_id=2&status=all - POST: [] +2026-02-19 12:56:39 - GET /index.php?server_id=1&channel_id=13&status=all - POST: [] +2026-02-19 12:56:41 - GET /index.php?server_id=1&channel_id=13&thread_id=3&status=all - POST: [] +2026-02-19 12:56:49 - GET /index.php?server_id=1&channel_id=13&status=all - POST: [] +2026-02-19 12:57:00 - GET /index.php?server_id=1&channel_id=13&thread_id=3&status=all - POST: [] +2026-02-19 12:57:10 - GET /index.php?server_id=1&channel_id=13&status=all - POST: [] +2026-02-19 12:57:20 - GET /index.php?server_id=1&channel_id=13&status=all - POST: [] +2026-02-19 13:01:56 - GET /?fl_project=38443 - POST: [] +2026-02-19 13:03:44 - GET /?fl_project=38443 - POST: [] +2026-02-19 13:04:14 - GET /?fl_project=38443 - POST: [] +2026-02-19 13:09:42 - GET /?fl_project=38443 - POST: [] +2026-02-19 13:09:49 - GET /index.php?server_id=1&channel_id=13&thread_id=2 - POST: [] +2026-02-19 13:09:53 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 13:10:00 - GET /index.php?server_id=1&channel_id=13&status=all - POST: [] +2026-02-19 13:10:09 - GET /index.php?server_id=1&channel_id=13&thread_id=3&status=all - POST: [] +2026-02-19 13:10:12 - GET /index.php?server_id=1&channel_id=13&status=all - POST: [] +2026-02-19 13:10:17 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 13:10:19 - GET /index.php?server_id=1&channel_id=13&status=all - POST: [] +2026-02-19 13:10:34 - GET /index.php?server_id=1&channel_id=13&status=all - POST: [] +2026-02-19 13:10:46 - GET /index.php?server_id=1&channel_id=21 - POST: [] +2026-02-19 13:10:54 - GET /index.php?server_id=1&channel_id=13&status=all - POST: [] +2026-02-19 13:11:06 - GET /index.php?server_id=1&channel_id=13&status=all - POST: [] +2026-02-19 13:11:18 - GET /index.php?server_id=1&channel_id=13&status=all - POST: [] +2026-02-19 13:11:37 - GET /index.php?server_id=1&channel_id=13&status=all - POST: [] +2026-02-19 13:11:49 - GET /index.php?server_id=1&channel_id=13&status=all - POST: [] +2026-02-19 13:11:51 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 13:11:51 - GET /index.php?server_id=1&channel_id=13&thread_id=2 - POST: [] +2026-02-19 13:11:54 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 13:11:57 - GET /index.php?server_id=1&channel_id=13&thread_id=2 - POST: [] +2026-02-19 13:11:59 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 13:12:03 - GET /index.php?server_id=1&channel_id=13&thread_id=4 - POST: [] +2026-02-19 13:12:05 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 13:12:07 - GET /index.php?server_id=1&channel_id=13&thread_id=3 - POST: [] +2026-02-19 13:12:23 - GET /index.php?server_id=1&channel_id=13&thread_id=3 - POST: [] +2026-02-19 13:12:26 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 13:12:28 - GET /index.php?server_id=1&channel_id=13&thread_id=4 - POST: [] +2026-02-19 13:12:31 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 13:12:32 - GET /index.php?server_id=1&channel_id=13&thread_id=2 - POST: [] +2026-02-19 13:12:34 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 13:12:44 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 13:12:45 - GET /index.php?server_id=1&channel_id=13&thread_id=4 - POST: [] +2026-02-19 13:12:48 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 13:12:49 - GET /index.php?server_id=1&channel_id=13&thread_id=3 - POST: [] +2026-02-19 13:12:54 - GET /index.php?server_id=1&channel_id=13&thread_id=3 - POST: [] +2026-02-19 13:12:58 - GET /index.php?server_id=1&channel_id=13&thread_id=3 - POST: [] +2026-02-19 13:13:01 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 13:13:03 - GET /index.php?server_id=1&channel_id=13&thread_id=4 - POST: [] +2026-02-19 13:13:05 - GET /index.php?server_id=1&channel_id=13&thread_id=4 - POST: [] +2026-02-19 13:13:07 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 13:13:12 - GET /index.php?server_id=1&channel_id=13&thread_id=4 - POST: [] +2026-02-19 13:13:14 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 13:13:21 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 13:13:30 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 13:13:45 - GET /index.php?server_id=1&channel_id=13&thread_id=5 - POST: [] +2026-02-19 13:13:48 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 13:14:34 - GET /index.php?server_id=1&channel_id=17 - POST: [] +2026-02-19 13:14:36 - GET /index.php?server_id=1&channel_id=17 - POST: [] +2026-02-19 13:14:38 - GET /index.php?server_id=1&channel_id=17 - POST: [] +2026-02-19 13:15:06 - GET /index.php?server_id=1&channel_id=17 - POST: [] +2026-02-19 13:15:08 - GET /index.php?server_id=1&channel_id=1 - POST: [] +2026-02-19 13:15:12 - GET /index.php?server_id=1&channel_id=1 - POST: [] +2026-02-19 13:15:30 - GET /index.php - POST: [] +2026-02-19 13:15:33 - GET /index.php - POST: [] +2026-02-19 13:15:34 - GET /index.php - POST: [] +{"date":"2026-02-19 13:16:18","method":"POST","post":{"avatar_url":"","display_name":"swefpifh \u1d47\u02b0\u1da0\u02b3","theme":"dark","voice_input_device":"OPTIyYzx3bNI0gtW62vaTCb7SxzY5rNnwOw5G42w36M=","voice_output_device":"znHy1zh6U7iZkBs7ovKSXvb3r4k0jk0DBbg\/TtaWmwk=","voice_input_volume":"1","voice_output_volume":"1","voice_echo_cancellation":"1","voice_noise_suppression":"1","voice_mode":"vox","voice_ptt_key":"v","voice_vox_threshold":"0.11","dnd_mode":"1","sound_notifications":"1"},"session":{"user_id":2},"user_id":2,"db_success":true} +{"date":"2026-02-19 13:17:25","method":"POST","post":{"avatar_url":"","display_name":"swefheim","theme":"light","voice_input_device":"default","voice_output_device":"default","voice_input_volume":"1","voice_output_volume":"1","voice_echo_cancellation":"1","voice_noise_suppression":"1","voice_mode":"ptt","voice_ptt_key":"v","voice_vox_threshold":"0.06","dnd_mode":"0","sound_notifications":"0"},"session":{"user_id":3},"user_id":3,"db_success":true} +2026-02-19 13:19:38 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 13:19:56 - GET /index.php?server_id=1&channel_id=13&thread_id=2 - POST: [] +2026-02-19 13:19:59 - GET /index.php?server_id=1&channel_id=13&thread_id=2 - POST: [] +2026-02-19 13:20:01 - GET /index.php?server_id=1&channel_id=13 - POST: [] +2026-02-19 13:20:10 - GET /index.php?server_id=1&channel_id=13&thread_id=3 - POST: [] +2026-02-19 13:20:12 - GET /index.php?server_id=1&channel_id=13&thread_id=3 - POST: [] +2026-02-19 13:20:13 - GET /index.php?server_id=1&channel_id=13 - POST: []