Some checks failed
CI Pipeline / Lint Code (push) Has been cancelled
CI Pipeline / Test API Package (push) Has been cancelled
CI Pipeline / Test Web Package (push) Has been cancelled
CI Pipeline / Test Shared Package (push) Has been cancelled
CI Pipeline / Build All Packages (push) Has been cancelled
CI Pipeline / Generate Coverage Report (push) Has been cancelled
Docker Build & Deploy / Build Docker Images (push) Has been cancelled
Docker Build & Deploy / Push Docker Images (push) Has been cancelled
Docker Build & Deploy / Deploy to Staging (push) Has been cancelled
Docker Build & Deploy / Deploy to Production (push) Has been cancelled
E2E Tests / End-to-End Tests (push) Has been cancelled
E2E Tests / E2E Tests (Mobile) (push) Has been cancelled
Security Scanning / NPM Audit (push) Has been cancelled
Security Scanning / Dependency License Check (push) Has been cancelled
Security Scanning / Code Quality Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
Implement a complete authentication system with local email/password authentication, Google OAuth, JWT tokens, and role-based access control. Backend Features: - Database schema with User, RefreshToken, VerificationToken, RecipeShare models - Role-based access control (USER, ADMIN) - Recipe visibility controls (PRIVATE, SHARED, PUBLIC) - Email verification for local accounts - Password reset functionality - JWT access tokens (15min) and refresh tokens (7 days) - Passport.js strategies: Local, JWT, Google OAuth - bcrypt password hashing with 12 salt rounds - Password strength validation (min 8 chars, uppercase, lowercase, number) - Rate limiting on auth endpoints (5 attempts/15min) - Email service with styled HTML templates for verification and password reset API Endpoints: - POST /api/auth/register - Register with email/password - POST /api/auth/login - Login and get tokens - POST /api/auth/logout - Invalidate refresh token - POST /api/auth/refresh - Get new access token - GET /api/auth/verify-email/:token - Verify email address - POST /api/auth/resend-verification - Resend verification email - POST /api/auth/forgot-password - Request password reset - POST /api/auth/reset-password - Reset password with token - GET /api/auth/google - Initiate Google OAuth - GET /api/auth/google/callback - Google OAuth callback - GET /api/auth/me - Get current user info Security Middleware: - requireAuth - Protect routes requiring authentication - requireAdmin - Admin-only route protection - optionalAuth - Routes that work with or without auth - requireOwnership - Check resource ownership Admin Tools: - npm run create-admin - Interactive script to create admin users - verify-user-manual.ts - Helper script for testing Test Coverage: - 49 unit and integration tests (all passing) - Password utility tests (12 tests) - JWT utility tests (17 tests) - Auth middleware tests (12 tests) - Auth routes integration tests (8 tests) Dependencies Added: - passport, passport-local, passport-jwt, passport-google-oauth20 - bcrypt, jsonwebtoken - nodemailer - express-rate-limit, express-validator, cookie-parser Environment Variables Required: - JWT_SECRET, JWT_REFRESH_SECRET - JWT_EXPIRES_IN, JWT_REFRESH_EXPIRES_IN - GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET (optional) - SMTP configuration for email - APP_URL, API_URL 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
121 lines
3.5 KiB
JavaScript
121 lines
3.5 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* Script to create an admin user
|
||
* Usage: npm run create-admin
|
||
* or: npx tsx src/scripts/create-admin.ts
|
||
*/
|
||
|
||
import { PrismaClient } from '@prisma/client';
|
||
import * as readline from 'readline';
|
||
import { hashPassword, validatePasswordStrength } from '../utils/password';
|
||
|
||
const prisma = new PrismaClient();
|
||
|
||
const rl = readline.createInterface({
|
||
input: process.stdin,
|
||
output: process.stdout,
|
||
});
|
||
|
||
function question(query: string): Promise<string> {
|
||
return new Promise((resolve) => {
|
||
rl.question(query, resolve);
|
||
});
|
||
}
|
||
|
||
async function createAdmin() {
|
||
try {
|
||
console.log('\n🌿 Basil Recipe Manager - Create Admin User\n');
|
||
|
||
// Get email
|
||
const email = await question('Email address: ');
|
||
if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
||
console.error('❌ Invalid email address');
|
||
process.exit(1);
|
||
}
|
||
|
||
// Check if user already exists
|
||
const existingUser = await prisma.user.findUnique({
|
||
where: { email: email.toLowerCase() },
|
||
});
|
||
|
||
if (existingUser) {
|
||
console.error('❌ User with this email already exists');
|
||
|
||
if (existingUser.role === 'ADMIN') {
|
||
console.log('ℹ️ This user is already an admin');
|
||
} else {
|
||
const upgrade = await question('Would you like to upgrade this user to admin? (yes/no): ');
|
||
if (upgrade.toLowerCase() === 'yes' || upgrade.toLowerCase() === 'y') {
|
||
await prisma.user.update({
|
||
where: { id: existingUser.id },
|
||
data: { role: 'ADMIN' },
|
||
});
|
||
console.log('✅ User upgraded to admin successfully');
|
||
}
|
||
}
|
||
|
||
process.exit(0);
|
||
}
|
||
|
||
// Get name
|
||
const name = await question('Full name (optional): ');
|
||
|
||
// Get password
|
||
const password = await question('Password (min 8 chars, uppercase, lowercase, number): ');
|
||
|
||
// Validate password
|
||
const passwordValidation = validatePasswordStrength(password);
|
||
if (!passwordValidation.valid) {
|
||
console.error('❌ Password does not meet requirements:');
|
||
passwordValidation.errors.forEach((error) => {
|
||
console.error(` - ${error}`);
|
||
});
|
||
process.exit(1);
|
||
}
|
||
|
||
// Confirm password
|
||
const passwordConfirm = await question('Confirm password: ');
|
||
if (password !== passwordConfirm) {
|
||
console.error('❌ Passwords do not match');
|
||
process.exit(1);
|
||
}
|
||
|
||
// Hash password
|
||
console.log('\n🔐 Hashing password...');
|
||
const passwordHash = await hashPassword(password);
|
||
|
||
// Create admin user
|
||
console.log('👤 Creating admin user...');
|
||
const admin = await prisma.user.create({
|
||
data: {
|
||
email: email.toLowerCase(),
|
||
name: name || undefined,
|
||
passwordHash,
|
||
role: 'ADMIN',
|
||
provider: 'local',
|
||
emailVerified: true, // Auto-verify admin users
|
||
emailVerifiedAt: new Date(),
|
||
},
|
||
});
|
||
|
||
console.log('\n✅ Admin user created successfully!');
|
||
console.log('\nUser details:');
|
||
console.log(` ID: ${admin.id}`);
|
||
console.log(` Email: ${admin.email}`);
|
||
console.log(` Name: ${admin.name || 'N/A'}`);
|
||
console.log(` Role: ${admin.role}`);
|
||
console.log(` Email Verified: ${admin.emailVerified}`);
|
||
console.log(` Created: ${admin.createdAt.toISOString()}`);
|
||
console.log('\n🔑 You can now log in with this email and password\n');
|
||
|
||
} catch (error) {
|
||
console.error('\n❌ Error creating admin user:', error);
|
||
process.exit(1);
|
||
} finally {
|
||
rl.close();
|
||
await prisma.$disconnect();
|
||
}
|
||
}
|
||
|
||
createAdmin();
|