+
+
Новый тикет
+
+
= htmlspecialchars($error) ?>
+
+
+
+
+
+
diff --git a/db/config.php b/db/config.php
index e512965..e335269 100644
--- a/db/config.php
+++ b/db/config.php
@@ -1,17 +1,22 @@
PDO::ERRMODE_EXCEPTION,
- PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
- ]);
+ try {
+ $pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [
+ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
+ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
+ ]);
+ } catch (PDOException $e) {
+ error_log($e->getMessage());
+ die('Database connection failed.');
+ }
}
return $pdo;
-}
+}
\ No newline at end of file
diff --git a/db/migrations/init.sql b/db/migrations/init.sql
new file mode 100644
index 0000000..ed2d875
--- /dev/null
+++ b/db/migrations/init.sql
@@ -0,0 +1,40 @@
+CREATE TABLE IF NOT EXISTS users (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ username VARCHAR(255) UNIQUE NOT NULL,
+ password_hash VARCHAR(255) NOT NULL,
+ role ENUM('curator', 'helper', 'user') DEFAULT 'user',
+ status ENUM('active', 'blocked') DEFAULT 'active',
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+CREATE TABLE IF NOT EXISTS tickets (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ user_id INT NOT NULL,
+ helper_id INT,
+ title VARCHAR(255) NOT NULL,
+ description TEXT NOT NULL,
+ category VARCHAR(255),
+ priority ENUM('low', 'medium', 'high') DEFAULT 'medium',
+ status ENUM('open', 'in_progress', 'awaiting_response', 'closed') DEFAULT 'open',
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ FOREIGN KEY (user_id) REFERENCES users(id),
+ FOREIGN KEY (helper_id) REFERENCES users(id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+CREATE TABLE IF NOT EXISTS messages (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ ticket_id INT NOT NULL,
+ user_id INT NOT NULL,
+ message TEXT,
+ file_path VARCHAR(255),
+ file_name VARCHAR(255),
+ file_type VARCHAR(255),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (ticket_id) REFERENCES tickets(id) ON DELETE CASCADE,
+ FOREIGN KEY (user_id) REFERENCES users(id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+-- Seed initial curator
+-- Password is 'admin123'
+INSERT IGNORE INTO users (username, password_hash, role) VALUES ('admin', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'curator');
diff --git a/healthz.php b/healthz.php
new file mode 100644
index 0000000..ff4d2cf
--- /dev/null
+++ b/healthz.php
@@ -0,0 +1,3 @@
+prepare($sql);
+$stmt->execute($params);
+$tickets = $stmt->fetchAll();
+
+$openTicketsCount = 0;
+if ($user['role'] === 'user') {
+ foreach ($tickets as $t) {
+ if ($t['status'] !== 'closed') $openTicketsCount++;
+ }
+}
+
+// Stats for Curator
+$stats = [];
+if ($user['role'] === 'curator') {
+ $stats['total'] = db()->query("SELECT COUNT(*) FROM tickets")->fetchColumn();
+ $stats['active'] = db()->query("SELECT COUNT(*) FROM tickets WHERE status != 'closed'")->fetchColumn();
+ // Avg response time (simplified: difference between ticket creation and first message from a helper/curator)
+ $stats['avg_response'] = db()->query("
+ SELECT ROUND(AVG(TIMESTAMPDIFF(HOUR, t.created_at, m.created_at)), 1)
+ FROM tickets t
+ JOIN messages m ON t.id = m.ticket_id
+ JOIN users u ON m.user_id = u.id
+ WHERE u.role != 'user'
+ AND m.id = (
+ SELECT MIN(m2.id)
+ FROM messages m2
+ JOIN users u2 ON m2.user_id = u2.id
+ WHERE m2.ticket_id = t.id AND u2.role != 'user'
+ )
+ ")->fetchColumn() ?: '...';
+}
+
+function getStatusLabel($status) {
+ $labels = [
+ 'open' => 'Открыт',
+ 'in_progress' => 'В работе',
+ 'awaiting_response' => 'Ожидает ответа',
+ 'closed' => 'Закрыт'
+ ];
+ return $labels[$status] ?? $status;
+}
+
+function getPriorityLabel($priority) {
+ $labels = [
+ 'low' => 'Низкий',
+ 'medium' => 'Средний',
+ 'high' => 'Высокий'
+ ];
+ return $labels[$priority] ?? $priority;
+}
?>
-
+