import { NextResponse } from 'next/server'; import { eq } from 'drizzle-orm'; import { withAuth, withPermission } from '@/lib/api/helpers'; import { db } from '@/lib/db'; import { emailAccounts } from '@/lib/db/schema/email'; import { errorResponse, ForbiddenError, NotFoundError } from '@/lib/errors'; import { getQueue } from '@/lib/queue'; export const POST = withAuth( withPermission('email', 'view', async (_req, ctx, params) => { try { const accountId = params.accountId!; // Owner check: the sibling toggle/disconnect endpoints already enforce // account.userId === ctx.userId. Without the same check here, any // user with `email:view` could force IMAP sync against a foreign // account, advancing lastSyncAt (data-loss risk on the legitimate // owner's next sync) and triggering work using the foreign user's // decrypted credentials. const account = await db.query.emailAccounts.findFirst({ where: eq(emailAccounts.id, accountId), }); if (!account) throw new NotFoundError('Email account'); if (account.userId !== ctx.userId) { throw new ForbiddenError('You do not own this email account'); } const queue = getQueue('email'); const job = await queue.add('inbox-sync', { accountId }); return NextResponse.json({ data: { jobId: job.id } }, { status: 202 }); } catch (error) { return errorResponse(error); } }), );