Skip to content

Commit 74126b3

Browse files
Refactor settings usage across components for improved configuration management
Signed-off-by: Shahm Najeeb <Shahm_Najeeb@outlook.com>
1 parent bf507f3 commit 74126b3

12 files changed

Lines changed: 45 additions & 90 deletions

File tree

app/api/accounts/[accountId]/emails/[uid]/attachments/[partId]/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {type NextRequest, NextResponse} from "next/server"
1010
import {requireAuth} from "@/lib/auth"
1111
import {getAttachment} from "@/lib/imap-service"
1212
import {checkRateLimit} from "@/lib/rate-limit"
13+
import {RateLimitSettings} from "@/lib/settings"
1314

1415
// Force Node.js runtime
1516
export const runtime = 'nodejs'
@@ -34,7 +35,7 @@ export async function GET(
3435

3536
// Rate limiting
3637
const ip = request.headers.get("x-forwarded-for") || request.headers.get("x-real-ip") || "unknown"
37-
const rateLimit = checkRateLimit(`attachment:${ip}`, 10, 60 * 1000) // 10 per minute
38+
const rateLimit = checkRateLimit(`attachment:${ip}`, RateLimitSettings.attachmentsLimit, RateLimitSettings.windowMs)
3839

3940
if (!rateLimit.allowed) {
4041
return NextResponse.json(

app/api/accounts/[accountId]/emails/[uid]/route.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {type NextRequest, NextResponse} from "next/server"
1111
import {requireAuth} from "@/lib/auth"
1212
import {getEmail} from "@/lib/imap-service"
1313
import {checkRateLimit} from "@/lib/rate-limit"
14-
import {CacheSettings} from "@/lib/settings"
14+
import {CacheSettings, RateLimitSettings} from "@/lib/settings"
1515

1616
// Force Node.js runtime (required for cookies())
1717
export const runtime = 'nodejs'
@@ -42,7 +42,7 @@ export async function GET(request: NextRequest, {params}: { params: Promise<{ ac
4242

4343
// Rate limiting
4444
const ip = request.headers.get("x-forwarded-for") || request.headers.get("x-real-ip") || "unknown"
45-
const rateLimit = checkRateLimit(`email:${ip}`, 30, 60 * 1000) // 30 per minute
45+
const rateLimit = checkRateLimit(`email:${ip}`, RateLimitSettings.emailLimit, RateLimitSettings.windowMs)
4646

4747
if (!rateLimit.allowed) {
4848
return NextResponse.json(

app/api/accounts/[accountId]/emails/route.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {type NextRequest, NextResponse} from "next/server"
1111
import {requireAuth} from "@/lib/auth"
1212
import {listEmails} from "@/lib/imap-service"
1313
import {checkRateLimit} from "@/lib/rate-limit"
14-
import {CacheSettings} from "@/lib/settings"
14+
import {CacheSettings, PaginationSettings, RateLimitSettings} from "@/lib/settings"
1515

1616
// Force Node.js runtime (required for cookies())
1717
export const runtime = 'nodejs'
@@ -42,7 +42,7 @@ export async function GET(request: NextRequest, {params}: { params: Promise<{ ac
4242

4343
// Rate limiting
4444
const ip = request.headers.get("x-forwarded-for") || request.headers.get("x-real-ip") || "unknown"
45-
const rateLimit = checkRateLimit(`emails:${ip}`, 20, 60 * 1000) // 20 per minute
45+
const rateLimit = checkRateLimit(`emails:${ip}`, RateLimitSettings.emailsLimit, RateLimitSettings.windowMs)
4646

4747
if (!rateLimit.allowed) {
4848
return NextResponse.json(
@@ -59,7 +59,10 @@ export async function GET(request: NextRequest, {params}: { params: Promise<{ ac
5959
// Parse query params
6060
const searchParams = request.nextUrl.searchParams
6161
const folder = searchParams.get("folder")
62-
const limit = Math.min(Number.parseInt(searchParams.get("limit") || "50", 10), 100) // Max 100
62+
const limit = Math.min(
63+
Number.parseInt(searchParams.get("limit") || String(PaginationSettings.defaultLimit), 10),
64+
PaginationSettings.maxLimit
65+
)
6366
const offset = Number.parseInt(searchParams.get("offset") || "0", 10)
6467

6568
if (!folder) {

app/api/accounts/[accountId]/folders/route.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {type NextRequest, NextResponse} from "next/server"
1111
import {requireAuth} from "@/lib/auth"
1212
import {listFolders} from "@/lib/imap-service"
1313
import {checkRateLimit} from "@/lib/rate-limit"
14-
import {CacheSettings} from "@/lib/settings"
14+
import {CacheSettings, RateLimitSettings} from "@/lib/settings"
1515

1616
export async function GET(request: NextRequest, {params}: { params: Promise<{ accountId: string }> }) {
1717
try {
@@ -29,7 +29,7 @@ export async function GET(request: NextRequest, {params}: { params: Promise<{ ac
2929

3030
// Rate limiting
3131
const ip = request.headers.get("x-forwarded-for") || request.headers.get("x-real-ip") || "unknown"
32-
const rateLimit = checkRateLimit(`folders:${ip}`, 30, 60 * 1000) // 30 per minute
32+
const rateLimit = checkRateLimit(`folders:${ip}`, RateLimitSettings.foldersLimit, RateLimitSettings.windowMs)
3333

3434
if (!rateLimit.allowed) {
3535
console.log("[API] /api/accounts/[accountId]/folders - Rate limit exceeded")

app/api/accounts/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {type NextRequest, NextResponse} from "next/server"
1111
import {requireAuth} from "@/lib/auth"
1212
import {getAccountList} from "@/lib/imap-config"
1313
import {checkRateLimit} from "@/lib/rate-limit"
14+
import {RateLimitSettings} from "@/lib/settings"
1415

1516
// Force Node.js runtime (required for cookies())
1617
export const runtime = 'nodejs'
@@ -44,7 +45,7 @@ export async function GET(request: NextRequest) {
4445

4546
// Rate limiting
4647
const ip = request.headers.get("x-forwarded-for") || request.headers.get("x-real-ip") || "unknown"
47-
const rateLimit = checkRateLimit(`accounts:${ip}`, 30, 60 * 1000) // 30 per minute
48+
const rateLimit = checkRateLimit(`accounts:${ip}`, RateLimitSettings.accountsLimit, RateLimitSettings.windowMs)
4849

4950
if (!rateLimit.allowed) {
5051
console.log("[API] /api/accounts - Rate limit exceeded for IP:", ip)

components/dashboard-client.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {Alert, AlertDescription} from "@/components/ui/alert"
1414
import {AlertCircle} from "lucide-react"
1515
import {Account} from "@/types";
1616
import {Cache} from "@/lib/cache"
17+
import {UISettings} from "@/lib/settings"
1718

1819
export function DashboardClient() {
1920
const [accounts, setAccounts] = useState<Account[]>([])
@@ -153,7 +154,7 @@ export function DashboardClient() {
153154

154155
<div className="flex h-[calc(100vh-4rem)]">
155156
{/* Folder sidebar */}
156-
<div className="w-64 border-r bg-muted/30 overflow-y-auto">
157+
<div className="border-r bg-muted/30 overflow-y-auto" style={{width: `${UISettings.sidebarWidth}px`}}>
157158
{selectedAccount && (
158159
<FolderList
159160
accountId={selectedAccount}
@@ -172,7 +173,8 @@ export function DashboardClient() {
172173
</div>
173174

174175
{/* Email list */}
175-
<div className="w-96 border-r bg-background overflow-y-auto">
176+
<div className="border-r bg-background overflow-y-auto"
177+
style={{width: `${UISettings.emailListWidth}px`}}>
176178
{selectedAccount && selectedFolder && (
177179
<EmailList
178180
accountId={selectedAccount}

components/email-viewer.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {Download, Paperclip} from "lucide-react"
1515
import {EmailAddress, EmailAttachment, EmailDetail, EmailViewerProps} from "@/types";
1616
import {Cache} from "@/lib/cache"
1717
import {formatPlainText} from "@/lib/text-formatter"
18+
import {CacheSettings, EmailDisplaySettings} from "@/lib/settings"
1819

1920
export function EmailViewer({accountId, folder, uid}: EmailViewerProps) {
2021
const [email, setEmail] = useState<EmailDetail | null>(null)
@@ -57,7 +58,7 @@ export function EmailViewer({accountId, folder, uid}: EmailViewerProps) {
5758
}
5859

5960
// Cache for 10 minutes (emails don't change often)
60-
Cache.set(cacheKey, data.email, 10 * 60 * 1000)
61+
Cache.set(cacheKey, data.email, CacheSettings.client.email)
6162

6263
setEmail(data.email)
6364
setLoading(false)
@@ -73,7 +74,7 @@ export function EmailViewer({accountId, folder, uid}: EmailViewerProps) {
7374

7475
// Auto-detect best view mode
7576
const defaultTab = useMemo(() => {
76-
if (!email) return "text"
77+
if (!email) return EmailDisplaySettings.defaultView
7778

7879
// If only text exists, use text
7980
if (email.text && !email.html) return "text"
@@ -101,7 +102,7 @@ export function EmailViewer({accountId, folder, uid}: EmailViewerProps) {
101102
return hasFormatting ? "html" : "text"
102103
}
103104

104-
return "text"
105+
return EmailDisplaySettings.defaultView
105106
}, [email])
106107

107108
async function downloadAttachment(att: EmailAttachment) {
@@ -162,7 +163,12 @@ export function EmailViewer({accountId, folder, uid}: EmailViewerProps) {
162163

163164
function formatAddress(addr?: EmailAddress[]): string {
164165
if (!addr || addr.length === 0) return "Unknown"
165-
return addr.map((a) => a.name || a.address).join(", ")
166+
return addr.map((a) => {
167+
if (EmailDisplaySettings.showEmailAddresses && a.name && a.address) {
168+
return `${a.name} <${a.address}>`
169+
}
170+
return a.name || a.address || "Unknown"
171+
}).join(", ")
166172
}
167173

168174
return (
@@ -187,7 +193,7 @@ export function EmailViewer({accountId, folder, uid}: EmailViewerProps) {
187193
)}
188194
<div className="flex gap-2">
189195
<span className="text-muted-foreground w-16">Date:</span>
190-
<span>{email.date ? format(new Date(email.date), "PPpp") : "Unknown"}</span>
196+
<span>{email.date ? format(new Date(email.date), EmailDisplaySettings.dateFormat) : "Unknown"}</span>
191197
</div>
192198
</div>
193199

lib/auth.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import bcrypt from "bcryptjs"
1313
import {jwtVerify, SignJWT} from "jose"
1414
import {cookies} from "next/headers"
1515
import {SessionData} from "@/types/server";
16+
import {SecuritySettings} from "@/lib/settings";
1617

1718
// Master password hash stored in environment variable
1819
// Generate with: bcrypt.hash('your-password', 10)
@@ -40,27 +41,25 @@ const SESSION_SECRET = process.env.SESSION_SECRET
4041
* Never logs the password or hash
4142
*/
4243
export async function verifyMasterPassword(password: string): Promise<boolean> {
43-
/*
4444
console.log("[AUTH DEBUG] verifyMasterPassword called")
4545
console.log("[AUTH DEBUG] Password received:", {
4646
type: typeof password,
4747
length: password?.length,
4848
trimmedLength: password?.trim().length,
4949
})
50-
*/
50+
5151

5252
if (!validateEnvironment() || !MASTER_PASSWORD_BCRYPT_HASH) {
5353
console.error("[AUTH] Environment validation failed")
5454
throw new Error("Environment not properly configured")
5555
}
5656

57-
/*
5857
console.log("[AUTH DEBUG] Environment hash exists:", !!MASTER_PASSWORD_BCRYPT_HASH)
5958
console.log("[AUTH DEBUG] Hash starts with $2:", MASTER_PASSWORD_BCRYPT_HASH?.startsWith('$2'))
6059
console.log("[AUTH DEBUG] Hash length:", MASTER_PASSWORD_BCRYPT_HASH?.length)
6160
console.log("[AUTH DEBUG] Hash first 10 chars:", MASTER_PASSWORD_BCRYPT_HASH?.substring(0, 10))
6261
console.log("[AUTH DEBUG] Hash last 10 chars:", MASTER_PASSWORD_BCRYPT_HASH?.substring(MASTER_PASSWORD_BCRYPT_HASH.length - 10))
63-
*/
62+
6463
const result = await bcrypt.compare(password, MASTER_PASSWORD_BCRYPT_HASH)
6564
console.log("[AUTH] bcrypt.compare result:", result)
6665

@@ -69,15 +68,15 @@ export async function verifyMasterPassword(password: string): Promise<boolean> {
6968

7069
/**
7170
* Create a new session token with JWT
72-
* Tokens expire after 8 hours
71+
* Tokens expire after set timeout
7372
*/
7473
export async function createSessionToken(): Promise<string> {
7574
if (!validateEnvironment() || !SESSION_SECRET) {
7675
throw new Error("Environment not properly configured")
7776
}
7877

7978
const issuedAt = Date.now()
80-
const expiresAt = issuedAt + 8 * 60 * 60 * 1000 // 8 hours
79+
const expiresAt = issuedAt + SecuritySettings.sessionTimeout
8180

8281
return await new SignJWT({
8382
authenticated: true,
@@ -119,7 +118,7 @@ export async function setSessionCookie(token: string): Promise<void> {
119118
httpOnly: true,
120119
secure: process.env.NODE_ENV === "production",
121120
sameSite: "strict",
122-
maxAge: 8 * 60 * 60, // 8 hours
121+
maxAge: SecuritySettings.sessionTimeout, // 8 hours
123122
path: "/",
124123
})
125124
}

lib/imap-service.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {simpleParser} from "mailparser"
1515
import {getAccountById} from "./imap-config"
1616
import DOMPurify from "isomorphic-dompurify"
1717
import {EmailCount, EmailDetail, EmailFolder, GetAttachmentParams, GetEmailParams, ListEmailsParams} from "@/types";
18-
import {FolderSettings} from "./settings"
18+
import {FolderSettings, ImapSettings} from "./settings"
1919

2020
/**
2121
* Create IMAP connection
@@ -58,9 +58,8 @@ function createImapConnection(accountId: string): Promise<Imap> {
5858
port: account.imap.port,
5959
tls: account.imap.secure,
6060
tlsOptions: {rejectUnauthorized: true},
61-
// Connection timeout
62-
connTimeout: 30000,
63-
authTimeout: 10000,
61+
connTimeout: ImapSettings.connectionTimeout,
62+
authTimeout: ImapSettings.authTimeout,
6463
})
6564

6665
imap.once("ready", () => {

lib/rate-limit.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* - Exponential backoff on repeated failures
88
*/
99
import {RateLimitEntry, RateLimitResult} from "@/types/server";
10+
import {RateLimitSettings, SecuritySettings} from "./settings";
1011

1112

1213
// In-memory store (use Redis in production for multi-instance deployments)
@@ -37,9 +38,9 @@ setInterval(
3738
*/
3839
export function checkRateLimit(
3940
identifier: string,
40-
maxAttempts: number = 10,
41-
windowMs: number = 15 * 60 * 1000, // 15 minutes
42-
blockDurationMs: number = 30 * 60 * 1000, // 30 minutes
41+
maxAttempts: number = SecuritySettings.maxLoginAttempts,
42+
windowMs: number = RateLimitSettings.windowMs,
43+
blockDurationMs: number = SecuritySettings.lockoutDuration,
4344
): RateLimitResult {
4445
const now = Date.now()
4546
const entry = store.get(identifier)

0 commit comments

Comments
 (0)