add a way for a user to get their email

This commit is contained in:
chark1es 2025-03-08 23:57:23 -08:00
parent 44e3f6e7ff
commit 2b84b9c433
2 changed files with 318 additions and 33 deletions

View file

@ -6,11 +6,11 @@ import { toast } from 'react-hot-toast';
export default function EmailRequestSettings() {
const auth = Authentication.getInstance();
const update = Update.getInstance();
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
const [requesting, setRequesting] = useState(false);
const [isOfficer, setIsOfficer] = useState(false);
const [createdEmail, setCreatedEmail] = useState<string | null>(null);
useEffect(() => {
const loadUserData = async () => {
@ -52,20 +52,38 @@ export default function EmailRequestSettings() {
try {
setRequesting(true);
// Update the user record to mark email as requested
const pb = auth.getPocketBase();
await pb.collection(Collections.USERS).update(user.id, {
requested_email: true
// Call the API to create the email account
const response = await fetch('/api/create-ieee-email', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
userId: user.id,
name: user.name,
email: user.email
})
});
// Refresh user data
const updatedUser = auth.getCurrentUser();
setUser(updatedUser);
const result = await response.json();
toast.success('Email request submitted successfully! Our team will process your request soon.');
if (response.ok && result.success) {
// Email created successfully
setCreatedEmail(result.data.ieeeEmail);
// Update the user record to mark email as requested
const pb = auth.getPocketBase();
await pb.collection(Collections.USERS).update(user.id, {
requested_email: true
});
toast.success('IEEE email created successfully! Check your email for login details.');
} else {
toast.error(result.message || 'Failed to create email. Please contact the webmaster for assistance.');
}
} catch (error) {
console.error('Error requesting email:', error);
toast.error('Failed to submit email request. Please try again later.');
toast.error('Failed to create email. Please try again later.');
} finally {
setRequesting(false);
}
@ -81,33 +99,38 @@ export default function EmailRequestSettings() {
if (!isOfficer) {
return (
<div className="alert alert-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" className="stroke-current shrink-0 w-6 h-6"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
<span>IEEE email addresses are only available to officers. If you are an officer and don't see the option to request an email, please contact the webmaster.</span>
<div className="p-4 bg-base-200 rounded-lg">
<p>IEEE email addresses are only available to officers. If you are an officer and don't see the option to request an email, please contact the webmaster.</p>
</div>
);
}
if (user?.requested_email) {
if (user?.requested_email || createdEmail) {
return (
<div className="space-y-4">
<div className="alert alert-success">
<svg xmlns="http://www.w3.org/2000/svg" className="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
<span>You have requested an IEEE email address. Our team is processing your request.</span>
</div>
<div className="p-4 bg-base-200 rounded-lg">
<h3 className="font-bold text-lg mb-2">
{createdEmail ? 'Your IEEE Email Address' : 'Email Request Status'}
</h3>
<div className="card bg-base-200 p-4">
<h3 className="font-bold text-lg mb-2">What happens next?</h3>
<ol className="list-decimal list-inside space-y-2">
<li>Our webmaster will create your email address (typically firstname.lastname@ieeeucsd.org)</li>
<li>You'll receive an email with your credentials and setup instructions</li>
<li>You can use this email for IEEE-related communications</li>
</ol>
</div>
{createdEmail && (
<div className="mb-4">
<p className="text-xl font-mono bg-base-100 p-2 rounded">{createdEmail}</p>
<p className="mt-2 text-sm">Check your personal email for login instructions.</p>
</div>
)}
<div className="alert alert-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" className="stroke-current shrink-0 w-6 h-6"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
<span>If you have any questions or need help with your IEEE email, please contact <a href="mailto:webmaster@ieeeucsd.org" className="underline">webmaster@ieeeucsd.org</a></span>
<div className="mb-4">
<h4 className="font-semibold mb-1">Access Your Email</h4>
<ul className="list-disc list-inside space-y-1">
<li>Webmail: <a href="https://heracles.mxrouting.net:2096/" target="_blank" rel="noopener noreferrer" className="link link-primary">https://heracles.mxrouting.net:2096/</a></li>
<li>IMAP/SMTP settings: <a href="https://mxroute.com/setup/" target="_blank" rel="noopener noreferrer" className="link link-primary">https://mxroute.com/setup/</a></li>
</ul>
</div>
<p className="text-sm">
If you have any questions or need help with your IEEE email, please contact <a href="mailto:webmaster@ieeeucsd.org" className="underline">webmaster@ieeeucsd.org</a>
</p>
</div>
</div>
);
@ -119,7 +142,7 @@ export default function EmailRequestSettings() {
As an IEEE officer, you're eligible for an official IEEE UCSD email address. This email can be used for all IEEE-related communications and provides a professional identity when representing the organization.
</p>
<div className="card bg-base-200 p-4">
<div className="p-4 bg-base-200 rounded-lg">
<h3 className="font-bold text-lg mb-2">Benefits of an IEEE email:</h3>
<ul className="list-disc list-inside space-y-1">
<li>Professional communication with sponsors and partners</li>
@ -137,16 +160,16 @@ export default function EmailRequestSettings() {
{requesting ? (
<>
<span className="loading loading-spinner loading-sm"></span>
Processing...
Creating Email...
</>
) : (
'Request IEEE Email Address'
'Create IEEE Email Address'
)}
</button>
<div className="text-xs opacity-70">
<p>By requesting an email, you agree to use it responsibly and in accordance with IEEE UCSD policies.</p>
<p>Email addresses are typically in the format firstname.lastname@ieeeucsd.org</p>
<p>Your email address will be based on your current email username.</p>
</div>
</div>
);

View file

@ -0,0 +1,262 @@
import type { APIRoute } from "astro";
export const POST: APIRoute = async ({ request }) => {
try {
const { userId, name, email } = await request.json();
if (!userId || !name || !email) {
return new Response(
JSON.stringify({
success: false,
message: "Missing required parameters",
}),
{
status: 400,
headers: {
"Content-Type": "application/json",
},
},
);
}
// Extract username from email (everything before the @ symbol)
const emailUsername = email.split("@")[0].toLowerCase();
// Remove any special characters that might cause issues
const cleanUsername = emailUsername.replace(/[^a-z0-9]/g, "");
// Generate a secure random password
const password = generateSecurePassword();
// MXRoute DirectAdmin API credentials from environment variables
const loginKey = import.meta.env.MXROUTE_LOGIN_KEY;
const serverLogin = import.meta.env.MXROUTE_SERVER_LOGIN;
const serverUrl = import.meta.env.MXROUTE_SERVER_URL;
const emailQuota = import.meta.env.MXROUTE_EMAIL_QUOTA;
const emailOutboundLimit = import.meta.env.MXROUTE_EMAIL_OUTBOUND_LIMIT;
const emailDomain = import.meta.env.MXROUTE_EMAIL_DOMAIN;
if (!loginKey || !serverLogin || !serverUrl || !emailDomain) {
throw new Error("Missing MXRoute configuration");
}
// DirectAdmin API endpoint for creating email accounts
// According to the documentation: https://docs.directadmin.com/developer/api/legacy-api.html
let baseUrl = serverUrl;
// If the URL contains a specific command, extract just the base URL
if (baseUrl.includes("/CMD_")) {
baseUrl = baseUrl.split("/CMD_")[0];
}
// Make sure there's no trailing slash
baseUrl = baseUrl.replace(/\/$/, "");
// Construct the email POP API URL
const emailApiUrl = `${baseUrl}/CMD_API_EMAIL_POP`;
console.log(`Creating email account: ${cleanUsername}@${emailDomain}`);
console.log(`DirectAdmin API URL: ${emailApiUrl}`);
// Create the email account via DirectAdmin API
// According to DirectAdmin legacy API docs:
// https://docs.directadmin.com/developer/api/legacy-api.html
const formData = new URLSearchParams();
formData.append("action", "create");
formData.append("domain", emailDomain);
formData.append("user", cleanUsername); // DirectAdmin uses 'user' for POP accounts
formData.append("passwd", password);
formData.append("passwd2", password);
formData.append("quota", emailQuota || "200");
formData.append("limit", emailOutboundLimit || "9600");
// Log the form data being sent
console.log("Form data:");
formData.forEach((value, key) => {
console.log(` ${key}: ${value}`);
});
const response = await fetch(emailApiUrl, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Authorization: `Basic ${Buffer.from(`${serverLogin}:${loginKey}`).toString("base64")}`,
},
body: formData,
});
const responseText = await response.text();
console.log(`DirectAdmin response: ${responseText}`);
// DirectAdmin API returns "error=1" in the response text for errors
if (responseText.includes("error=1") || !response.ok) {
console.error("Error creating email account:", responseText);
// Parse the error details if possible
let errorMessage = "Failed to create email account";
try {
const errorParams = new URLSearchParams(responseText);
if (errorParams.has("text")) {
errorMessage = decodeURIComponent(errorParams.get("text") || "");
}
if (errorParams.has("details")) {
const details = decodeURIComponent(errorParams.get("details") || "");
errorMessage += `: ${details.replace(/<br>/g, " ")}`;
}
} catch (e) {
console.error("Error parsing DirectAdmin error response:", e);
}
// If the error is because the email already exists
if (responseText.includes("already exists")) {
return new Response(
JSON.stringify({
success: false,
message: `Email address ${cleanUsername}@${emailDomain} already exists. Please contact the webmaster for assistance.`,
}),
{
status: 409,
headers: {
"Content-Type": "application/json",
},
},
);
}
throw new Error(errorMessage);
}
// Send notification email to the user with their new email credentials
await sendCredentialsEmail(
email,
`${cleanUsername}@${emailDomain}`,
password,
);
// Send notification to webmaster
await sendWebmasterNotification(
userId,
name,
email,
`${cleanUsername}@${emailDomain}`,
);
return new Response(
JSON.stringify({
success: true,
data: {
ieeeEmail: `${cleanUsername}@${emailDomain}`,
message:
"Email account created successfully. Check your email for login details.",
},
}),
{
status: 200,
headers: {
"Content-Type": "application/json",
},
},
);
} catch (error) {
console.error("Error in create-ieee-email:", error);
return new Response(
JSON.stringify({
success: false,
message: error instanceof Error ? error.message : "An error occurred",
}),
{
status: 500,
headers: {
"Content-Type": "application/json",
},
},
);
}
};
// Generate a secure random password
function generateSecurePassword(length = 16) {
const charset =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+";
let password = "";
// Ensure at least one character from each category
password += charset.substring(0, 26).charAt(Math.floor(Math.random() * 26)); // lowercase
password += charset.substring(26, 52).charAt(Math.floor(Math.random() * 26)); // uppercase
password += charset.substring(52, 62).charAt(Math.floor(Math.random() * 10)); // number
password += charset
.substring(62)
.charAt(Math.floor(Math.random() * (charset.length - 62))); // special
// Fill the rest randomly
for (let i = 4; i < length; i++) {
password += charset.charAt(Math.floor(Math.random() * charset.length));
}
// Shuffle the password
return password
.split("")
.sort(() => 0.5 - Math.random())
.join("");
}
// Send email with credentials to the user
async function sendCredentialsEmail(
userEmail: string,
ieeeEmail: string,
password: string,
) {
// In a real implementation, you would use an email service like SendGrid, Mailgun, etc.
// For now, we'll just log the email that would be sent
console.log(`
To: ${userEmail}
Subject: Your IEEE UCSD Email Account
Hello,
Your IEEE UCSD email account has been created:
Email address: ${ieeeEmail}
Password: ${password}
You can access your email through:
- Webmail: https://heracles.mxrouting.net:2096/
- IMAP/SMTP settings can be found at: https://mxroute.com/setup/
Please change your password after your first login.
If you have any questions, please contact webmaster@ieeeucsd.org.
Best regards,
IEEE UCSD Web Team
`);
// In a production environment, replace with actual email sending code
return true;
}
// Send notification to webmaster
async function sendWebmasterNotification(
userId: string,
name: string,
email: string,
ieeeEmail: string,
) {
// In a real implementation, you would use an email service
console.log(`
To: webmaster@ieeeucsd.org
Subject: New IEEE Email Account Created
A new IEEE email account has been created:
User ID: ${userId}
Name: ${name}
Personal Email: ${email}
IEEE Email: ${ieeeEmail}
This is an automated notification.
`);
// In a production environment, replace with actual email sending code
return true;
}