deckhearth/scripts/user-auth-schema.sql
Randall Stillwell dc95d0d76d Implement complete user authentication and admin panel system
 Features Added:
- User registration and login with JWT authentication
- Role-based access control (user/admin)
- Comprehensive admin panel with user management
- Password hashing with bcrypt
- Session management and token storage
- Protected routes and public routes
- Modern login/register forms with validation

🗄️ Database Schema:
- Users table with profile information
- Roles and permissions system
- User-role junction tables
- Session management tables
- Database triggers and indexes

🎨 UI/UX Improvements:
- Updated navigation with user menu
- Admin badge and access controls
- Responsive authentication forms
- Loading states and error handling
- Role-based UI elements

🔧 API Endpoints:
- /api/auth/login - User authentication
- /api/auth/register - User registration
- /api/admin/users - User management (admin only)
- /api/setup-auth - Database schema setup

🚀 Admin Panel Features:
- User listing with search and pagination
- Role assignment (user/admin)
- User activation/deactivation
- System dashboard with stats
- Card management placeholder
- Real-time user management
2025-07-21 21:06:38 -05:00

138 lines
No EOL
5.2 KiB
PL/PgSQL

-- User Authentication and Role Management Schema
-- Users table
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
first_name VARCHAR(50),
last_name VARCHAR(50),
avatar_url TEXT,
is_active BOOLEAN DEFAULT true,
email_verified BOOLEAN DEFAULT false,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
last_login TIMESTAMP WITH TIME ZONE
);
-- Roles table
CREATE TABLE IF NOT EXISTS roles (
id SERIAL PRIMARY KEY,
name VARCHAR(50) UNIQUE NOT NULL,
description TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- User roles junction table (many-to-many)
CREATE TABLE IF NOT EXISTS user_roles (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
role_id INTEGER REFERENCES roles(id) ON DELETE CASCADE,
assigned_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
assigned_by INTEGER REFERENCES users(id),
UNIQUE(user_id, role_id)
);
-- Permissions table
CREATE TABLE IF NOT EXISTS permissions (
id SERIAL PRIMARY KEY,
name VARCHAR(100) UNIQUE NOT NULL,
description TEXT,
resource VARCHAR(50), -- cards, users, decks, etc.
action VARCHAR(50), -- create, read, update, delete
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- Role permissions junction table
CREATE TABLE IF NOT EXISTS role_permissions (
id SERIAL PRIMARY KEY,
role_id INTEGER REFERENCES roles(id) ON DELETE CASCADE,
permission_id INTEGER REFERENCES permissions(id) ON DELETE CASCADE,
UNIQUE(role_id, permission_id)
);
-- Sessions table for JWT token management
CREATE TABLE IF NOT EXISTS user_sessions (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
token_hash VARCHAR(255) NOT NULL,
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
last_used TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
user_agent TEXT,
ip_address INET
);
-- Update existing tables to include user ownership
ALTER TABLE user_collections ADD COLUMN IF NOT EXISTS created_by INTEGER REFERENCES users(id);
ALTER TABLE user_decks ADD COLUMN IF NOT EXISTS created_by INTEGER REFERENCES users(id);
-- Insert default roles
INSERT INTO roles (name, description) VALUES
('user', 'Standard user with basic permissions'),
('admin', 'Administrator with full system access')
ON CONFLICT (name) DO NOTHING;
-- Insert default permissions
INSERT INTO permissions (name, description, resource, action) VALUES
('cards.read', 'View cards', 'cards', 'read'),
('cards.create', 'Create new cards', 'cards', 'create'),
('cards.update', 'Update existing cards', 'cards', 'update'),
('cards.delete', 'Delete cards', 'cards', 'delete'),
('collections.read', 'View collections', 'collections', 'read'),
('collections.create', 'Create collections', 'collections', 'create'),
('collections.update', 'Update collections', 'collections', 'update'),
('collections.delete', 'Delete collections', 'collections', 'delete'),
('decks.read', 'View decks', 'decks', 'read'),
('decks.create', 'Create decks', 'decks', 'create'),
('decks.update', 'Update decks', 'decks', 'update'),
('decks.delete', 'Delete decks', 'decks', 'delete'),
('users.read', 'View users', 'users', 'read'),
('users.create', 'Create users', 'users', 'create'),
('users.update', 'Update users', 'users', 'update'),
('users.delete', 'Delete users', 'users', 'delete'),
('admin.access', 'Access admin panel', 'admin', 'access')
ON CONFLICT (name) DO NOTHING;
-- Assign permissions to roles
-- User role permissions
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r, permissions p
WHERE r.name = 'user'
AND p.name IN (
'cards.read',
'collections.read', 'collections.create', 'collections.update', 'collections.delete',
'decks.read', 'decks.create', 'decks.update', 'decks.delete'
)
ON CONFLICT (role_id, permission_id) DO NOTHING;
-- Admin role permissions (all permissions)
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r, permissions p
WHERE r.name = 'admin'
ON CONFLICT (role_id, permission_id) DO NOTHING;
-- Create indexes for performance
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
CREATE INDEX IF NOT EXISTS idx_user_roles_user_id ON user_roles(user_id);
CREATE INDEX IF NOT EXISTS idx_user_sessions_user_id ON user_sessions(user_id);
CREATE INDEX IF NOT EXISTS idx_user_sessions_expires_at ON user_sessions(expires_at);
-- Create updated_at trigger function
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ language 'plpgsql';
-- Add updated_at triggers
DROP TRIGGER IF EXISTS update_users_updated_at ON users;
CREATE TRIGGER update_users_updated_at
BEFORE UPDATE ON users
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();