Address changes.

This commit is contained in:
Cody Cook 2025-02-22 17:20:19 -08:00
commit 635b3ddcbc
59 changed files with 7249 additions and 2745 deletions

View file

@ -2,29 +2,27 @@
namespace DJMixHosting;
use DateMalformedStringException;
use DateTime;
use Exception;
use Random\RandomException;
use Aws\Ses\SesClient;
use Aws\Exception\AwsException;
class User {
private $db;
private $id;
private $username;
private $firstName;
private $lastName;
private $email;
private $location;
private $bio;
private $created;
private $updated;
private $verified;
private $role;
private $img = "";
private $api_key;
private Database $db;
private string $id;
private string $username;
private string $firstName;
private string $lastName;
private string $email;
private string $location;
private string $bio;
private string $created;
private string $updated;
private bool $verified;
private string $role;
private string $img = "";
private string $api_key;
public function __construct($db, $id = null) {
$this->db = $db;
@ -36,7 +34,8 @@ class User {
/**
* Load user data from the database by id.
*/
private function loadUserById($id) {
private function loadUserById($id): void
{
$stmt = $this->db->prepare("SELECT * FROM users WHERE id = ?");
$stmt->bind_param("i", $id);
$stmt->execute();
@ -56,9 +55,9 @@ class User {
$this->updated = $user_data['lastupdated'];
$this->role = $user_data['isAdmin'] ? 'admin' : 'user';
// These fields are not in your table; assign defaults or remove them.
$this->location = "";
$this->bio = "";
// New fields loaded from the database
$this->location = $user_data['location'] ?? "";
$this->bio = $user_data['bio'] ?? "";
}
}
@ -69,7 +68,7 @@ class User {
*/
public function newUser(string $username, string $password, string $email, string $firstName, string $lastName): int {
if ($this->check_existing_user($username, $email)) {
throw new \Random\RandomException("User already exists");
throw new RandomException("User already exists");
}
$this->username = $username;
$this->email = $email;
@ -87,8 +86,8 @@ class User {
$this->img = "";
$this->api_key = bin2hex(random_bytes(32));
$stmt = $this->db->prepare("INSERT INTO users (username, password, email, firstName, lastName, img, emailVerified) VALUES (?, ?, ?, ?, ?, '', 0)");
$stmt->bind_param("sssss", $this->username, $password_hashed, $this->email, $this->firstName, $this->lastName);
$stmt = $this->db->prepare("INSERT INTO users (username, password, email, firstName, lastName, img, emailVerified, apiKey, location, bio) VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?, ?)");
$stmt->bind_param("sssssssss", $this->username, $password_hashed, $this->email, $this->firstName, $this->lastName, $this->img, $this->api_key, $this->location, $this->bio);
$stmt->execute();
$userId = $stmt->insert_id;
$stmt->close();
@ -97,8 +96,8 @@ class User {
return $userId;
}
private function check_existing_user($username, $email) {
private function check_existing_user($username, $email): false|array|null
{
$stmt = $this->db->prepare("SELECT * FROM users WHERE username = ? OR email = ?");
$stmt->bind_param("ss", $username, $email);
$stmt->execute();
@ -113,8 +112,10 @@ class User {
*
* Returns the user data array if successful. In case of failure,
* a string error message is returned.
* @throws DateMalformedStringException
*/
public function login($email, $password) {
public function login($email, $password): array|string
{
// Retrieve user record by email
$stmt = $this->db->prepare("SELECT * FROM users WHERE email = ?");
$stmt->bind_param("s", $email);
@ -161,6 +162,7 @@ class User {
* Update (or create) a record in the login_attempts table for a failed attempt.
* If attempts reach 3, set a lockout that doubles each time.
* Returns the current number of attempts.
* @throws DateMalformedStringException
*/
private function updateFailedAttempt($email) {
// Check for an existing record
@ -187,31 +189,27 @@ class User {
$stmt = $this->db->prepare("UPDATE login_attempts SET attempts = ?, lockouts = ?, last_attempt = NOW(), lockout_until = ? WHERE email = ?");
$lockout_until_str = $lockout_until->format('Y-m-d H:i:s');
$stmt->bind_param("iiss", $attempts, $lockouts, $lockout_until_str, $email);
$stmt->execute();
$stmt->close();
} else {
$stmt = $this->db->prepare("UPDATE login_attempts SET attempts = ?, last_attempt = NOW() WHERE email = ?");
$stmt->bind_param("is", $attempts, $email);
$stmt->execute();
$stmt->close();
}
return $attempts;
} else {
// Create a new record for this email
$attempts = 1;
$lockouts = 0;
$stmt = $this->db->prepare("INSERT INTO login_attempts (email, attempts, lockouts, last_attempt) VALUES (?, ?, ?, NOW())");
$stmt->bind_param("sii", $email, $attempts, $lockouts);
$stmt->execute();
$stmt->close();
return $attempts;
}
$stmt->execute();
$stmt->close();
return $attempts;
}
/**
* Reset the login_attempts record for the given email.
*/
private function resetLoginAttempts($email) {
private function resetLoginAttempts($email): void
{
$stmt = $this->db->prepare("DELETE FROM login_attempts WHERE email = ?");
$stmt->bind_param("s", $email);
$stmt->execute();
@ -224,12 +222,12 @@ class User {
* @param string $newEmail
* @param array $config Configuration array for AWS SES and app settings.
* @return string Success message.
* @throws \Exception on validation or email-sending failure.
* @throws Exception on validation or email-sending failure.
*/
public function updateEmail(string $newEmail, array $config): string {
$newEmail = filter_var($newEmail, FILTER_VALIDATE_EMAIL);
if (!$newEmail) {
throw new \Exception("Invalid email format.");
throw new Exception("Invalid email format.");
}
// Update email and mark as unverified.
@ -258,7 +256,11 @@ class User {
return "Email updated. A verification email has been sent to your new address.";
}
public function updateName($firstName, $lastName) {
/**
* @throws Exception
*/
public function updateName($firstName, $lastName): string
{
// Update the user's name.
$stmt = $this->db->prepare("UPDATE users SET firstName = ?, lastName = ? WHERE id = ?");
$stmt->bind_param("ssi", $firstName, $lastName, $this->id);
@ -274,7 +276,11 @@ class User {
return "Name updated successfully.";
}
public function updatePassword($currentPassword, $newPassword, $confirmPassword) {
/**
* @throws Exception
*/
public function updatePassword($currentPassword, $newPassword, $confirmPassword): string
{
// Retrieve the current password hash.
$stmt = $this->db->prepare("SELECT password FROM users WHERE id = ?");
$stmt->bind_param("i", $this->id);
@ -307,7 +313,11 @@ class User {
return "Password updated successfully.";
}
public function updateUsername($newUsername) {
/**
* @throws Exception
*/
public function updateUsername($newUsername): string
{
// Validate username format.
if (!preg_match('/^[a-zA-Z0-9_]{3,25}$/', $newUsername)) {
throw new Exception("Invalid username format.");
@ -339,7 +349,7 @@ class User {
*
* @param string $verification_code The code submitted by the user.
* @return string Success message.
* @throws \Exception If the code is invalid or expired.
* @throws Exception If the code is invalid or expired.
*/
public function verifyEmail(string $verification_code): string {
// Look up the verification record for this user and code
@ -351,14 +361,14 @@ class User {
$stmt->close();
if (!$record) {
throw new \Exception("Invalid verification code.");
throw new Exception("Invalid verification code.");
}
// Check if the verification code has expired
$current_time = new \DateTime();
$expires_at = new \DateTime($record['expires_at']);
$current_time = new DateTime();
$expires_at = new DateTime($record['expires_at']);
if ($current_time > $expires_at) {
throw new \Exception("Verification code has expired. Please request a new one.");
throw new Exception("Verification code has expired. Please request a new one.");
}
// Update the user's record to mark the email as verified
@ -379,18 +389,50 @@ class User {
return "Email verified successfully.";
}
// New setters for location and bio
/**
* @throws Exception
*/
public function setLocation(string $location): string {
$stmt = $this->db->prepare("UPDATE users SET location = ? WHERE id = ?");
$stmt->bind_param("si", $location, $this->id);
if ($stmt->execute()) {
$this->location = $location;
$stmt->close();
return "Location updated successfully.";
}
$stmt->close();
throw new Exception("Failed to update location.");
}
/**
* @throws Exception
*/
public function setBio(string $bio): string {
$stmt = $this->db->prepare("UPDATE users SET bio = ? WHERE id = ?");
$stmt->bind_param("si", $bio, $this->id);
if ($stmt->execute()) {
$this->bio = $bio;
$stmt->close();
return "Bio updated successfully.";
}
$stmt->close();
throw new Exception("Failed to update bio.");
}
// Getter methods
public function getId() { return $this->id; }
public function getUsername() { return $this->username; }
public function getFirstName() { return $this->firstName; }
public function getLastName() { return $this->lastName; }
public function getEmail() { return $this->email; }
public function getLocation() { return $this->location; }
public function getBio() { return $this->bio; }
public function getCreated() { return $this->created; }
public function getUpdated() { return $this->updated; }
public function getVerified() { return $this->verified; }
public function getRole() { return $this->role; }
public function getImg() { return $this->img; }
public function getApiKey() { return $this->api_key; }
public function getId(): string { return $this->id; }
public function getUsername(): string { return $this->username; }
public function getFirstName(): string { return $this->firstName; }
public function getLastName(): string { return $this->lastName; }
public function getEmail(): string { return $this->email; }
public function getLocation(): string { return $this->location; }
public function getBio(): string { return $this->bio; }
public function getCreated(): string { return $this->created; }
public function getUpdated(): string { return $this->updated; }
public function getVerified(): string { return $this->verified; }
public function getRole(): string { return $this->role; }
public function getImg(): string { return $this->img; }
public function getApiKey(): string { return $this->api_key; }
}