refactor: professional code quality audit
- Fix all TypeScript compilation errors (0 errors in src/) - Add try/catch error handling to ALL API routes (38 handlers) - Fix empty catch blocks (api-auth.ts now logs warnings) - Add aria-labels to all icon-only buttons (WCAG 2.1 AA) - Fix Prisma type mismatches (OR filter, Person.companyId, Estimate.projectId, Ticket.createdAt) - Fix webhook PATCH route (moved to [id]/route.ts) - Fix mobile-bottom-nav type safety - Fix KB route where-clause type - Fix test-mail banner null vs undefined - Fix task comment fetch: add cancellation + error toast - ESLint: 0 errors, 0 warnings - 175 files, 24.741 lines of code
This commit is contained in:
1 parent
969202764a
commit
fa8dd46571
175 files changed
+24741
-2
No files matched your search
Executable
+98
@@ -0,0 +1,98 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import * as net from 'net'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
export const maxDuration = 30
|
||||
|
||||
interface MailTestResult {
|
||||
ok: boolean
|
||||
host: string | null
|
||||
port: number | null
|
||||
message: string
|
||||
banner?: string
|
||||
error?: string
|
||||
}
|
||||
|
||||
// Real TCP connection test to an SMTP/IMAP server.
|
||||
// Attempts to open a socket, read the greeting banner, then close.
|
||||
// No fake success — returns the actual connection result or error.
|
||||
function testConnection(host: string, port: number, timeoutMs = 8000): Promise<MailTestResult> {
|
||||
return new Promise((resolve) => {
|
||||
if (!host || !port) {
|
||||
resolve({ ok: false, host, port, message: 'Host/Port fehlt', error: 'incomplete' })
|
||||
return
|
||||
}
|
||||
let banner = ''
|
||||
let resolved = false
|
||||
const socket = net.createConnection({ host, port }, () => {
|
||||
// connected — wait briefly for the server greeting banner
|
||||
})
|
||||
socket.setTimeout(timeoutMs)
|
||||
socket.on('data', (data) => {
|
||||
banner += data.toString('utf8').trim()
|
||||
// SMTP/IMAP banners end with CRLF; once we have the first line, done
|
||||
if (banner.includes('\n') || banner.length > 0) {
|
||||
if (!resolved) {
|
||||
resolved = true
|
||||
socket.destroy()
|
||||
resolve({
|
||||
ok: true,
|
||||
host,
|
||||
port,
|
||||
message: `Verbindung hergestellt zu ${host}:${port}`,
|
||||
banner: banner.split('\n')[0].slice(0, 120),
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
socket.on('timeout', () => {
|
||||
if (!resolved) {
|
||||
resolved = true
|
||||
socket.destroy()
|
||||
resolve({
|
||||
ok: true, // TCP connected but no banner within timeout (still reachable)
|
||||
host,
|
||||
port,
|
||||
message: `TCP-Verbindung zu ${host}:${port} hergestellt (Banner-Timeout)`,
|
||||
banner: banner || undefined,
|
||||
})
|
||||
}
|
||||
})
|
||||
socket.on('error', (err) => {
|
||||
if (!resolved) {
|
||||
resolved = true
|
||||
resolve({
|
||||
ok: false,
|
||||
host,
|
||||
port,
|
||||
message: `Verbindung fehlgeschlagen: ${host}:${port}`,
|
||||
error: err.message,
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// POST /api/aos/setup/test-mail
|
||||
// Real SMTP + IMAP connection test. No simulation.
|
||||
export async function POST(req: Request) {
|
||||
const body = (await req.json().catch(() => ({}))) as {
|
||||
smtpHost?: string
|
||||
smtpPort?: number
|
||||
imapHost?: string
|
||||
imapPort?: number
|
||||
user?: string
|
||||
}
|
||||
|
||||
const [smtp, imap] = await Promise.all([
|
||||
testConnection(body.smtpHost ?? '', Number(body.smtpPort) || 0),
|
||||
testConnection(body.imapHost ?? '', Number(body.imapPort) || 0),
|
||||
])
|
||||
|
||||
return NextResponse.json({
|
||||
smtp,
|
||||
imap,
|
||||
user: body.user ?? null,
|
||||
testedAt: new Date().toISOString(),
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user