This commit introduces several new scripts for managing database operations, including user creation, permission grants, and data migrations. It also adds new documentation files to guide users through the setup and configuration processes. Additionally, the project structure is updated to enhance organization and maintainability, ensuring a smoother development experience for contributors. These changes support the ongoing transition to a web-based architecture and improve overall project functionality.
179 lines
6.7 KiB
TypeScript
179 lines
6.7 KiB
TypeScript
import { PrismaClient as PrismaClientAuth } from '../node_modules/.prisma/client-auth';
|
||
import { Resend } from 'resend';
|
||
import * as dotenv from 'dotenv';
|
||
import crypto from 'crypto';
|
||
|
||
dotenv.config();
|
||
|
||
const prismaAuth = new PrismaClientAuth({
|
||
datasourceUrl: process.env.DATABASE_URL_AUTH,
|
||
});
|
||
|
||
const resend = new Resend(process.env.RESEND_API_KEY);
|
||
|
||
async function checkAndResend() {
|
||
try {
|
||
console.log('🔍 Checking users in database...\n');
|
||
|
||
// Find users that are not verified
|
||
const unverifiedUsers = await prismaAuth.user.findMany({
|
||
where: {
|
||
emailVerified: false,
|
||
},
|
||
select: {
|
||
id: true,
|
||
email: true,
|
||
name: true,
|
||
emailConfirmationToken: true,
|
||
emailConfirmationTokenExpiry: true,
|
||
createdAt: true,
|
||
},
|
||
orderBy: {
|
||
createdAt: 'desc',
|
||
},
|
||
});
|
||
|
||
if (unverifiedUsers.length === 0) {
|
||
console.log('✅ No unverified users found.');
|
||
console.log('\n📋 All users:');
|
||
const allUsers = await prismaAuth.user.findMany({
|
||
select: {
|
||
id: true,
|
||
email: true,
|
||
name: true,
|
||
emailVerified: true,
|
||
createdAt: true,
|
||
},
|
||
orderBy: {
|
||
createdAt: 'desc',
|
||
},
|
||
});
|
||
allUsers.forEach(user => {
|
||
console.log(` - ${user.email} (${user.name}) - Verified: ${user.emailVerified}`);
|
||
});
|
||
return;
|
||
}
|
||
|
||
console.log(`Found ${unverifiedUsers.length} unverified user(s):\n`);
|
||
|
||
for (const user of unverifiedUsers) {
|
||
console.log(`📧 User: ${user.email} (${user.name})`);
|
||
console.log(` Created: ${user.createdAt}`);
|
||
console.log(` Has token: ${user.emailConfirmationToken ? 'Yes' : 'No'}`);
|
||
if (user.emailConfirmationTokenExpiry) {
|
||
const isExpired = user.emailConfirmationTokenExpiry < new Date();
|
||
console.log(` Token expires: ${user.emailConfirmationTokenExpiry} ${isExpired ? '(EXPIRED)' : ''}`);
|
||
}
|
||
console.log('');
|
||
}
|
||
|
||
// Get the most recent unverified user
|
||
const latestUser = unverifiedUsers[0];
|
||
console.log(`\n📤 Attempting to resend confirmation email to: ${latestUser.email}\n`);
|
||
|
||
// Generate new token if needed
|
||
let token = latestUser.emailConfirmationToken;
|
||
if (!token || (latestUser.emailConfirmationTokenExpiry && latestUser.emailConfirmationTokenExpiry < new Date())) {
|
||
console.log('🔄 Generating new confirmation token...');
|
||
token = crypto.randomBytes(32).toString('hex');
|
||
const tokenExpiry = new Date();
|
||
tokenExpiry.setHours(tokenExpiry.getHours() + 24);
|
||
|
||
await prismaAuth.user.update({
|
||
where: { id: latestUser.id },
|
||
data: {
|
||
emailConfirmationToken: token,
|
||
emailConfirmationTokenExpiry: tokenExpiry,
|
||
},
|
||
});
|
||
console.log('✅ New token generated');
|
||
}
|
||
|
||
// Send email
|
||
const baseUrl = process.env.NEXTAUTH_URL || process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3001';
|
||
const confirmationUrl = `${baseUrl}/api/auth/verify-email?token=${token}`;
|
||
const fromEmail = process.env.RESEND_FROM_EMAIL?.trim().replace(/^["']|["']$/g, '') || 'onboarding@resend.dev';
|
||
|
||
console.log(`📧 Sending email from: ${fromEmail}`);
|
||
console.log(`📧 Sending email to: ${latestUser.email}`);
|
||
console.log(`🔗 Confirmation URL: ${confirmationUrl}\n`);
|
||
|
||
try {
|
||
const result = await resend.emails.send({
|
||
from: fromEmail,
|
||
to: latestUser.email,
|
||
subject: 'Confirm your email address',
|
||
html: `
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Confirm your email</title>
|
||
</head>
|
||
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;">
|
||
<div style="background-color: #f8f9fa; padding: 30px; border-radius: 8px;">
|
||
<h1 style="color: #2563eb; margin-top: 0;">Confirm your email address</h1>
|
||
<p>Hi ${latestUser.name},</p>
|
||
<p>You requested a new confirmation email. Please confirm your email address by clicking the button below:</p>
|
||
<div style="text-align: center; margin: 30px 0;">
|
||
<a href="${confirmationUrl}" style="background-color: #2563eb; color: white; padding: 12px 24px; text-decoration: none; border-radius: 6px; display: inline-block; font-weight: bold;">Confirm Email Address</a>
|
||
</div>
|
||
<p>Or copy and paste this link into your browser:</p>
|
||
<p style="word-break: break-all; color: #666; font-size: 14px;">${confirmationUrl}</p>
|
||
<p style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #e5e7eb; color: #6b7280; font-size: 14px;">
|
||
This link will expire in 24 hours. If you didn't request this email, you can safely ignore it.
|
||
</p>
|
||
</div>
|
||
</body>
|
||
</html>
|
||
`,
|
||
});
|
||
|
||
if (result.error) {
|
||
console.error('❌ Error from Resend API:');
|
||
console.error(' Status:', result.error.statusCode);
|
||
console.error(' Message:', result.error.message);
|
||
console.error(' Name:', result.error.name);
|
||
|
||
if (result.error.message?.includes('domain')) {
|
||
console.error('\n⚠️ IMPORTANT: Domain verification issue!');
|
||
console.error(' Resend\'s test domain (onboarding@resend.dev) can only send to:');
|
||
console.error(' - The email address associated with your Resend account');
|
||
console.error(' - To send to other addresses, you need to verify your own domain');
|
||
console.error(' - Go to: https://resend.com/domains to verify a domain');
|
||
}
|
||
} else {
|
||
console.log('✅ Email sent successfully!');
|
||
console.log(' Email ID:', result.data?.id);
|
||
console.log(`\n📬 Check the inbox for: ${latestUser.email}`);
|
||
console.log(' (Also check spam/junk folder)');
|
||
}
|
||
} catch (error: any) {
|
||
console.error('❌ Error sending email:');
|
||
console.error(' Message:', error.message);
|
||
if (error.response) {
|
||
console.error(' Response:', JSON.stringify(error.response, null, 2));
|
||
}
|
||
}
|
||
|
||
} catch (error: any) {
|
||
console.error('❌ Error:', error.message);
|
||
if (error.message?.includes('email_verified')) {
|
||
console.error('\n⚠️ Database migration may not have been run!');
|
||
console.error(' Run: sudo -u postgres psql -d punimtag_auth -f migrations/add-email-verification-columns.sql');
|
||
}
|
||
} finally {
|
||
await prismaAuth.$disconnect();
|
||
}
|
||
}
|
||
|
||
checkAndResend();
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|