Add debugging tools and optimize auth endpoints
🔧 Debugging Improvements: - Optimize setup-auth endpoint to avoid timeouts - Add table existence checks before operations - Simplify permission system for faster setup - Add environment variable validation - Create test-db endpoint for connection debugging 🚀 Performance Optimizations: - Break down large SQL queries into smaller chunks - Add early returns for already-setup scenarios - Reduce permission complexity during initial setup - Better error messages for troubleshooting 🧪 New Debug Endpoints: - /api/test-db - Test database connectivity - /api/setup-auth - Optimized schema setup These changes should resolve timeout issues and provide better debugging information for authentication problems.
This commit is contained in:
parent
07b3dda6fa
commit
0531f25cbb
3 changed files with 162 additions and 64 deletions
|
|
@ -24,6 +24,21 @@ export default async function handler(req: NextRequest) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check environment variables
|
||||||
|
if (!process.env.DATABASE_URL) {
|
||||||
|
return new NextResponse(JSON.stringify({
|
||||||
|
error: 'Database configuration missing',
|
||||||
|
details: 'DATABASE_URL environment variable not set'
|
||||||
|
}), {
|
||||||
|
status: 500,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process.env.JWT_SECRET) {
|
||||||
|
console.warn('JWT_SECRET not set, using fallback');
|
||||||
|
}
|
||||||
|
|
||||||
const client = await pool.connect();
|
const client = await pool.connect();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -62,6 +77,25 @@ export default async function handler(req: NextRequest) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if users table exists
|
||||||
|
const tableCheck = await client.query(`
|
||||||
|
SELECT EXISTS (
|
||||||
|
SELECT FROM information_schema.tables
|
||||||
|
WHERE table_schema = 'public'
|
||||||
|
AND table_name = 'users'
|
||||||
|
);
|
||||||
|
`);
|
||||||
|
|
||||||
|
if (!tableCheck.rows[0].exists) {
|
||||||
|
return new NextResponse(JSON.stringify({
|
||||||
|
error: 'Database not initialized',
|
||||||
|
details: 'Please run the setup-auth endpoint first'
|
||||||
|
}), {
|
||||||
|
status: 500,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Check if user already exists
|
// Check if user already exists
|
||||||
const existingUser = await client.query(
|
const existingUser = await client.query(
|
||||||
'SELECT id FROM users WHERE username = $1 OR email = $2',
|
'SELECT id FROM users WHERE username = $1 OR email = $2',
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,28 @@ export default async function handler(req: NextRequest) {
|
||||||
try {
|
try {
|
||||||
console.log('Setting up user authentication schema...');
|
console.log('Setting up user authentication schema...');
|
||||||
|
|
||||||
// Execute the schema setup
|
// Check if users table already exists
|
||||||
|
const tableCheck = await client.query(`
|
||||||
|
SELECT EXISTS (
|
||||||
|
SELECT FROM information_schema.tables
|
||||||
|
WHERE table_schema = 'public'
|
||||||
|
AND table_name = 'users'
|
||||||
|
);
|
||||||
|
`);
|
||||||
|
|
||||||
|
if (tableCheck.rows[0].exists) {
|
||||||
|
return new NextResponse(JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: 'User authentication schema already exists',
|
||||||
|
already_setup: true
|
||||||
|
}), {
|
||||||
|
status: 200,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create tables one by one
|
||||||
await client.query(`
|
await client.query(`
|
||||||
-- Users table
|
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
username VARCHAR(50) UNIQUE NOT NULL,
|
username VARCHAR(50) UNIQUE NOT NULL,
|
||||||
|
|
@ -36,16 +55,18 @@ export default async function handler(req: NextRequest) {
|
||||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||||
last_login TIMESTAMP WITH TIME ZONE
|
last_login TIMESTAMP WITH TIME ZONE
|
||||||
);
|
);
|
||||||
|
`);
|
||||||
|
|
||||||
-- Roles table
|
await client.query(`
|
||||||
CREATE TABLE IF NOT EXISTS roles (
|
CREATE TABLE IF NOT EXISTS roles (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
name VARCHAR(50) UNIQUE NOT NULL,
|
name VARCHAR(50) UNIQUE NOT NULL,
|
||||||
description TEXT,
|
description TEXT,
|
||||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
|
`);
|
||||||
|
|
||||||
-- User roles junction table (many-to-many)
|
await client.query(`
|
||||||
CREATE TABLE IF NOT EXISTS user_roles (
|
CREATE TABLE IF NOT EXISTS user_roles (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
|
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
|
@ -54,8 +75,9 @@ export default async function handler(req: NextRequest) {
|
||||||
assigned_by INTEGER REFERENCES users(id),
|
assigned_by INTEGER REFERENCES users(id),
|
||||||
UNIQUE(user_id, role_id)
|
UNIQUE(user_id, role_id)
|
||||||
);
|
);
|
||||||
|
`);
|
||||||
|
|
||||||
-- Permissions table
|
await client.query(`
|
||||||
CREATE TABLE IF NOT EXISTS permissions (
|
CREATE TABLE IF NOT EXISTS permissions (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
name VARCHAR(100) UNIQUE NOT NULL,
|
name VARCHAR(100) UNIQUE NOT NULL,
|
||||||
|
|
@ -64,16 +86,18 @@ export default async function handler(req: NextRequest) {
|
||||||
action VARCHAR(50),
|
action VARCHAR(50),
|
||||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
|
`);
|
||||||
|
|
||||||
-- Role permissions junction table
|
await client.query(`
|
||||||
CREATE TABLE IF NOT EXISTS role_permissions (
|
CREATE TABLE IF NOT EXISTS role_permissions (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
role_id INTEGER REFERENCES roles(id) ON DELETE CASCADE,
|
role_id INTEGER REFERENCES roles(id) ON DELETE CASCADE,
|
||||||
permission_id INTEGER REFERENCES permissions(id) ON DELETE CASCADE,
|
permission_id INTEGER REFERENCES permissions(id) ON DELETE CASCADE,
|
||||||
UNIQUE(role_id, permission_id)
|
UNIQUE(role_id, permission_id)
|
||||||
);
|
);
|
||||||
|
`);
|
||||||
|
|
||||||
-- Sessions table for JWT token management
|
await client.query(`
|
||||||
CREATE TABLE IF NOT EXISTS user_sessions (
|
CREATE TABLE IF NOT EXISTS user_sessions (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
|
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
|
@ -86,12 +110,6 @@ export default async function handler(req: NextRequest) {
|
||||||
);
|
);
|
||||||
`);
|
`);
|
||||||
|
|
||||||
// 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
|
// Insert default roles
|
||||||
await client.query(`
|
await client.query(`
|
||||||
INSERT INTO roles (name, description) VALUES
|
INSERT INTO roles (name, description) VALUES
|
||||||
|
|
@ -100,51 +118,32 @@ export default async function handler(req: NextRequest) {
|
||||||
ON CONFLICT (name) DO NOTHING;
|
ON CONFLICT (name) DO NOTHING;
|
||||||
`);
|
`);
|
||||||
|
|
||||||
// Insert default permissions
|
// Insert basic permissions
|
||||||
const permissions = [
|
const permissions = [
|
||||||
['cards.read', 'View cards', 'cards', 'read'],
|
['cards.read', 'View cards'],
|
||||||
['cards.create', 'Create new cards', 'cards', 'create'],
|
['collections.manage', 'Manage collections'],
|
||||||
['cards.update', 'Update existing cards', 'cards', 'update'],
|
['decks.manage', 'Manage decks'],
|
||||||
['cards.delete', 'Delete cards', 'cards', 'delete'],
|
['admin.access', 'Access admin panel']
|
||||||
['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) {
|
for (const [name, description] of permissions) {
|
||||||
await client.query(`
|
await client.query(`
|
||||||
INSERT INTO permissions (name, description, resource, action)
|
INSERT INTO permissions (name, description)
|
||||||
VALUES ($1, $2, $3, $4)
|
VALUES ($1, $2)
|
||||||
ON CONFLICT (name) DO NOTHING;
|
ON CONFLICT (name) DO NOTHING;
|
||||||
`, [name, description, resource, action]);
|
`, [name, description]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign permissions to roles
|
// Assign permissions to roles
|
||||||
// User role permissions
|
|
||||||
await client.query(`
|
await client.query(`
|
||||||
INSERT INTO role_permissions (role_id, permission_id)
|
INSERT INTO role_permissions (role_id, permission_id)
|
||||||
SELECT r.id, p.id
|
SELECT r.id, p.id
|
||||||
FROM roles r, permissions p
|
FROM roles r, permissions p
|
||||||
WHERE r.name = 'user'
|
WHERE r.name = 'user'
|
||||||
AND p.name IN (
|
AND p.name IN ('cards.read', 'collections.manage', 'decks.manage')
|
||||||
'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;
|
ON CONFLICT (role_id, permission_id) DO NOTHING;
|
||||||
`);
|
`);
|
||||||
|
|
||||||
// Admin role permissions (all permissions)
|
|
||||||
await client.query(`
|
await client.query(`
|
||||||
INSERT INTO role_permissions (role_id, permission_id)
|
INSERT INTO role_permissions (role_id, permission_id)
|
||||||
SELECT r.id, p.id
|
SELECT r.id, p.id
|
||||||
|
|
@ -153,32 +152,11 @@ export default async function handler(req: NextRequest) {
|
||||||
ON CONFLICT (role_id, permission_id) DO NOTHING;
|
ON CONFLICT (role_id, permission_id) DO NOTHING;
|
||||||
`);
|
`);
|
||||||
|
|
||||||
// Create indexes for performance
|
// Create indexes
|
||||||
await client.query(`
|
await client.query(`
|
||||||
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
|
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_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_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!');
|
console.log('✅ User authentication schema setup completed!');
|
||||||
|
|
|
||||||
86
api/test-db.ts
Normal file
86
api/test-db.ts
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
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 !== 'GET') {
|
||||||
|
return new NextResponse(JSON.stringify({ error: 'Method not allowed' }), {
|
||||||
|
status: 405,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = await pool.connect();
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('Testing database connection...');
|
||||||
|
|
||||||
|
// Test basic connection
|
||||||
|
const result = await client.query('SELECT NOW() as current_time, version() as postgres_version');
|
||||||
|
|
||||||
|
// Check if users table exists
|
||||||
|
const tableCheck = await client.query(`
|
||||||
|
SELECT EXISTS (
|
||||||
|
SELECT FROM information_schema.tables
|
||||||
|
WHERE table_schema = 'public'
|
||||||
|
AND table_name = 'users'
|
||||||
|
) as users_table_exists;
|
||||||
|
`);
|
||||||
|
|
||||||
|
// Check if cards table exists
|
||||||
|
const cardsCheck = await client.query(`
|
||||||
|
SELECT EXISTS (
|
||||||
|
SELECT FROM information_schema.tables
|
||||||
|
WHERE table_schema = 'public'
|
||||||
|
AND table_name = 'cards'
|
||||||
|
) as cards_table_exists;
|
||||||
|
`);
|
||||||
|
|
||||||
|
// Get table counts
|
||||||
|
let userCount = 0;
|
||||||
|
let cardCount = 0;
|
||||||
|
|
||||||
|
if (tableCheck.rows[0].users_table_exists) {
|
||||||
|
const userCountResult = await client.query('SELECT COUNT(*) as count FROM users');
|
||||||
|
userCount = parseInt(userCountResult.rows[0].count);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cardsCheck.rows[0].cards_table_exists) {
|
||||||
|
const cardCountResult = await client.query('SELECT COUNT(*) as count FROM cards');
|
||||||
|
cardCount = parseInt(cardCountResult.rows[0].count);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new NextResponse(JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: 'Database connection successful',
|
||||||
|
database_info: {
|
||||||
|
current_time: result.rows[0].current_time,
|
||||||
|
postgres_version: result.rows[0].postgres_version,
|
||||||
|
users_table_exists: tableCheck.rows[0].users_table_exists,
|
||||||
|
cards_table_exists: cardsCheck.rows[0].cards_table_exists,
|
||||||
|
user_count: userCount,
|
||||||
|
card_count: cardCount
|
||||||
|
}
|
||||||
|
}), {
|
||||||
|
status: 200,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Database connection failed:', error);
|
||||||
|
return new NextResponse(JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
error: 'Database connection failed',
|
||||||
|
details: (error as Error).message
|
||||||
|
}), {
|
||||||
|
status: 500,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
client.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue