File size: 2,085 Bytes
656ac31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/**
 * Shared API route utility functions.
 * Eliminates duplication across stocks, trading-signals, and analysis routes.
 */

/**
 * Safely converts an unknown value to a finite number, or returns null.
 */
export function toNumber(value: unknown): number | null {
  const n = typeof value === 'number' ? value : Number(value)
  return Number.isFinite(n) ? n : null
}

/**
 * Picks the first present key from an object.
 * Useful when upstream APIs return data under varying field names.
 */
export function pick<T extends Record<string, unknown>>(
  obj: T | null | undefined,
  keys: string[]
): unknown {
  if (!obj) return undefined
  for (const k of keys) {
    if (k in obj) return obj[k]
  }
  return undefined
}

/**
 * Extracts and validates a stock symbol from Next.js dynamic route params.
 * Returns uppercase sanitized symbol or null if invalid.
 */
export async function getSymbolFromParams(
  params: Promise<{ symbol: string }> | { symbol: string }
): Promise<string | null> {
  const resolved = await params
  const raw = resolved?.symbol
  if (!raw || typeof raw !== 'string') return null
  const symbol = raw.toUpperCase().trim()
  // Only allow alphanumeric + dot (e.g. "THYAO", "XU100.IS")
  if (!/^[A-Z0-9.]{1,20}$/.test(symbol)) return null
  return symbol
}

/**
 * Sanitizes upstream error messages to prevent leaking internal details.
 * Strips file paths, stack traces, and internal URLs.
 */
export function sanitizeErrorMessage(error: unknown): string {
  if (!error) return 'Bilinmeyen hata'
  const message = error instanceof Error ? error.message : String(error)
  
  // Strip file paths (unix/windows)
  let sanitized = message.replace(/\/[\w./\\-]+\.(ts|js|tsx|jsx|py|json)/gi, '[path]')
  // Strip stack traces
  sanitized = sanitized.replace(/at\s+\w+.*\(.*\)/g, '')
  // Strip internal URLs (keep only the error context)
  sanitized = sanitized.replace(/https?:\/\/[^\s"')]+/g, '[url]')
  // Truncate to 200 chars
  if (sanitized.length > 200) {
    sanitized = sanitized.slice(0, 200) + '...'
  }
  return sanitized.trim() || 'Bilinmeyen hata'
}