✨ 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
207 lines
No EOL
7.4 KiB
TypeScript
207 lines
No EOL
7.4 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import { Pool } from 'pg';
|
|
|
|
const pool = new Pool({
|
|
connectionString: process.env.DATABASE_URL,
|
|
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false,
|
|
});
|
|
|
|
export default async function handler(req: NextRequest) {
|
|
if (req.method !== 'POST') {
|
|
return new NextResponse(JSON.stringify({ error: 'Method not allowed' }), {
|
|
status: 405,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
});
|
|
}
|
|
|
|
const client = await pool.connect();
|
|
|
|
try {
|
|
console.log('Setting up user authentication schema...');
|
|
|
|
// Execute the schema setup
|
|
await client.query(`
|
|
-- 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),
|
|
action VARCHAR(50),
|
|
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
|
|
await client.query(`
|
|
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
|
|
await client.query(`
|
|
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
|
|
const permissions = [
|
|
['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']
|
|
];
|
|
|
|
for (const [name, description, resource, action] of permissions) {
|
|
await client.query(`
|
|
INSERT INTO permissions (name, description, resource, action)
|
|
VALUES ($1, $2, $3, $4)
|
|
ON CONFLICT (name) DO NOTHING;
|
|
`, [name, description, resource, action]);
|
|
}
|
|
|
|
// Assign permissions to roles
|
|
// User role permissions
|
|
await client.query(`
|
|
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)
|
|
await client.query(`
|
|
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
|
|
await client.query(`
|
|
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
|
|
await client.query(`
|
|
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
|
|
await client.query(`
|
|
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();
|
|
`);
|
|
|
|
console.log('✅ User authentication schema setup completed!');
|
|
|
|
return new NextResponse(JSON.stringify({
|
|
success: true,
|
|
message: 'User authentication schema setup completed successfully'
|
|
}), {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Schema setup failed:', error);
|
|
return new NextResponse(JSON.stringify({
|
|
success: false,
|
|
error: 'Schema setup failed',
|
|
details: (error as Error).message
|
|
}), {
|
|
status: 500,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
});
|
|
} finally {
|
|
client.release();
|
|
}
|
|
}
|