Remove conflicting TypeScript API files
🔧 Build Fix:
- Remove .ts versions of API files that conflict with .js versions
- Keep only working JavaScript API endpoints:
- api/auth/login.js
- api/auth/register.js
- api/setup-auth.js
- api/simple.js
- api/debug.js
This resolves the Vercel build error about conflicting file paths.
This commit is contained in:
parent
653e35baee
commit
299fb1916b
7 changed files with 0 additions and 707 deletions
|
|
@ -1,163 +0,0 @@
|
||||||
import { NextRequest, NextResponse } from 'next/server';
|
|
||||||
import { Pool } from 'pg';
|
|
||||||
import bcrypt from 'bcryptjs';
|
|
||||||
import jwt from 'jsonwebtoken';
|
|
||||||
|
|
||||||
const pool = new Pool({
|
|
||||||
connectionString: process.env.DATABASE_URL,
|
|
||||||
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false,
|
|
||||||
});
|
|
||||||
|
|
||||||
interface LoginRequest {
|
|
||||||
username: string;
|
|
||||||
password: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
// Parse request body
|
|
||||||
let body: LoginRequest;
|
|
||||||
try {
|
|
||||||
const bodyText = await req.text();
|
|
||||||
body = JSON.parse(bodyText);
|
|
||||||
} catch (parseError) {
|
|
||||||
return new NextResponse(JSON.stringify({
|
|
||||||
error: 'Invalid JSON in request body'
|
|
||||||
}), {
|
|
||||||
status: 400,
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const { username, password } = body;
|
|
||||||
|
|
||||||
// Validate input
|
|
||||||
if (!username || !password) {
|
|
||||||
return new NextResponse(JSON.stringify({
|
|
||||||
error: 'Username and password are required'
|
|
||||||
}), {
|
|
||||||
status: 400,
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get user by username or email
|
|
||||||
const userResult = await client.query(`
|
|
||||||
SELECT id, username, email, password_hash, first_name, last_name, is_active, last_login
|
|
||||||
FROM users
|
|
||||||
WHERE (username = $1 OR email = $1) AND is_active = true
|
|
||||||
`, [username]);
|
|
||||||
|
|
||||||
if (userResult.rows.length === 0) {
|
|
||||||
return new NextResponse(JSON.stringify({
|
|
||||||
error: 'Invalid credentials'
|
|
||||||
}), {
|
|
||||||
status: 401,
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = userResult.rows[0];
|
|
||||||
|
|
||||||
// Verify password
|
|
||||||
const isValidPassword = await bcrypt.compare(password, user.password_hash);
|
|
||||||
if (!isValidPassword) {
|
|
||||||
return new NextResponse(JSON.stringify({
|
|
||||||
error: 'Invalid credentials'
|
|
||||||
}), {
|
|
||||||
status: 401,
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get user roles and permissions
|
|
||||||
const userRoles = await client.query(`
|
|
||||||
SELECT r.name, r.description,
|
|
||||||
array_agg(p.name) as permissions
|
|
||||||
FROM roles r
|
|
||||||
JOIN user_roles ur ON r.id = ur.role_id
|
|
||||||
LEFT JOIN role_permissions rp ON r.id = rp.role_id
|
|
||||||
LEFT JOIN permissions p ON rp.permission_id = p.id
|
|
||||||
WHERE ur.user_id = $1
|
|
||||||
GROUP BY r.id, r.name, r.description
|
|
||||||
`, [user.id]);
|
|
||||||
|
|
||||||
const roles = userRoles.rows.map(r => r.name);
|
|
||||||
const permissions = [...new Set(userRoles.rows.flatMap(r => r.permissions || []))];
|
|
||||||
|
|
||||||
// Generate JWT token
|
|
||||||
const jwtSecret = process.env.JWT_SECRET || 'fallback-secret-change-in-production';
|
|
||||||
const token = jwt.sign(
|
|
||||||
{
|
|
||||||
userId: user.id,
|
|
||||||
username: user.username,
|
|
||||||
email: user.email,
|
|
||||||
roles,
|
|
||||||
permissions
|
|
||||||
},
|
|
||||||
jwtSecret,
|
|
||||||
{ expiresIn: '7d' }
|
|
||||||
);
|
|
||||||
|
|
||||||
// Store session
|
|
||||||
const tokenHash = await bcrypt.hash(token, 10);
|
|
||||||
const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); // 7 days
|
|
||||||
|
|
||||||
await client.query(`
|
|
||||||
INSERT INTO user_sessions (user_id, token_hash, expires_at, user_agent, ip_address)
|
|
||||||
VALUES ($1, $2, $3, $4, $5)
|
|
||||||
`, [
|
|
||||||
user.id,
|
|
||||||
tokenHash,
|
|
||||||
expiresAt,
|
|
||||||
req.headers.get('user-agent') || null,
|
|
||||||
req.headers.get('x-forwarded-for') || req.headers.get('x-real-ip') || null
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Update last login
|
|
||||||
await client.query(
|
|
||||||
'UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = $1',
|
|
||||||
[user.id]
|
|
||||||
);
|
|
||||||
|
|
||||||
return new NextResponse(JSON.stringify({
|
|
||||||
success: true,
|
|
||||||
message: 'Login successful',
|
|
||||||
user: {
|
|
||||||
id: user.id,
|
|
||||||
username: user.username,
|
|
||||||
email: user.email,
|
|
||||||
firstName: user.first_name,
|
|
||||||
lastName: user.last_name,
|
|
||||||
roles,
|
|
||||||
permissions,
|
|
||||||
lastLogin: user.last_login
|
|
||||||
},
|
|
||||||
token
|
|
||||||
}), {
|
|
||||||
status: 200,
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Login error:', error);
|
|
||||||
return new NextResponse(JSON.stringify({
|
|
||||||
error: 'Login failed',
|
|
||||||
details: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined
|
|
||||||
}), {
|
|
||||||
status: 500,
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
client.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,205 +0,0 @@
|
||||||
import { NextRequest, NextResponse } from 'next/server';
|
|
||||||
import { Pool } from 'pg';
|
|
||||||
import bcrypt from 'bcryptjs';
|
|
||||||
import jwt from 'jsonwebtoken';
|
|
||||||
|
|
||||||
const pool = new Pool({
|
|
||||||
connectionString: process.env.DATABASE_URL,
|
|
||||||
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false,
|
|
||||||
});
|
|
||||||
|
|
||||||
interface RegisterRequest {
|
|
||||||
username: string;
|
|
||||||
email: string;
|
|
||||||
password: string;
|
|
||||||
firstName?: string;
|
|
||||||
lastName?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
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' },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Parse request body
|
|
||||||
let body: RegisterRequest;
|
|
||||||
try {
|
|
||||||
const bodyText = await req.text();
|
|
||||||
body = JSON.parse(bodyText);
|
|
||||||
} catch (parseError) {
|
|
||||||
return new NextResponse(JSON.stringify({
|
|
||||||
error: 'Invalid JSON in request body'
|
|
||||||
}), {
|
|
||||||
status: 400,
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const { username, email, password, firstName, lastName } = body;
|
|
||||||
|
|
||||||
// Validate input
|
|
||||||
if (!username || !email || !password) {
|
|
||||||
return new NextResponse(JSON.stringify({
|
|
||||||
error: 'Username, email, and password are required'
|
|
||||||
}), {
|
|
||||||
status: 400,
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (password.length < 6) {
|
|
||||||
return new NextResponse(JSON.stringify({
|
|
||||||
error: 'Password must be at least 6 characters long'
|
|
||||||
}), {
|
|
||||||
status: 400,
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
const existingUser = await client.query(
|
|
||||||
'SELECT id FROM users WHERE username = $1 OR email = $2',
|
|
||||||
[username, email]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (existingUser.rows.length > 0) {
|
|
||||||
return new NextResponse(JSON.stringify({
|
|
||||||
error: 'Username or email already exists'
|
|
||||||
}), {
|
|
||||||
status: 409,
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash password
|
|
||||||
const saltRounds = 12;
|
|
||||||
const passwordHash = await bcrypt.hash(password, saltRounds);
|
|
||||||
|
|
||||||
// Create user
|
|
||||||
const userResult = await client.query(`
|
|
||||||
INSERT INTO users (username, email, password_hash, first_name, last_name)
|
|
||||||
VALUES ($1, $2, $3, $4, $5)
|
|
||||||
RETURNING id, username, email, first_name, last_name, created_at
|
|
||||||
`, [username, email, passwordHash, firstName || null, lastName || null]);
|
|
||||||
|
|
||||||
const user = userResult.rows[0];
|
|
||||||
|
|
||||||
// Assign default 'user' role
|
|
||||||
const roleResult = await client.query(
|
|
||||||
'SELECT id FROM roles WHERE name = $1',
|
|
||||||
['user']
|
|
||||||
);
|
|
||||||
|
|
||||||
if (roleResult.rows.length > 0) {
|
|
||||||
await client.query(
|
|
||||||
'INSERT INTO user_roles (user_id, role_id) VALUES ($1, $2)',
|
|
||||||
[user.id, roleResult.rows[0].id]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate JWT token
|
|
||||||
const jwtSecret = process.env.JWT_SECRET || 'fallback-secret-change-in-production';
|
|
||||||
const token = jwt.sign(
|
|
||||||
{
|
|
||||||
userId: user.id,
|
|
||||||
username: user.username,
|
|
||||||
email: user.email
|
|
||||||
},
|
|
||||||
jwtSecret,
|
|
||||||
{ expiresIn: '7d' }
|
|
||||||
);
|
|
||||||
|
|
||||||
// Store session
|
|
||||||
const tokenHash = await bcrypt.hash(token, 10);
|
|
||||||
const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); // 7 days
|
|
||||||
|
|
||||||
await client.query(`
|
|
||||||
INSERT INTO user_sessions (user_id, token_hash, expires_at, user_agent, ip_address)
|
|
||||||
VALUES ($1, $2, $3, $4, $5)
|
|
||||||
`, [
|
|
||||||
user.id,
|
|
||||||
tokenHash,
|
|
||||||
expiresAt,
|
|
||||||
req.headers.get('user-agent') || null,
|
|
||||||
req.headers.get('x-forwarded-for') || req.headers.get('x-real-ip') || null
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Get user roles for response
|
|
||||||
const userRoles = await client.query(`
|
|
||||||
SELECT r.name, r.description
|
|
||||||
FROM roles r
|
|
||||||
JOIN user_roles ur ON r.id = ur.role_id
|
|
||||||
WHERE ur.user_id = $1
|
|
||||||
`, [user.id]);
|
|
||||||
|
|
||||||
return new NextResponse(JSON.stringify({
|
|
||||||
success: true,
|
|
||||||
message: 'User registered successfully',
|
|
||||||
user: {
|
|
||||||
id: user.id,
|
|
||||||
username: user.username,
|
|
||||||
email: user.email,
|
|
||||||
firstName: user.first_name,
|
|
||||||
lastName: user.last_name,
|
|
||||||
roles: userRoles.rows.map(r => r.name),
|
|
||||||
createdAt: user.created_at
|
|
||||||
},
|
|
||||||
token
|
|
||||||
}), {
|
|
||||||
status: 201,
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Registration error:', error);
|
|
||||||
return new NextResponse(JSON.stringify({
|
|
||||||
error: 'Registration failed',
|
|
||||||
details: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined
|
|
||||||
}), {
|
|
||||||
status: 500,
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
client.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
import { NextRequest, NextResponse } from 'next/server';
|
|
||||||
|
|
||||||
export default async function handler(req: NextRequest) {
|
|
||||||
return new NextResponse(JSON.stringify({
|
|
||||||
success: true,
|
|
||||||
message: 'API is working!',
|
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
environment: process.env.NODE_ENV || 'unknown',
|
|
||||||
has_database_url: !!process.env.DATABASE_URL,
|
|
||||||
has_jwt_secret: !!process.env.JWT_SECRET,
|
|
||||||
method: req.method,
|
|
||||||
url: req.url
|
|
||||||
}), {
|
|
||||||
status: 200,
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
20
api/hello.ts
20
api/hello.ts
|
|
@ -1,20 +0,0 @@
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
||||||
|
|
||||||
type Data = {
|
|
||||||
message: string;
|
|
||||||
timestamp: string;
|
|
||||||
environment: string;
|
|
||||||
has_database_url: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function handler(
|
|
||||||
req: NextApiRequest,
|
|
||||||
res: NextApiResponse<Data>
|
|
||||||
) {
|
|
||||||
res.status(200).json({
|
|
||||||
message: 'Hello from TCG Vault API!',
|
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
environment: process.env.NODE_ENV || 'unknown',
|
|
||||||
has_database_url: !!process.env.DATABASE_URL
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
@ -1,185 +0,0 @@
|
||||||
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...');
|
|
||||||
|
|
||||||
// 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(`
|
|
||||||
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
|
|
||||||
);
|
|
||||||
`);
|
|
||||||
|
|
||||||
await client.query(`
|
|
||||||
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
|
|
||||||
);
|
|
||||||
`);
|
|
||||||
|
|
||||||
await client.query(`
|
|
||||||
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)
|
|
||||||
);
|
|
||||||
`);
|
|
||||||
|
|
||||||
await client.query(`
|
|
||||||
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
|
|
||||||
);
|
|
||||||
`);
|
|
||||||
|
|
||||||
await client.query(`
|
|
||||||
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)
|
|
||||||
);
|
|
||||||
`);
|
|
||||||
|
|
||||||
await client.query(`
|
|
||||||
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
|
|
||||||
);
|
|
||||||
`);
|
|
||||||
|
|
||||||
// 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 basic permissions
|
|
||||||
const permissions = [
|
|
||||||
['cards.read', 'View cards'],
|
|
||||||
['collections.manage', 'Manage collections'],
|
|
||||||
['decks.manage', 'Manage decks'],
|
|
||||||
['admin.access', 'Access admin panel']
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const [name, description] of permissions) {
|
|
||||||
await client.query(`
|
|
||||||
INSERT INTO permissions (name, description)
|
|
||||||
VALUES ($1, $2)
|
|
||||||
ON CONFLICT (name) DO NOTHING;
|
|
||||||
`, [name, description]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assign permissions to roles
|
|
||||||
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.manage', 'decks.manage')
|
|
||||||
ON CONFLICT (role_id, permission_id) DO NOTHING;
|
|
||||||
`);
|
|
||||||
|
|
||||||
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
|
|
||||||
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);
|
|
||||||
`);
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
export default function handler(req: any, res: any) {
|
|
||||||
res.status(200).json({
|
|
||||||
message: 'Simple endpoint works!',
|
|
||||||
timestamp: new Date().toISOString()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
111
api/test-db.ts
111
api/test-db.ts
|
|
@ -1,111 +0,0 @@
|
||||||
import { NextRequest, NextResponse } from 'next/server';
|
|
||||||
import { Pool } from 'pg';
|
|
||||||
|
|
||||||
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' },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check environment variables first
|
|
||||||
if (!process.env.DATABASE_URL) {
|
|
||||||
return new NextResponse(JSON.stringify({
|
|
||||||
success: false,
|
|
||||||
error: 'DATABASE_URL environment variable not set',
|
|
||||||
environment: process.env.NODE_ENV || 'unknown'
|
|
||||||
}), {
|
|
||||||
status: 500,
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const pool = new Pool({
|
|
||||||
connectionString: process.env.DATABASE_URL,
|
|
||||||
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false,
|
|
||||||
connectionTimeoutMillis: 10000, // 10 seconds
|
|
||||||
query_timeout: 10000, // 10 seconds
|
|
||||||
});
|
|
||||||
|
|
||||||
let client: any;
|
|
||||||
|
|
||||||
try {
|
|
||||||
console.log('Testing database connection...');
|
|
||||||
|
|
||||||
// Set a timeout for the entire operation
|
|
||||||
const timeoutPromise = new Promise((_, reject) => {
|
|
||||||
setTimeout(() => reject(new Error('Database operation timed out')), 15000);
|
|
||||||
});
|
|
||||||
|
|
||||||
const dbOperation = async () => {
|
|
||||||
client = await pool.connect();
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
`);
|
|
||||||
|
|
||||||
return {
|
|
||||||
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
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const dbResult = await Promise.race([dbOperation(), timeoutPromise]);
|
|
||||||
|
|
||||||
return new NextResponse(JSON.stringify({
|
|
||||||
success: true,
|
|
||||||
message: 'Database connection successful',
|
|
||||||
database_info: dbResult
|
|
||||||
}), {
|
|
||||||
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,
|
|
||||||
database_url_exists: !!process.env.DATABASE_URL,
|
|
||||||
database_url_preview: process.env.DATABASE_URL ?
|
|
||||||
process.env.DATABASE_URL.substring(0, 20) + '...' : 'not set'
|
|
||||||
}), {
|
|
||||||
status: 500,
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
if (client) {
|
|
||||||
try {
|
|
||||||
client.release();
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error releasing client:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await pool.end();
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error ending pool:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue