Prepare secure config and reliable workout sync

This commit is contained in:
Hamza-Ayed
2026-10-04 00:33:44 +03:00
parent 05f1c9ec6b
commit e7f3c777d1
17 changed files with 553 additions and 119 deletions
+96 -6
View File
@@ -10,14 +10,17 @@ USE fitness_app;
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
uuid CHAR(36) UNIQUE NOT NULL COMMENT 'Unique identifier for the user',
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL COMMENT 'bcrypt hash',
api_key VARCHAR(64) UNIQUE NOT NULL COMMENT 'API key for client authentication',
api_secret VARCHAR(64) NOT NULL COMMENT 'Secret for HMAC signature',
phone_e164 VARCHAR(16) UNIQUE COMMENT 'Verified phone number in E.164 format',
phone_verified_at DATETIME NULL,
username VARCHAR(50) UNIQUE,
email VARCHAR(100) UNIQUE,
password_hash VARCHAR(255) NULL COMMENT 'Optional legacy password hash',
api_key VARCHAR(64) UNIQUE COMMENT 'Optional legacy API key',
api_secret VARCHAR(64) NULL COMMENT 'Optional legacy HMAC secret',
full_name VARCHAR(100),
avatar_url VARCHAR(255),
is_active BOOLEAN DEFAULT TRUE,
account_role ENUM('member', 'owner', 'content_manager', 'support') NOT NULL DEFAULT 'member',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_uuid (uuid),
@@ -25,11 +28,97 @@ CREATE TABLE IF NOT EXISTS users (
INDEX idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE app_settings (
setting_key VARCHAR(100) PRIMARY KEY,
setting_value JSON NOT NULL,
is_public BOOLEAN NOT NULL DEFAULT FALSE,
revision BIGINT UNSIGNED NOT NULL DEFAULT 1,
updated_by INT NULL,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_settings_public (is_public, setting_key),
CONSTRAINT fk_settings_editor FOREIGN KEY (updated_by) REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE app_setting_audit (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
setting_key VARCHAR(100) NOT NULL,
previous_value JSON NULL,
new_value JSON NOT NULL,
actor_user_id INT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX idx_settings_audit_key_time (setting_key, created_at),
INDEX idx_settings_audit_actor_time (actor_user_id, created_at),
CONSTRAINT fk_settings_audit_actor FOREIGN KEY (actor_user_id) REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Phone OTP challenges contain keyed digests, never the plaintext code.
CREATE TABLE otp_challenges (
challenge_uuid CHAR(36) PRIMARY KEY,
phone_e164 VARCHAR(16) NOT NULL,
purpose ENUM('register', 'login', 'change_phone') NOT NULL,
code_digest CHAR(64) NOT NULL,
attempt_count TINYINT UNSIGNED NOT NULL DEFAULT 0,
max_attempts TINYINT UNSIGNED NOT NULL DEFAULT 5,
request_ip_digest CHAR(64) NULL,
device_uuid CHAR(36) NULL,
expires_at DATETIME NOT NULL,
consumed_at DATETIME NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX idx_otp_phone_created (phone_e164, created_at),
INDEX idx_otp_expiry (expires_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE auth_rate_limit_buckets (
bucket_digest CHAR(64) PRIMARY KEY,
bucket_type ENUM('phone', 'ip', 'device') NOT NULL,
window_started_at DATETIME NOT NULL,
request_count INT UNSIGNED NOT NULL DEFAULT 0,
blocked_until DATETIME NULL,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_rate_bucket_expiry (updated_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE user_devices (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
device_uuid CHAR(36) NOT NULL,
platform ENUM('ios', 'android', 'web') NOT NULL,
public_key TEXT NULL,
key_fingerprint CHAR(64) NULL UNIQUE,
key_algorithm VARCHAR(32) NULL,
display_name VARCHAR(80) NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
last_seen_at DATETIME NULL,
revoked_at DATETIME NULL,
UNIQUE KEY uq_device_user_uuid (user_id, device_uuid),
INDEX idx_devices_user_active (user_id, revoked_at),
CONSTRAINT fk_devices_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE auth_sessions (
session_uuid CHAR(36) PRIMARY KEY,
family_uuid CHAR(36) NOT NULL,
user_id INT NOT NULL,
device_uuid CHAR(36) NULL,
refresh_token_digest CHAR(64) NOT NULL UNIQUE,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
last_used_at DATETIME NULL,
expires_at DATETIME NOT NULL,
revoked_at DATETIME NULL,
replaced_by CHAR(36) NULL,
INDEX idx_sessions_user_active (user_id, revoked_at, expires_at),
INDEX idx_sessions_family (family_uuid),
CONSTRAINT fk_sessions_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
CONSTRAINT fk_sessions_device FOREIGN KEY (user_id, device_uuid)
REFERENCES user_devices(user_id, device_uuid) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Workouts Table
CREATE TABLE IF NOT EXISTS workouts (
id INT AUTO_INCREMENT PRIMARY KEY,
workout_uuid CHAR(36) UNIQUE NOT NULL COMMENT 'Unique identifier for the workout',
user_id INT NOT NULL,
client_workout_uuid CHAR(36) NULL COMMENT 'Client-generated idempotency key',
workout_type ENUM('running', 'walking') NOT NULL,
distance_meters INT NOT NULL COMMENT 'Total distance in meters',
duration_seconds INT NOT NULL COMMENT 'Total duration in seconds',
@@ -59,7 +148,8 @@ CREATE TABLE IF NOT EXISTS workouts (
INDEX idx_workout_type (workout_type),
INDEX idx_synced_at (synced_at),
INDEX idx_created_at (created_at),
INDEX idx_user_created (user_id, created_at)
INDEX idx_user_created (user_id, created_at),
UNIQUE KEY uq_user_client_workout (user_id, client_workout_uuid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Workout Segments Table (for detailed route tracking if needed)