Files
server-manager/src/Models/User.php
Agent 1741545318 Add soft deletes via status column
- Add status ENUM('active','disabled','deleted') to 8 tables
- Change all physical DELETE to UPDATE status='deleted'
- All SELECT queries filter by status='active'
- toggleActive now alternates between active/disabled status
- Exceptions: monitoring_history, rate_limits, sessions keep physical purge
- audit_logs remains immutable
2026-06-07 17:26:29 -04:00

129 lines
3.6 KiB
PHP
Executable File

<?php
declare(strict_types=1);
namespace ServerManager\Models;
use ServerManager\Core\Database;
use ServerManager\Core\Security;
class User
{
private Database $db;
private Security $security;
public function __construct()
{
$this->db = Database::getInstance();
$this->security = new Security();
}
public function findById(int $id): ?array
{
return $this->db->fetch('SELECT * FROM users WHERE id = ? AND status = \'active\'', [$id]);
}
public function findByUsername(string $username): ?array
{
return $this->db->fetch('SELECT * FROM users WHERE username = ? AND status = \'active\'', [$username]);
}
public function findByEmail(string $email): ?array
{
return $this->db->fetch('SELECT * FROM users WHERE email = ? AND status = \'active\'', [$email]);
}
public function authenticate(string $username, string $password): ?array
{
$user = $this->findByUsername($username);
if (!$user) {
return null;
}
if ($user['status'] !== 'active' || !$user['is_active']) {
return null;
}
if (!$this->security->verifyPassword($password, $user['password_hash'])) {
return null;
}
$this->updateLastLogin($user['id']);
return $user;
}
public function create(array $data): int
{
$data['password_hash'] = $this->security->hashPassword($data['password']);
unset($data['password']);
$data['created_at'] = date('Y-m-d H:i:s');
$data['updated_at'] = date('Y-m-d H:i:s');
if (empty($data['api_token'])) {
$data['api_token'] = $this->security->generateApiToken();
}
return $this->db->insert('users', $data);
}
public function update(int $id, array $data): bool
{
if (isset($data['password'])) {
$data['password_hash'] = $this->security->hashPassword($data['password']);
unset($data['password']);
}
$data['updated_at'] = date('Y-m-d H:i:s');
return $this->db->update('users', $data, 'id = ?', [$id]) > 0;
}
public function delete(int $id): bool
{
return $this->db->update('users', [
'status' => 'deleted',
'is_active' => 0,
'updated_at' => date('Y-m-d H:i:s'),
], 'id = ?', [$id]) > 0;
}
public function getAll(int $page = 1, int $perPage = 20): array
{
$offset = ($page - 1) * $perPage;
$total = $this->db->fetch("SELECT COUNT(*) as total FROM users WHERE status = 'active'");
$users = $this->db->fetchAll(
'SELECT id, username, email, role, status, is_active, last_login_at, created_at
FROM users WHERE status = \'active\' ORDER BY created_at DESC LIMIT ? OFFSET ?',
[$perPage, $offset]
);
return [
'data' => $users,
'total' => (int) ($total['total'] ?? 0),
'page' => $page,
'per_page' => $perPage,
'total_pages' => ceil((int) ($total['total'] ?? 0) / $perPage),
];
}
public function findByApiToken(string $token): ?array
{
return $this->db->fetch('SELECT * FROM users WHERE api_token = ? AND status = \'active\' AND is_active = 1', [$token]);
}
public function updateLastLogin(int $id): void
{
$this->db->update('users', ['last_login_at' => date('Y-m-d H:i:s')], 'id = ?', [$id]);
}
public function countAll(): int
{
$result = $this->db->fetch("SELECT COUNT(*) as total FROM users WHERE status = 'active'");
return (int) ($result['total'] ?? 0);
}
}