Corporations / src /User.php
akra35567's picture
Upload 5 files
343aa99 verified
<?php
namespace SoftEdge;
use PDO;
use PDOException;
/**
* User Management Class
* Handles user registration, authentication, and profile management
*/
class User
{
private PDO $db;
private array $config;
public function __construct(PDO $db = null)
{
$this->config = $this->loadConfig();
$this->db = $db ?? $this->getDatabaseConnection();
}
/**
* Load configuration
*/
private function loadConfig(): array
{
return [
'db_host' => $_ENV['DB_HOST'] ?? 'localhost',
'db_name' => $_ENV['DB_NAME'] ?? 'softedge_db',
'db_user' => $_ENV['DB_USER'] ?? 'root',
'db_pass' => $_ENV['DB_PASS'] ?? '',
'jwt_secret' => $_ENV['JWT_SECRET'] ?? 'your-jwt-secret-key',
'google_client_id' => $_ENV['GOOGLE_CLIENT_ID'] ?? '',
'google_client_secret' => $_ENV['GOOGLE_CLIENT_SECRET'] ?? '',
'github_client_id' => $_ENV['GITHUB_CLIENT_ID'] ?? '',
'github_client_secret' => $_ENV['GITHUB_CLIENT_SECRET'] ?? ''
];
}
/**
* Get database connection
*/
private function getDatabaseConnection(): PDO
{
try {
$dsn = "mysql:host={$this->config['db_host']};dbname={$this->config['db_name']};charset=utf8mb4";
$pdo = new PDO($dsn, $this->config['db_user'], $this->config['db_pass']);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
return $pdo;
} catch (PDOException $e) {
error_log("Database connection failed: " . $e->getMessage());
throw new \RuntimeException('Database connection failed');
}
}
/**
* Create users table if it doesn't exist
*/
public function createTables(): void
{
try {
// Users table
$this->db->exec("
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password VARCHAR(255),
avatar VARCHAR(500),
provider VARCHAR(50) DEFAULT 'local',
provider_id VARCHAR(255),
role ENUM('user', 'admin') DEFAULT 'user',
email_verified BOOLEAN DEFAULT FALSE,
verification_token VARCHAR(255),
reset_token VARCHAR(255),
reset_expires DATETIME,
last_login DATETIME,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
");
// Page visits table
$this->db->exec("
CREATE TABLE IF NOT EXISTS page_visits (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT,
page VARCHAR(255) NOT NULL,
ip_address VARCHAR(45),
user_agent TEXT,
referrer VARCHAR(500),
session_id VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
");
// User sessions table
$this->db->exec("
CREATE TABLE IF NOT EXISTS user_sessions (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
session_token VARCHAR(255) UNIQUE NOT NULL,
ip_address VARCHAR(45),
user_agent TEXT,
expires_at DATETIME NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
");
} catch (PDOException $e) {
error_log("Table creation failed: " . $e->getMessage());
throw new \RuntimeException('Failed to create database tables');
}
}
/**
* Register a new user
*/
public function register(array $data): array
{
$this->validateRegistrationData($data);
try {
$this->db->beginTransaction();
// Check if email already exists
$stmt = $this->db->prepare("SELECT id FROM users WHERE email = ?");
$stmt->execute([$data['email']]);
if ($stmt->fetch()) {
throw new \InvalidArgumentException('Email já cadastrado');
}
// Hash password
$hashedPassword = password_hash($data['password'], PASSWORD_ARGON2ID);
// Generate verification token
$verificationToken = bin2hex(random_bytes(32));
// Insert user
$stmt = $this->db->prepare("
INSERT INTO users (name, email, password, verification_token, created_at)
VALUES (?, ?, ?, ?, NOW())
");
$stmt->execute([
$data['name'],
$data['email'],
$hashedPassword,
$verificationToken
]);
$userId = $this->db->lastInsertId();
$this->db->commit();
return [
'success' => true,
'user_id' => $userId,
'verification_token' => $verificationToken,
'message' => 'Usuário registrado com sucesso. Verifique seu email.'
];
} catch (PDOException $e) {
$this->db->rollBack();
error_log("Registration failed: " . $e->getMessage());
throw new \RuntimeException('Erro ao registrar usuário');
}
}
/**
* Authenticate user
*/
public function login(string $email, string $password): array
{
try {
$stmt = $this->db->prepare("
SELECT id, name, email, password, role, email_verified
FROM users
WHERE email = ? AND provider = 'local'
");
$stmt->execute([$email]);
$user = $stmt->fetch();
if (!$user || !password_verify($password, $user['password'])) {
throw new \InvalidArgumentException('Email ou senha incorretos');
}
if (!$user['email_verified']) {
throw new \InvalidArgumentException('Email não verificado. Verifique sua caixa de entrada.');
}
// Update last login
$stmt = $this->db->prepare("UPDATE users SET last_login = NOW() WHERE id = ?");
$stmt->execute([$user['id']]);
// Create session
$sessionToken = $this->createSession($user['id']);
// Log page visit
$this->logPageVisit($user['id'], 'login', $_SERVER['HTTP_USER_AGENT'] ?? '');
return [
'success' => true,
'user' => [
'id' => $user['id'],
'name' => $user['name'],
'email' => $user['email'],
'role' => $user['role']
],
'session_token' => $sessionToken
];
} catch (PDOException $e) {
error_log("Login failed: " . $e->getMessage());
throw new \RuntimeException('Erro ao fazer login');
}
}
/**
* Social login (Google, GitHub)
*/
public function socialLogin(string $provider, array $profile): array
{
try {
$this->db->beginTransaction();
// Check if user exists
$stmt = $this->db->prepare("
SELECT id, name, email, role, email_verified
FROM users
WHERE provider = ? AND provider_id = ?
");
$stmt->execute([$provider, $profile['id']]);
$user = $stmt->fetch();
if (!$user) {
// Create new user
$stmt = $this->db->prepare("
INSERT INTO users (name, email, avatar, provider, provider_id, email_verified, created_at)
VALUES (?, ?, ?, ?, ?, TRUE, NOW())
");
$stmt->execute([
$profile['name'],
$profile['email'],
$profile['avatar'] ?? null,
$provider,
$profile['id']
]);
$userId = $this->db->lastInsertId();
$user = [
'id' => $userId,
'name' => $profile['name'],
'email' => $profile['email'],
'role' => 'user',
'email_verified' => true
];
}
// Update last login
$stmt = $this->db->prepare("UPDATE users SET last_login = NOW() WHERE id = ?");
$stmt->execute([$user['id']]);
// Create session
$sessionToken = $this->createSession($user['id']);
// Log page visit
$this->logPageVisit($user['id'], 'social_login', $_SERVER['HTTP_USER_AGENT'] ?? '');
$this->db->commit();
return [
'success' => true,
'user' => $user,
'session_token' => $sessionToken
];
} catch (PDOException $e) {
$this->db->rollBack();
error_log("Social login failed: " . $e->getMessage());
throw new \RuntimeException('Erro ao fazer login social');
}
}
/**
* Create user session
*/
private function createSession(int $userId): string
{
$sessionToken = bin2hex(random_bytes(32));
$expiresAt = date('Y-m-d H:i:s', strtotime('+24 hours'));
$stmt = $this->db->prepare("
INSERT INTO user_sessions (user_id, session_token, ip_address, user_agent, expires_at, created_at)
VALUES (?, ?, ?, ?, ?, NOW())
");
$stmt->execute([
$userId,
$sessionToken,
$_SERVER['REMOTE_ADDR'] ?? '',
$_SERVER['HTTP_USER_AGENT'] ?? '',
$expiresAt
]);
return $sessionToken;
}
/**
* Validate session
*/
public function validateSession(string $sessionToken): ?array
{
try {
$stmt = $this->db->prepare("
SELECT u.id, u.name, u.email, u.role, u.email_verified, s.expires_at
FROM user_sessions s
JOIN users u ON s.user_id = u.id
WHERE s.session_token = ? AND s.expires_at > NOW()
");
$stmt->execute([$sessionToken]);
$result = $stmt->fetch();
return $result ?: null;
} catch (PDOException $e) {
error_log("Session validation failed: " . $e->getMessage());
return null;
}
}
/**
* Log page visit
*/
public function logPageVisit(?int $userId, string $page, string $userAgent = ''): void
{
try {
$stmt = $this->db->prepare("
INSERT INTO page_visits (user_id, page, ip_address, user_agent, referrer, session_id, created_at)
VALUES (?, ?, ?, ?, ?, ?, NOW())
");
$stmt->execute([
$userId,
$page,
$_SERVER['REMOTE_ADDR'] ?? '',
$userAgent,
$_SERVER['HTTP_REFERER'] ?? '',
session_id()
]);
} catch (PDOException $e) {
error_log("Page visit logging failed: " . $e->getMessage());
}
}
/**
* Get admin statistics
*/
public function getAdminStats(): array
{
try {
// Total users
$stmt = $this->db->query("SELECT COUNT(*) as total FROM users");
$totalUsers = $stmt->fetch()['total'];
// Total page visits
$stmt = $this->db->query("SELECT COUNT(*) as total FROM page_visits");
$totalVisits = $stmt->fetch()['total'];
// Recent visits (last 30 days)
$stmt = $this->db->prepare("
SELECT COUNT(*) as total
FROM page_visits
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
");
$stmt->execute();
$recentVisits = $stmt->fetch()['total'];
// Top pages
$stmt = $this->db->prepare("
SELECT page, COUNT(*) as visits
FROM page_visits
GROUP BY page
ORDER BY visits DESC
LIMIT 10
");
$stmt->execute();
$topPages = $stmt->fetchAll();
// Recent users
$stmt = $this->db->prepare("
SELECT id, name, email, created_at
FROM users
ORDER BY created_at DESC
LIMIT 10
");
$stmt->execute();
$recentUsers = $stmt->fetchAll();
return [
'total_users' => $totalUsers,
'total_visits' => $totalVisits,
'recent_visits' => $recentVisits,
'top_pages' => $topPages,
'recent_users' => $recentUsers
];
} catch (PDOException $e) {
error_log("Admin stats failed: " . $e->getMessage());
return [];
}
}
/**
* Validate registration data
*/
private function validateRegistrationData(array $data): void
{
$required = ['name', 'email', 'password'];
foreach ($required as $field) {
if (empty($data[$field])) {
throw new \InvalidArgumentException("Campo {$field} é obrigatório");
}
}
if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException('Email inválido');
}
if (strlen($data['name']) < 2) {
throw new \InvalidArgumentException('Nome deve ter pelo menos 2 caracteres');
}
if (strlen($data['password']) < 8) {
throw new \InvalidArgumentException('Senha deve ter pelo menos 8 caracteres');
}
}
/**
* Check if user is admin
*/
public function isAdmin(int $userId): bool
{
try {
$stmt = $this->db->prepare("SELECT role FROM users WHERE id = ?");
$stmt->execute([$userId]);
$user = $stmt->fetch();
return $user && $user['role'] === 'admin';
} catch (PDOException $e) {
error_log("Admin check failed: " . $e->getMessage());
return false;
}
}
}