This commit is contained in:
2025-10-16 17:48:29 -05:00
parent eeac2c6904
commit 45a61a032c
8 changed files with 168 additions and 373 deletions

View File

@@ -1,12 +1,8 @@
import { NextRequest, NextResponse } from 'next/server';
import { db } from '@/app/db/drizzle';
import { emails } from '@/app/db/schema';
import { authenticate, getBody } from '@/app/lib/utils';
import { authenticate } from '@/app/lib/utils';
import { eq } from 'drizzle-orm';
import { CopyObjectCommand, GetObjectCommand, HeadObjectCommand } from '@aws-sdk/client-s3';
import { getS3Client } from '@/app/lib/utils';
import nodemailer from 'nodemailer';
import { Readable } from 'stream';
export async function GET(req: NextRequest) {
if (!authenticate(req)) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
@@ -26,67 +22,9 @@ export async function GET(req: NextRequest) {
html: email.html,
raw: email.raw,
processed: email.processed ? 'true' : 'false',
processedAt: email.processedAt?.toISOString() || null,
processedBy: email.processedBy,
queuedTo: email.queuedTo,
status: email.status,
});
}
// PUT: Update processed in S3 and DB
export async function PUT(req: NextRequest) {
if (!authenticate(req)) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
const { bucket, key, processed } = await req.json();
if (!bucket || !key) return NextResponse.json({ error: 'Missing params' }, { status: 400 });
const s3 = getS3Client();
const head = await s3.send(new HeadObjectCommand({ Bucket: bucket, Key: key }));
const newMeta = { ...head.Metadata, [process.env.PROCESSED_META_KEY!]: processed };
await s3.send(new CopyObjectCommand({
Bucket: bucket,
Key: key,
CopySource: `${bucket}/${key}`,
Metadata: newMeta,
MetadataDirective: 'REPLACE'
}));
await db.update(emails).set({ processed: processed === 'true' }).where(eq(emails.s3Key, key));
return NextResponse.json({ success: true });
}
// POST: Resend, update in S3 and DB
export async function POST(req: NextRequest) {
if (!authenticate(req)) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
const { bucket, key } = await req.json();
if (!bucket || !key) return NextResponse.json({ error: 'Missing params' }, { status: 400 });
const s3 = getS3Client();
const { Body } = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
const raw = await getBody(Body as Readable);
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: Number(process.env.SMTP_PORT),
secure: false,
auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS },
tls: { rejectUnauthorized: false }
});
try {
await transporter.sendMail({ raw });
// Update S3 Metadata
const head = await s3.send(new HeadObjectCommand({ Bucket: bucket, Key: key }));
const newMeta = { ...head.Metadata, [process.env.PROCESSED_META_KEY!]: process.env.PROCESSED_META_VALUE! };
await s3.send(new CopyObjectCommand({
Bucket: bucket,
Key: key,
CopySource: `${bucket}/${key}`,
Metadata: newMeta,
MetadataDirective: 'REPLACE'
}));
// Update DB
await db.update(emails).set({ processed: true }).where(eq(emails.s3Key, key));
return NextResponse.json({ message: 'Resent successfully' });
} catch (error) {
return NextResponse.json({ error: (error as Error).message }, { status: 500 });
}
}

View File

@@ -20,7 +20,20 @@ export async function GET(req: NextRequest) {
subject: emails.subject,
date: emails.date,
processed: emails.processed,
processedAt: emails.processedAt,
processedBy: emails.processedBy,
queuedTo: emails.queuedTo,
status: emails.status,
}).from(emails).where(sql`${mailbox} = ANY(${emails.to}) AND ${emails.domainId} = ${domain.id}`);
return NextResponse.json(emailList.map(e => ({ key: e.key, subject: e.subject, date: e.date?.toISOString(), processed: e.processed ? 'true' : 'false' })));
return NextResponse.json(emailList.map(e => ({
key: e.key,
subject: e.subject,
date: e.date?.toISOString(),
processed: e.processed ? 'true' : 'false',
processedAt: e.processedAt?.toISOString() || null,
processedBy: e.processedBy,
queuedTo: e.queuedTo,
status: e.status,
})));
}

View File

@@ -2,7 +2,7 @@ import { NextRequest, NextResponse } from 'next/server';
import { db } from '@/app/db/drizzle';
import { domains, emails } from '@/app/db/schema';
import { authenticate } from '@/app/lib/utils';
import { eq, sql } from 'drizzle-orm';
import { eq } from 'drizzle-orm';
export async function GET(req: NextRequest) {
if (!authenticate(req)) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
@@ -14,9 +14,24 @@ export async function GET(req: NextRequest) {
const [domain] = await db.select().from(domains).where(eq(domains.bucket, bucket));
if (!domain) return NextResponse.json({ error: 'Domain not found' }, { status: 404 });
// Hole alle E-Mail-Adressen aus den "to" Feldern für diese Domain
const mailboxData = await db.select({ to: emails.to }).from(emails).where(eq(emails.domainId, domain.id));
// Extrahiere die Domain aus dem Bucket-Namen (z.B. "example-com-emails" -> "example.com")
const domainName = bucket.replace('-emails', '').replace(/-/g, '.');
const uniqueMailboxes = new Set<string>();
mailboxData.forEach(em => em.to?.forEach(r => uniqueMailboxes.add(r.toLowerCase())));
// Filtere nur E-Mail-Adressen, die zur aktuellen Domain gehören
mailboxData.forEach(em => {
em.to?.forEach(recipient => {
const recipientLower = recipient.toLowerCase();
// Prüfe, ob die E-Mail-Adresse zur Domain gehört
if (recipientLower.endsWith(`@${domainName}`)) {
uniqueMailboxes.add(recipientLower);
}
});
});
return NextResponse.json(Array.from(uniqueMailboxes));
return NextResponse.json(Array.from(uniqueMailboxes).sort());
}

View File

@@ -1,30 +0,0 @@
import { NextRequest, NextResponse } from 'next/server';
import { db } from '@/app/db/drizzle';
import { domains, emails } from '@/app/db/schema';
import { authenticate } from '@/app/lib/utils';
import { eq } from 'drizzle-orm';
export async function POST(req: NextRequest) {
if (!authenticate(req)) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
const { bucket } = await req.json();
if (!bucket) return NextResponse.json({ error: 'Missing bucket' }, { status: 400 });
const [domain] = await db.select().from(domains).where(eq(domains.bucket, bucket));
if (!domain) return NextResponse.json({ error: 'Domain not found' }, { status: 404 });
const unprocessed = await db.select({ s3Key: emails.s3Key }).from(emails).where(eq(emails.processed, false));
let count = 0;
for (const em of unprocessed) {
// Call POST /api/email internally for resend (updates DB/S3)
await fetch(`${req.headers.get('origin')}/api/email`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: req.headers.get('Authorization')! },
body: JSON.stringify({ bucket, key: em.s3Key }),
});
count++;
}
return NextResponse.json({ message: `Resent ${count} emails` });
}