@narcisbodea/smstunnel-sdk 1.1.4 → 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/angular/index.d.mts +2 -6
- package/dist/angular/index.d.ts +2 -6
- package/dist/angular/index.js +27 -49
- package/dist/angular/index.js.map +1 -1
- package/dist/angular/index.mjs +27 -49
- package/dist/angular/index.mjs.map +1 -1
- package/dist/index.d.mts +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.d.mts +1 -3
- package/dist/react/index.d.ts +1 -3
- package/dist/react/index.js +3 -3
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +3 -3
- package/dist/react/index.mjs.map +1 -1
- package/dist/server/index.d.mts +0 -1
- package/dist/server/index.d.ts +0 -1
- package/dist/server/index.js +3 -3
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +3 -3
- package/dist/server/index.mjs.map +1 -1
- package/dist/vue/index.d.mts +0 -10
- package/dist/vue/index.d.ts +0 -10
- package/dist/vue/index.js +3 -5
- package/dist/vue/index.js.map +1 -1
- package/dist/vue/index.mjs +3 -5
- package/dist/vue/index.mjs.map +1 -1
- package/package.json +14 -4
package/dist/react/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/react/SmsTunnelPairing.tsx","../../src/react/useSmsTunnel.ts","../../src/react/QrCodeCanvas.tsx","../../src/shared/labels.ts"],"sourcesContent":["import { useState } from 'react';\r\nimport { useSmsTunnel } from './useSmsTunnel';\r\nimport { QrCodeCanvas } from './QrCodeCanvas';\r\nimport { EN_LABELS } from './types';\r\nimport type { SmsTunnelPairingProps } from './types';\r\n\r\n// Inline styles - no external CSS dependency\r\nconst styles = {\r\n container: {\r\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\r\n fontSize: '14px',\r\n color: '#1f2937',\r\n } as React.CSSProperties,\r\n card: {\r\n border: '1px solid #e5e7eb',\r\n borderRadius: '12px',\r\n padding: '24px',\r\n backgroundColor: '#ffffff',\r\n } as React.CSSProperties,\r\n title: {\r\n fontSize: '18px',\r\n fontWeight: 600,\r\n color: '#111827',\r\n marginBottom: '4px',\r\n } as React.CSSProperties,\r\n subtitle: {\r\n fontSize: '13px',\r\n color: '#6b7280',\r\n marginBottom: '20px',\r\n } as React.CSSProperties,\r\n section: {\r\n border: '1px solid #e5e7eb',\r\n borderRadius: '8px',\r\n padding: '16px',\r\n marginBottom: '16px',\r\n } as React.CSSProperties,\r\n label: {\r\n display: 'block',\r\n fontSize: '13px',\r\n fontWeight: 500,\r\n color: '#374151',\r\n marginBottom: '6px',\r\n } as React.CSSProperties,\r\n inputRow: {\r\n display: 'flex',\r\n gap: '8px',\r\n } as React.CSSProperties,\r\n input: {\r\n flex: 1,\r\n border: '1px solid #d1d5db',\r\n borderRadius: '8px',\r\n padding: '6px 12px',\r\n fontSize: '13px',\r\n outline: 'none',\r\n } as React.CSSProperties,\r\n button: {\r\n padding: '6px 16px',\r\n borderRadius: '8px',\r\n border: 'none',\r\n fontSize: '13px',\r\n fontWeight: 500,\r\n cursor: 'pointer',\r\n backgroundColor: '#4f46e5',\r\n color: '#ffffff',\r\n transition: 'opacity 0.2s',\r\n } as React.CSSProperties,\r\n buttonSecondary: {\r\n padding: '6px 16px',\r\n borderRadius: '8px',\r\n border: '1px solid #d1d5db',\r\n fontSize: '13px',\r\n fontWeight: 500,\r\n cursor: 'pointer',\r\n backgroundColor: '#ffffff',\r\n color: '#374151',\r\n } as React.CSSProperties,\r\n buttonDanger: {\r\n padding: '6px',\r\n borderRadius: '8px',\r\n border: 'none',\r\n cursor: 'pointer',\r\n backgroundColor: 'transparent',\r\n color: '#9ca3af',\r\n transition: 'color 0.2s',\r\n } as React.CSSProperties,\r\n pairedBanner: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: '12px',\r\n border: '1px solid #bbf7d0',\r\n backgroundColor: '#f0fdf4',\r\n borderRadius: '8px',\r\n padding: '16px',\r\n marginBottom: '16px',\r\n } as React.CSSProperties,\r\n pairedIcon: {\r\n width: '40px',\r\n height: '40px',\r\n borderRadius: '50%',\r\n backgroundColor: '#dcfce7',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n color: '#16a34a',\r\n fontSize: '18px',\r\n flexShrink: 0,\r\n } as React.CSSProperties,\r\n unpairedBanner: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: '12px',\r\n border: '1px solid #fde68a',\r\n backgroundColor: '#fefce8',\r\n borderRadius: '8px',\r\n padding: '16px',\r\n marginBottom: '16px',\r\n } as React.CSSProperties,\r\n qrContainer: {\r\n textAlign: 'center' as const,\r\n padding: '16px',\r\n } as React.CSSProperties,\r\n qrFrame: {\r\n display: 'inline-block',\r\n border: '2px dashed #a5b4fc',\r\n borderRadius: '12px',\r\n padding: '16px',\r\n backgroundColor: '#ffffff',\r\n marginBottom: '12px',\r\n } as React.CSSProperties,\r\n spinner: {\r\n display: 'inline-block',\r\n width: '12px',\r\n height: '12px',\r\n border: '2px solid #d1d5db',\r\n borderTopColor: '#4f46e5',\r\n borderRadius: '50%',\r\n animation: 'smstunnel-spin 0.8s linear infinite',\r\n marginRight: '8px',\r\n verticalAlign: 'middle',\r\n } as React.CSSProperties,\r\n error: {\r\n color: '#dc2626',\r\n fontSize: '13px',\r\n marginBottom: '12px',\r\n } as React.CSSProperties,\r\n success: {\r\n color: '#16a34a',\r\n fontSize: '12px',\r\n marginTop: '8px',\r\n } as React.CSSProperties,\r\n errorSmall: {\r\n color: '#dc2626',\r\n fontSize: '12px',\r\n marginTop: '8px',\r\n } as React.CSSProperties,\r\n};\r\n\r\n// Inject keyframes once\r\nlet stylesInjected = false;\r\nfunction injectKeyframes() {\r\n if (stylesInjected || typeof document === 'undefined') return;\r\n const style = document.createElement('style');\r\n style.textContent = `@keyframes smstunnel-spin { to { transform: rotate(360deg); } }`;\r\n document.head.appendChild(style);\r\n stylesInjected = true;\r\n}\r\n\r\nexport function SmsTunnelPairing({\r\n apiBaseUrl,\r\n getAuthHeaders,\r\n labels = EN_LABELS,\r\n onPaired,\r\n onUnpaired,\r\n showTestSms = true,\r\n showServerUrlInput = true,\r\n qrSize = 220,\r\n routePrefix = 'smstunnel',\r\n className,\r\n}: SmsTunnelPairingProps) {\r\n injectKeyframes();\r\n\r\n const tunnel = useSmsTunnel({ apiBaseUrl, getAuthHeaders, routePrefix });\r\n\r\n const [localServerUrl, setLocalServerUrl] = useState('');\r\n const [savingUrl, setSavingUrl] = useState(false);\r\n const [testPhone, setTestPhone] = useState('');\r\n const [testMsg, setTestMsg] = useState('Test SMS');\r\n const [testResult, setTestResult] = useState<{\r\n success: boolean;\r\n error?: string;\r\n messageId?: string;\r\n } | null>(null);\r\n const [sendingTest, setSendingTest] = useState(false);\r\n\r\n // Sync server URL once loaded\r\n if (tunnel.serverUrl && !localServerUrl) {\r\n setLocalServerUrl(tunnel.serverUrl);\r\n }\r\n\r\n const handleSaveUrl = async () => {\r\n if (!localServerUrl.trim()) return;\r\n setSavingUrl(true);\r\n await tunnel.updateServerUrl(localServerUrl.trim().replace(/\\/$/, ''));\r\n setSavingUrl(false);\r\n };\r\n\r\n const handleUnpair = async () => {\r\n if (!confirm(labels.unpairConfirm)) return;\r\n await tunnel.unpair();\r\n onUnpaired?.();\r\n };\r\n\r\n const handleGenerateQr = async () => {\r\n await tunnel.generateQr();\r\n };\r\n\r\n // Watch for pairing completion\r\n const prevStatus = tunnel.status;\r\n if (prevStatus === 'paired' && tunnel.deviceName) {\r\n // Fire onPaired if status changed\r\n }\r\n\r\n const handleTestSms = async () => {\r\n if (!testPhone.trim()) return;\r\n setSendingTest(true);\r\n setTestResult(null);\r\n const result = await tunnel.sendTestSms(testPhone.trim(), testMsg);\r\n setTestResult(result);\r\n setSendingTest(false);\r\n };\r\n\r\n if (tunnel.status === 'loading') {\r\n return (\r\n <div style={styles.container} className={className}>\r\n <div style={styles.card}>\r\n <div style={{ textAlign: 'center', padding: '24px' }}>\r\n <span style={styles.spinner} />\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div style={styles.container} className={className}>\r\n <div style={styles.card}>\r\n <div style={styles.title}>{labels.title}</div>\r\n <div style={styles.subtitle}>{labels.description}</div>\r\n\r\n {/* Server URL */}\r\n {showServerUrlInput && (\r\n <div style={styles.section}>\r\n <label style={styles.label}>{labels.serverUrlLabel}</label>\r\n <div style={styles.inputRow}>\r\n <input\r\n value={localServerUrl}\r\n onChange={(e) => setLocalServerUrl(e.target.value)}\r\n placeholder={labels.serverUrlPlaceholder}\r\n style={styles.input}\r\n />\r\n <button\r\n style={{\r\n ...styles.button,\r\n opacity: savingUrl || !localServerUrl.trim() ? 0.5 : 1,\r\n }}\r\n onClick={handleSaveUrl}\r\n disabled={savingUrl || !localServerUrl.trim()}\r\n >\r\n {labels.saveButton}\r\n </button>\r\n </div>\r\n </div>\r\n )}\r\n\r\n {tunnel.error && <div style={styles.error}>{tunnel.error}</div>}\r\n\r\n {tunnel.status === 'paired' ? (\r\n <div>\r\n {/* Paired banner */}\r\n <div style={styles.pairedBanner}>\r\n <div style={styles.pairedIcon}>✓</div>\r\n <div style={{ flex: 1 }}>\r\n <div style={{ fontSize: '14px', fontWeight: 600, color: '#166534' }}>\r\n {labels.pairedStatus}\r\n </div>\r\n <div style={{ fontSize: '12px', color: '#16a34a' }}>\r\n {labels.deviceLabel}: {tunnel.deviceName || 'Android Phone'}\r\n </div>\r\n </div>\r\n <button\r\n onClick={handleUnpair}\r\n style={styles.buttonDanger}\r\n title={labels.unpairButton}\r\n >\r\n ✕\r\n </button>\r\n </div>\r\n\r\n {/* Test SMS */}\r\n {showTestSms && (\r\n <div style={styles.section}>\r\n <div\r\n style={{\r\n fontSize: '13px',\r\n fontWeight: 600,\r\n color: '#374151',\r\n marginBottom: '12px',\r\n }}\r\n >\r\n {labels.testSmsTitle}\r\n </div>\r\n <div style={styles.inputRow}>\r\n <input\r\n value={testPhone}\r\n onChange={(e) => setTestPhone(e.target.value)}\r\n placeholder={labels.testPhonePlaceholder}\r\n style={styles.input}\r\n />\r\n <input\r\n value={testMsg}\r\n onChange={(e) => setTestMsg(e.target.value)}\r\n placeholder={labels.testMessagePlaceholder}\r\n style={styles.input}\r\n />\r\n <button\r\n style={{\r\n ...styles.button,\r\n opacity: sendingTest || !testPhone.trim() ? 0.5 : 1,\r\n }}\r\n onClick={handleTestSms}\r\n disabled={sendingTest || !testPhone.trim()}\r\n >\r\n {sendingTest ? (\r\n <span style={styles.spinner} />\r\n ) : (\r\n labels.sendTestButton\r\n )}\r\n </button>\r\n </div>\r\n {testResult && (\r\n <div style={testResult.success ? styles.success : styles.errorSmall}>\r\n {testResult.success\r\n ? `${labels.smsSentSuccess} (ID: ${testResult.messageId})`\r\n : `${labels.smsError}: ${testResult.error}`}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n ) : (\r\n <div>\r\n {/* Not paired banner */}\r\n <div style={styles.unpairedBanner}>\r\n <div style={{ fontSize: '24px' }}>📱</div>\r\n <div>\r\n <div style={{ fontSize: '14px', fontWeight: 600, color: '#854d0e' }}>\r\n {labels.notPairedStatus}\r\n </div>\r\n <div style={{ fontSize: '12px', color: '#a16207' }}>\r\n {labels.notPairedDescription}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n {!tunnel.showQr ? (\r\n <button\r\n style={{\r\n ...styles.button,\r\n opacity: tunnel.generating ? 0.5 : 1,\r\n }}\r\n onClick={handleGenerateQr}\r\n disabled={tunnel.generating}\r\n >\r\n {tunnel.generating && <span style={styles.spinner} />}\r\n {labels.connectButton}\r\n </button>\r\n ) : (\r\n <div style={styles.qrContainer}>\r\n <div style={{ fontSize: '13px', color: '#4b5563', marginBottom: '12px' }}>\r\n {labels.scanQrPrompt}\r\n </div>\r\n <div style={styles.qrFrame}>\r\n <QrCodeCanvas value={tunnel.qrData} size={qrSize} />\r\n </div>\r\n <div\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n gap: '8px',\r\n fontSize: '12px',\r\n color: '#9ca3af',\r\n marginBottom: '12px',\r\n }}\r\n >\r\n <span style={styles.spinner} />\r\n {labels.waitingForPairing}\r\n </div>\r\n <button style={styles.buttonSecondary} onClick={tunnel.cancelPairing}>\r\n {labels.cancelButton}\r\n </button>\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { useState, useEffect, useRef, useCallback } from 'react';\r\nimport type { SmsTunnelState, UseSmsTunnelOptions } from './types';\r\n\r\nexport function useSmsTunnel(options: UseSmsTunnelOptions) {\r\n const { apiBaseUrl, getAuthHeaders, routePrefix = 'smstunnel', pollInterval = 3000 } = options;\r\n\r\n const [state, setState] = useState<SmsTunnelState>({\r\n status: 'loading',\r\n serverUrl: '',\r\n deviceName: '',\r\n showQr: false,\r\n qrData: '',\r\n generating: false,\r\n polling: false,\r\n error: '',\r\n });\r\n\r\n const pollRef = useRef<ReturnType<typeof setInterval> | null>(null);\r\n const pairingTokenRef = useRef<string>('');\r\n\r\n const baseUrl = apiBaseUrl.replace(/\\/$/, '');\r\n\r\n const authHeaders = useCallback((): Record<string, string> => {\r\n return getAuthHeaders ? getAuthHeaders() : {};\r\n }, [getAuthHeaders]);\r\n\r\n const fetchStatus = useCallback(async () => {\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/status`, {\r\n headers: authHeaders(),\r\n });\r\n const data = await res.json();\r\n setState((prev) => ({\r\n ...prev,\r\n status: data.paired ? 'paired' : 'unpaired',\r\n serverUrl: data.serverUrl || '',\r\n deviceName: data.deviceName || '',\r\n }));\r\n } catch {\r\n setState((prev) => ({ ...prev, status: 'unpaired' }));\r\n }\r\n }, [baseUrl, routePrefix, authHeaders]);\r\n\r\n useEffect(() => {\r\n fetchStatus();\r\n }, [fetchStatus]);\r\n\r\n // Polling effect\r\n useEffect(() => {\r\n if (!state.polling || !pairingTokenRef.current) return;\r\n\r\n pollRef.current = setInterval(async () => {\r\n try {\r\n const res = await fetch(\r\n `${baseUrl}/${routePrefix}/pairing-status/${pairingTokenRef.current}`,\r\n );\r\n const data = await res.json();\r\n\r\n if (data.status === 'completed') {\r\n setState((prev) => ({ ...prev, polling: false, showQr: false }));\r\n if (pollRef.current) clearInterval(pollRef.current);\r\n // Wait for callback to arrive, then refetch\r\n setTimeout(() => fetchStatus(), 2000);\r\n } else if (data.status === 'expired') {\r\n setState((prev) => ({\r\n ...prev,\r\n polling: false,\r\n showQr: false,\r\n error: 'QR code expired',\r\n }));\r\n if (pollRef.current) clearInterval(pollRef.current);\r\n }\r\n } catch {\r\n // ignore polling errors\r\n }\r\n }, pollInterval);\r\n\r\n return () => {\r\n if (pollRef.current) clearInterval(pollRef.current);\r\n };\r\n }, [state.polling, baseUrl, routePrefix, pollInterval, fetchStatus]);\r\n\r\n const generateQr = useCallback(async () => {\r\n setState((prev) => ({ ...prev, generating: true, error: '' }));\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/create-token`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...authHeaders(),\r\n },\r\n });\r\n const data = await res.json();\r\n\r\n if (!data.success) {\r\n setState((prev) => ({\r\n ...prev,\r\n generating: false,\r\n error: data.error || 'Failed to create QR code',\r\n }));\r\n return;\r\n }\r\n\r\n pairingTokenRef.current = data.token;\r\n setState((prev) => ({\r\n ...prev,\r\n qrData: data.qrData,\r\n showQr: true,\r\n polling: true,\r\n generating: false,\r\n }));\r\n } catch (err: any) {\r\n setState((prev) => ({\r\n ...prev,\r\n generating: false,\r\n error: `Error: ${err.message}`,\r\n }));\r\n }\r\n }, [baseUrl, routePrefix, authHeaders]);\r\n\r\n const cancelPairing = useCallback(() => {\r\n if (pollRef.current) clearInterval(pollRef.current);\r\n pairingTokenRef.current = '';\r\n setState((prev) => ({\r\n ...prev,\r\n showQr: false,\r\n polling: false,\r\n qrData: '',\r\n }));\r\n }, []);\r\n\r\n const unpair = useCallback(async () => {\r\n try {\r\n await fetch(`${baseUrl}/${routePrefix}/unpair`, {\r\n method: 'POST',\r\n headers: authHeaders(),\r\n });\r\n setState((prev) => ({\r\n ...prev,\r\n status: 'unpaired',\r\n deviceName: '',\r\n }));\r\n } catch (err: any) {\r\n setState((prev) => ({ ...prev, error: `Unpair failed: ${err.message}` }));\r\n }\r\n }, [baseUrl, routePrefix, authHeaders]);\r\n\r\n const sendTestSms = useCallback(\r\n async (to: string, message: string) => {\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/send`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...authHeaders(),\r\n },\r\n body: JSON.stringify({ to, message }),\r\n });\r\n return (await res.json()) as { success: boolean; messageId?: string; error?: string };\r\n } catch (err: any) {\r\n return { success: false, error: err.message };\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n const updateServerUrl = useCallback(\r\n async (serverUrl: string) => {\r\n try {\r\n await fetch(`${baseUrl}/${routePrefix}/update-config`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...authHeaders(),\r\n },\r\n body: JSON.stringify({ serverUrl }),\r\n });\r\n setState((prev) => ({ ...prev, serverUrl }));\r\n } catch (err: any) {\r\n setState((prev) => ({ ...prev, error: `Update failed: ${err.message}` }));\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n const refetch = useCallback(() => fetchStatus(), [fetchStatus]);\r\n\r\n // ─── E2E Encryption ────────────────────────────────\r\n\r\n const getDeviceE2EStatus = useCallback(\r\n async (deviceId: string) => {\r\n try {\r\n const res = await fetch(\r\n `${baseUrl}/${routePrefix}/e2e-status/${deviceId}`,\r\n { headers: authHeaders() },\r\n );\r\n return await res.json();\r\n } catch (err: any) {\r\n return { success: false, error: err.message };\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n const getDevicePublicKey = useCallback(\r\n async (deviceId: string) => {\r\n try {\r\n const res = await fetch(\r\n `${baseUrl}/${routePrefix}/public-key/${deviceId}`,\r\n { headers: authHeaders() },\r\n );\r\n return await res.json();\r\n } catch (err: any) {\r\n return { success: false, error: err.message };\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n const verifyDeviceKey = useCallback(\r\n async (deviceId: string, fingerprint: string) => {\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/verify-key`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', ...authHeaders() },\r\n body: JSON.stringify({ deviceId, fingerprint }),\r\n });\r\n return await res.json();\r\n } catch (err: any) {\r\n return { valid: false, needsRePairing: true, error: err.message };\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n const sendEncryptedSms = useCallback(\r\n async (encryptedPayload: string, deviceId: string, is2FA: boolean = false) => {\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/send-encrypted`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', ...authHeaders() },\r\n body: JSON.stringify({ encryptedPayload, deviceId, is2FA }),\r\n });\r\n return await res.json();\r\n } catch (err: any) {\r\n return { success: false, error: err.message };\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n return {\r\n ...state,\r\n generateQr,\r\n cancelPairing,\r\n unpair,\r\n sendTestSms,\r\n updateServerUrl,\r\n refetch,\r\n // E2E\r\n getDeviceE2EStatus,\r\n getDevicePublicKey,\r\n verifyDeviceKey,\r\n sendEncryptedSms,\r\n };\r\n}\r\n","import { useEffect, useRef } from 'react';\r\nimport qrcode from 'qrcode-generator';\r\n\r\nexport interface QrCodeCanvasProps {\r\n value: string;\r\n size?: number;\r\n}\r\n\r\nexport function QrCodeCanvas({ value, size = 200 }: QrCodeCanvasProps) {\r\n const canvasRef = useRef<HTMLCanvasElement>(null);\r\n\r\n useEffect(() => {\r\n if (!canvasRef.current || !value) return;\r\n\r\n const canvas = canvasRef.current;\r\n const ctx = canvas.getContext('2d');\r\n if (!ctx) return;\r\n\r\n const qr = qrcode(0, 'M');\r\n qr.addData(value);\r\n qr.make();\r\n\r\n const moduleCount = qr.getModuleCount();\r\n const cellSize = size / moduleCount;\r\n\r\n canvas.width = size;\r\n canvas.height = size;\r\n ctx.fillStyle = '#ffffff';\r\n ctx.fillRect(0, 0, size, size);\r\n ctx.fillStyle = '#000000';\r\n\r\n for (let row = 0; row < moduleCount; row++) {\r\n for (let col = 0; col < moduleCount; col++) {\r\n if (qr.isDark(row, col)) {\r\n ctx.fillRect(col * cellSize, row * cellSize, cellSize, cellSize);\r\n }\r\n }\r\n }\r\n }, [value, size]);\r\n\r\n return <canvas ref={canvasRef} width={size} height={size} />;\r\n}\r\n","export interface SmsTunnelLabels {\r\n title: string;\r\n description: string;\r\n serverUrlLabel: string;\r\n serverUrlPlaceholder: string;\r\n saveButton: string;\r\n pairedStatus: string;\r\n deviceLabel: string;\r\n unpairButton: string;\r\n unpairConfirm: string;\r\n notPairedStatus: string;\r\n notPairedDescription: string;\r\n connectButton: string;\r\n scanQrPrompt: string;\r\n waitingForPairing: string;\r\n cancelButton: string;\r\n qrExpired: string;\r\n testSmsTitle: string;\r\n testPhonePlaceholder: string;\r\n testMessagePlaceholder: string;\r\n sendTestButton: string;\r\n smsSentSuccess: string;\r\n smsError: string;\r\n}\r\n\r\nexport const EN_LABELS: SmsTunnelLabels = {\r\n title: 'SMS Configuration (SMSTunnel)',\r\n description: 'Connect an Android phone with the SMSTunnel app to send SMS directly from the application.',\r\n serverUrlLabel: 'SMSTunnel Server',\r\n serverUrlPlaceholder: 'https://smstunnel.io',\r\n saveButton: 'Save',\r\n pairedStatus: 'Connected',\r\n deviceLabel: 'Device',\r\n unpairButton: 'Disconnect',\r\n unpairConfirm: 'Are you sure you want to disconnect the SMS device?',\r\n notPairedStatus: 'Not connected',\r\n notPairedDescription: 'Scan the QR code with the SMSTunnel app on your Android phone.',\r\n connectButton: 'Connect phone',\r\n scanQrPrompt: 'Scan this QR code with the SMSTunnel app on your phone:',\r\n waitingForPairing: 'Waiting for connection...',\r\n cancelButton: 'Cancel',\r\n qrExpired: 'QR code has expired. Generate a new one.',\r\n testSmsTitle: 'Send test SMS',\r\n testPhonePlaceholder: 'Phone number (e.g., +1234567890)',\r\n testMessagePlaceholder: 'Message',\r\n sendTestButton: 'Send',\r\n smsSentSuccess: 'SMS sent successfully!',\r\n smsError: 'Error',\r\n};\r\n\r\nexport const RO_LABELS: SmsTunnelLabels = {\r\n title: 'Configurare SMS (SMSTunnel)',\r\n description: 'Conecteaza un telefon Android cu aplicatia SMSTunnel pentru a trimite SMS-uri direct din aplicatie.',\r\n serverUrlLabel: 'Server SMSTunnel',\r\n serverUrlPlaceholder: 'https://smstunnel.io',\r\n saveButton: 'Salveaza',\r\n pairedStatus: 'Conectat',\r\n deviceLabel: 'Dispozitiv',\r\n unpairButton: 'Deconecteaza',\r\n unpairConfirm: 'Sigur doresti sa deconectezi dispozitivul SMS?',\r\n notPairedStatus: 'Neconectat',\r\n notPairedDescription: 'Scaneaza codul QR cu aplicatia SMSTunnel de pe telefonul Android.',\r\n connectButton: 'Conecteaza telefon',\r\n scanQrPrompt: 'Scaneaza acest cod QR cu aplicatia SMSTunnel de pe telefon:',\r\n waitingForPairing: 'Se asteapta conectarea...',\r\n cancelButton: 'Anuleaza',\r\n qrExpired: 'Codul QR a expirat. Genereaza unul nou.',\r\n testSmsTitle: 'Trimite SMS de test',\r\n testPhonePlaceholder: 'Nr. telefon (ex: 0741234567)',\r\n testMessagePlaceholder: 'Mesaj',\r\n sendTestButton: 'Trimite',\r\n smsSentSuccess: 'SMS trimis cu succes!',\r\n smsError: 'Eroare',\r\n};\r\n"],"mappings":"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","names":["useState","useEffect","useRef","jsx","useState"]}
|
|
1
|
+
{"version":3,"sources":["../../src/react/SmsTunnelPairing.tsx","../../src/react/useSmsTunnel.ts","../../src/react/QrCodeCanvas.tsx","../../src/shared/labels.ts"],"sourcesContent":["import { useState } from 'react';\r\nimport { useSmsTunnel } from './useSmsTunnel';\r\nimport { QrCodeCanvas } from './QrCodeCanvas';\r\nimport { EN_LABELS } from './types';\r\nimport type { SmsTunnelPairingProps } from './types';\r\n\r\n// Inline styles - no external CSS dependency\r\nconst styles = {\r\n container: {\r\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\r\n fontSize: '14px',\r\n color: '#1f2937',\r\n } as React.CSSProperties,\r\n card: {\r\n border: '1px solid #e5e7eb',\r\n borderRadius: '12px',\r\n padding: '24px',\r\n backgroundColor: '#ffffff',\r\n } as React.CSSProperties,\r\n title: {\r\n fontSize: '18px',\r\n fontWeight: 600,\r\n color: '#111827',\r\n marginBottom: '4px',\r\n } as React.CSSProperties,\r\n subtitle: {\r\n fontSize: '13px',\r\n color: '#6b7280',\r\n marginBottom: '20px',\r\n } as React.CSSProperties,\r\n section: {\r\n border: '1px solid #e5e7eb',\r\n borderRadius: '8px',\r\n padding: '16px',\r\n marginBottom: '16px',\r\n } as React.CSSProperties,\r\n label: {\r\n display: 'block',\r\n fontSize: '13px',\r\n fontWeight: 500,\r\n color: '#374151',\r\n marginBottom: '6px',\r\n } as React.CSSProperties,\r\n inputRow: {\r\n display: 'flex',\r\n gap: '8px',\r\n } as React.CSSProperties,\r\n input: {\r\n flex: 1,\r\n border: '1px solid #d1d5db',\r\n borderRadius: '8px',\r\n padding: '6px 12px',\r\n fontSize: '13px',\r\n outline: 'none',\r\n } as React.CSSProperties,\r\n button: {\r\n padding: '6px 16px',\r\n borderRadius: '8px',\r\n border: 'none',\r\n fontSize: '13px',\r\n fontWeight: 500,\r\n cursor: 'pointer',\r\n backgroundColor: '#4f46e5',\r\n color: '#ffffff',\r\n transition: 'opacity 0.2s',\r\n } as React.CSSProperties,\r\n buttonSecondary: {\r\n padding: '6px 16px',\r\n borderRadius: '8px',\r\n border: '1px solid #d1d5db',\r\n fontSize: '13px',\r\n fontWeight: 500,\r\n cursor: 'pointer',\r\n backgroundColor: '#ffffff',\r\n color: '#374151',\r\n } as React.CSSProperties,\r\n buttonDanger: {\r\n padding: '6px',\r\n borderRadius: '8px',\r\n border: 'none',\r\n cursor: 'pointer',\r\n backgroundColor: 'transparent',\r\n color: '#9ca3af',\r\n transition: 'color 0.2s',\r\n } as React.CSSProperties,\r\n pairedBanner: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: '12px',\r\n border: '1px solid #bbf7d0',\r\n backgroundColor: '#f0fdf4',\r\n borderRadius: '8px',\r\n padding: '16px',\r\n marginBottom: '16px',\r\n } as React.CSSProperties,\r\n pairedIcon: {\r\n width: '40px',\r\n height: '40px',\r\n borderRadius: '50%',\r\n backgroundColor: '#dcfce7',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n color: '#16a34a',\r\n fontSize: '18px',\r\n flexShrink: 0,\r\n } as React.CSSProperties,\r\n unpairedBanner: {\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: '12px',\r\n border: '1px solid #fde68a',\r\n backgroundColor: '#fefce8',\r\n borderRadius: '8px',\r\n padding: '16px',\r\n marginBottom: '16px',\r\n } as React.CSSProperties,\r\n qrContainer: {\r\n textAlign: 'center' as const,\r\n padding: '16px',\r\n } as React.CSSProperties,\r\n qrFrame: {\r\n display: 'inline-block',\r\n border: '2px dashed #a5b4fc',\r\n borderRadius: '12px',\r\n padding: '16px',\r\n backgroundColor: '#ffffff',\r\n marginBottom: '12px',\r\n } as React.CSSProperties,\r\n spinner: {\r\n display: 'inline-block',\r\n width: '12px',\r\n height: '12px',\r\n border: '2px solid #d1d5db',\r\n borderTopColor: '#4f46e5',\r\n borderRadius: '50%',\r\n animation: 'smstunnel-spin 0.8s linear infinite',\r\n marginRight: '8px',\r\n verticalAlign: 'middle',\r\n } as React.CSSProperties,\r\n error: {\r\n color: '#dc2626',\r\n fontSize: '13px',\r\n marginBottom: '12px',\r\n } as React.CSSProperties,\r\n success: {\r\n color: '#16a34a',\r\n fontSize: '12px',\r\n marginTop: '8px',\r\n } as React.CSSProperties,\r\n errorSmall: {\r\n color: '#dc2626',\r\n fontSize: '12px',\r\n marginTop: '8px',\r\n } as React.CSSProperties,\r\n};\r\n\r\n// Inject keyframes once\r\nlet stylesInjected = false;\r\nfunction injectKeyframes() {\r\n if (stylesInjected || typeof document === 'undefined') return;\r\n const style = document.createElement('style');\r\n style.textContent = `@keyframes smstunnel-spin { to { transform: rotate(360deg); } }`;\r\n document.head.appendChild(style);\r\n stylesInjected = true;\r\n}\r\n\r\nexport function SmsTunnelPairing({\r\n apiBaseUrl,\r\n getAuthHeaders,\r\n labels = EN_LABELS,\r\n onPaired,\r\n onUnpaired,\r\n showTestSms = true,\r\n showServerUrlInput = true,\r\n qrSize = 220,\r\n className,\r\n}: SmsTunnelPairingProps) {\r\n injectKeyframes();\r\n\r\n const tunnel = useSmsTunnel({ apiBaseUrl, getAuthHeaders });\r\n\r\n const [localServerUrl, setLocalServerUrl] = useState('');\r\n const [savingUrl, setSavingUrl] = useState(false);\r\n const [testPhone, setTestPhone] = useState('');\r\n const [testMsg, setTestMsg] = useState('Test SMS');\r\n const [testResult, setTestResult] = useState<{\r\n success: boolean;\r\n error?: string;\r\n messageId?: string;\r\n } | null>(null);\r\n const [sendingTest, setSendingTest] = useState(false);\r\n\r\n // Sync server URL once loaded\r\n if (tunnel.serverUrl && !localServerUrl) {\r\n setLocalServerUrl(tunnel.serverUrl);\r\n }\r\n\r\n const handleSaveUrl = async () => {\r\n if (!localServerUrl.trim()) return;\r\n setSavingUrl(true);\r\n await tunnel.updateServerUrl(localServerUrl.trim().replace(/\\/$/, ''));\r\n setSavingUrl(false);\r\n };\r\n\r\n const handleUnpair = async () => {\r\n if (!confirm(labels.unpairConfirm)) return;\r\n await tunnel.unpair();\r\n onUnpaired?.();\r\n };\r\n\r\n const handleGenerateQr = async () => {\r\n await tunnel.generateQr();\r\n };\r\n\r\n // Watch for pairing completion\r\n const prevStatus = tunnel.status;\r\n if (prevStatus === 'paired' && tunnel.deviceName) {\r\n // Fire onPaired if status changed\r\n }\r\n\r\n const handleTestSms = async () => {\r\n if (!testPhone.trim()) return;\r\n setSendingTest(true);\r\n setTestResult(null);\r\n const result = await tunnel.sendTestSms(testPhone.trim(), testMsg);\r\n setTestResult(result);\r\n setSendingTest(false);\r\n };\r\n\r\n if (tunnel.status === 'loading') {\r\n return (\r\n <div style={styles.container} className={className}>\r\n <div style={styles.card}>\r\n <div style={{ textAlign: 'center', padding: '24px' }}>\r\n <span style={styles.spinner} />\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div style={styles.container} className={className}>\r\n <div style={styles.card}>\r\n <div style={styles.title}>{labels.title}</div>\r\n <div style={styles.subtitle}>{labels.description}</div>\r\n\r\n {/* Server URL */}\r\n {showServerUrlInput && (\r\n <div style={styles.section}>\r\n <label style={styles.label}>{labels.serverUrlLabel}</label>\r\n <div style={styles.inputRow}>\r\n <input\r\n value={localServerUrl}\r\n onChange={(e) => setLocalServerUrl(e.target.value)}\r\n placeholder={labels.serverUrlPlaceholder}\r\n style={styles.input}\r\n />\r\n <button\r\n style={{\r\n ...styles.button,\r\n opacity: savingUrl || !localServerUrl.trim() ? 0.5 : 1,\r\n }}\r\n onClick={handleSaveUrl}\r\n disabled={savingUrl || !localServerUrl.trim()}\r\n >\r\n {labels.saveButton}\r\n </button>\r\n </div>\r\n </div>\r\n )}\r\n\r\n {tunnel.error && <div style={styles.error}>{tunnel.error}</div>}\r\n\r\n {tunnel.status === 'paired' ? (\r\n <div>\r\n {/* Paired banner */}\r\n <div style={styles.pairedBanner}>\r\n <div style={styles.pairedIcon}>✓</div>\r\n <div style={{ flex: 1 }}>\r\n <div style={{ fontSize: '14px', fontWeight: 600, color: '#166534' }}>\r\n {labels.pairedStatus}\r\n </div>\r\n <div style={{ fontSize: '12px', color: '#16a34a' }}>\r\n {labels.deviceLabel}: {tunnel.deviceName || 'Android Phone'}\r\n </div>\r\n </div>\r\n <button\r\n onClick={handleUnpair}\r\n style={styles.buttonDanger}\r\n title={labels.unpairButton}\r\n >\r\n ✕\r\n </button>\r\n </div>\r\n\r\n {/* Test SMS */}\r\n {showTestSms && (\r\n <div style={styles.section}>\r\n <div\r\n style={{\r\n fontSize: '13px',\r\n fontWeight: 600,\r\n color: '#374151',\r\n marginBottom: '12px',\r\n }}\r\n >\r\n {labels.testSmsTitle}\r\n </div>\r\n <div style={styles.inputRow}>\r\n <input\r\n value={testPhone}\r\n onChange={(e) => setTestPhone(e.target.value)}\r\n placeholder={labels.testPhonePlaceholder}\r\n style={styles.input}\r\n />\r\n <input\r\n value={testMsg}\r\n onChange={(e) => setTestMsg(e.target.value)}\r\n placeholder={labels.testMessagePlaceholder}\r\n style={styles.input}\r\n />\r\n <button\r\n style={{\r\n ...styles.button,\r\n opacity: sendingTest || !testPhone.trim() ? 0.5 : 1,\r\n }}\r\n onClick={handleTestSms}\r\n disabled={sendingTest || !testPhone.trim()}\r\n >\r\n {sendingTest ? (\r\n <span style={styles.spinner} />\r\n ) : (\r\n labels.sendTestButton\r\n )}\r\n </button>\r\n </div>\r\n {testResult && (\r\n <div style={testResult.success ? styles.success : styles.errorSmall}>\r\n {testResult.success\r\n ? `${labels.smsSentSuccess} (ID: ${testResult.messageId})`\r\n : `${labels.smsError}: ${testResult.error}`}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n ) : (\r\n <div>\r\n {/* Not paired banner */}\r\n <div style={styles.unpairedBanner}>\r\n <div style={{ fontSize: '24px' }}>📱</div>\r\n <div>\r\n <div style={{ fontSize: '14px', fontWeight: 600, color: '#854d0e' }}>\r\n {labels.notPairedStatus}\r\n </div>\r\n <div style={{ fontSize: '12px', color: '#a16207' }}>\r\n {labels.notPairedDescription}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n {!tunnel.showQr ? (\r\n <button\r\n style={{\r\n ...styles.button,\r\n opacity: tunnel.generating ? 0.5 : 1,\r\n }}\r\n onClick={handleGenerateQr}\r\n disabled={tunnel.generating}\r\n >\r\n {tunnel.generating && <span style={styles.spinner} />}\r\n {labels.connectButton}\r\n </button>\r\n ) : (\r\n <div style={styles.qrContainer}>\r\n <div style={{ fontSize: '13px', color: '#4b5563', marginBottom: '12px' }}>\r\n {labels.scanQrPrompt}\r\n </div>\r\n <div style={styles.qrFrame}>\r\n <QrCodeCanvas value={tunnel.qrData} size={qrSize} />\r\n </div>\r\n <div\r\n style={{\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n gap: '8px',\r\n fontSize: '12px',\r\n color: '#9ca3af',\r\n marginBottom: '12px',\r\n }}\r\n >\r\n <span style={styles.spinner} />\r\n {labels.waitingForPairing}\r\n </div>\r\n <button style={styles.buttonSecondary} onClick={tunnel.cancelPairing}>\r\n {labels.cancelButton}\r\n </button>\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n}\r\n","import { useState, useEffect, useRef, useCallback } from 'react';\r\nimport type { SmsTunnelState, UseSmsTunnelOptions } from './types';\r\n\r\nexport function useSmsTunnel(options: UseSmsTunnelOptions) {\r\n const { apiBaseUrl, getAuthHeaders, pollInterval = 3000 } = options;\r\n const routePrefix = 'smstunnel';\r\n\r\n const [state, setState] = useState<SmsTunnelState>({\r\n status: 'loading',\r\n serverUrl: '',\r\n deviceName: '',\r\n showQr: false,\r\n qrData: '',\r\n generating: false,\r\n polling: false,\r\n error: '',\r\n });\r\n\r\n const pollRef = useRef<ReturnType<typeof setInterval> | null>(null);\r\n const pairingTokenRef = useRef<string>('');\r\n\r\n const baseUrl = apiBaseUrl.replace(/\\/$/, '');\r\n\r\n const authHeaders = useCallback((): Record<string, string> => {\r\n return getAuthHeaders ? getAuthHeaders() : {};\r\n }, [getAuthHeaders]);\r\n\r\n const fetchStatus = useCallback(async () => {\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/status`, {\r\n headers: authHeaders(),\r\n });\r\n const data = await res.json();\r\n setState((prev) => ({\r\n ...prev,\r\n status: data.paired ? 'paired' : 'unpaired',\r\n serverUrl: data.serverUrl || '',\r\n deviceName: data.deviceName || '',\r\n }));\r\n } catch {\r\n setState((prev) => ({ ...prev, status: 'unpaired' }));\r\n }\r\n }, [baseUrl, routePrefix, authHeaders]);\r\n\r\n useEffect(() => {\r\n fetchStatus();\r\n }, [fetchStatus]);\r\n\r\n // Polling effect\r\n useEffect(() => {\r\n if (!state.polling || !pairingTokenRef.current) return;\r\n\r\n pollRef.current = setInterval(async () => {\r\n try {\r\n const res = await fetch(\r\n `${baseUrl}/${routePrefix}/pairing-status/${pairingTokenRef.current}`,\r\n );\r\n const data = await res.json();\r\n\r\n if (data.status === 'completed') {\r\n setState((prev) => ({ ...prev, polling: false, showQr: false }));\r\n if (pollRef.current) clearInterval(pollRef.current);\r\n // Wait for callback to arrive, then refetch\r\n setTimeout(() => fetchStatus(), 2000);\r\n } else if (data.status === 'expired') {\r\n setState((prev) => ({\r\n ...prev,\r\n polling: false,\r\n showQr: false,\r\n error: 'QR code expired',\r\n }));\r\n if (pollRef.current) clearInterval(pollRef.current);\r\n }\r\n } catch {\r\n // ignore polling errors\r\n }\r\n }, pollInterval);\r\n\r\n return () => {\r\n if (pollRef.current) clearInterval(pollRef.current);\r\n };\r\n }, [state.polling, baseUrl, routePrefix, pollInterval, fetchStatus]);\r\n\r\n const generateQr = useCallback(async () => {\r\n setState((prev) => ({ ...prev, generating: true, error: '' }));\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/create-token`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...authHeaders(),\r\n },\r\n });\r\n const data = await res.json();\r\n\r\n if (!data.success) {\r\n setState((prev) => ({\r\n ...prev,\r\n generating: false,\r\n error: data.error || 'Failed to create QR code',\r\n }));\r\n return;\r\n }\r\n\r\n pairingTokenRef.current = data.token;\r\n setState((prev) => ({\r\n ...prev,\r\n qrData: data.qrData,\r\n showQr: true,\r\n polling: true,\r\n generating: false,\r\n }));\r\n } catch (err: any) {\r\n setState((prev) => ({\r\n ...prev,\r\n generating: false,\r\n error: `Error: ${err.message}`,\r\n }));\r\n }\r\n }, [baseUrl, routePrefix, authHeaders]);\r\n\r\n const cancelPairing = useCallback(() => {\r\n if (pollRef.current) clearInterval(pollRef.current);\r\n pairingTokenRef.current = '';\r\n setState((prev) => ({\r\n ...prev,\r\n showQr: false,\r\n polling: false,\r\n qrData: '',\r\n }));\r\n }, []);\r\n\r\n const unpair = useCallback(async () => {\r\n try {\r\n await fetch(`${baseUrl}/${routePrefix}/unpair`, {\r\n method: 'POST',\r\n headers: authHeaders(),\r\n });\r\n setState((prev) => ({\r\n ...prev,\r\n status: 'unpaired',\r\n deviceName: '',\r\n }));\r\n } catch (err: any) {\r\n setState((prev) => ({ ...prev, error: `Unpair failed: ${err.message}` }));\r\n }\r\n }, [baseUrl, routePrefix, authHeaders]);\r\n\r\n const sendTestSms = useCallback(\r\n async (to: string, message: string) => {\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/send`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...authHeaders(),\r\n },\r\n body: JSON.stringify({ to, message }),\r\n });\r\n return (await res.json()) as { success: boolean; messageId?: string; error?: string };\r\n } catch (err: any) {\r\n return { success: false, error: err.message };\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n const updateServerUrl = useCallback(\r\n async (serverUrl: string) => {\r\n try {\r\n await fetch(`${baseUrl}/${routePrefix}/update-config`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...authHeaders(),\r\n },\r\n body: JSON.stringify({ serverUrl }),\r\n });\r\n setState((prev) => ({ ...prev, serverUrl }));\r\n } catch (err: any) {\r\n setState((prev) => ({ ...prev, error: `Update failed: ${err.message}` }));\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n const refetch = useCallback(() => fetchStatus(), [fetchStatus]);\r\n\r\n // ─── E2E Encryption ────────────────────────────────\r\n\r\n const getDeviceE2EStatus = useCallback(\r\n async (deviceId: string) => {\r\n try {\r\n const res = await fetch(\r\n `${baseUrl}/${routePrefix}/e2e-status/${deviceId}`,\r\n { headers: authHeaders() },\r\n );\r\n return await res.json();\r\n } catch (err: any) {\r\n return { success: false, error: err.message };\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n const getDevicePublicKey = useCallback(\r\n async (deviceId: string) => {\r\n try {\r\n const res = await fetch(\r\n `${baseUrl}/${routePrefix}/public-key/${deviceId}`,\r\n { headers: authHeaders() },\r\n );\r\n return await res.json();\r\n } catch (err: any) {\r\n return { success: false, error: err.message };\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n const verifyDeviceKey = useCallback(\r\n async (deviceId: string, fingerprint: string) => {\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/verify-key`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', ...authHeaders() },\r\n body: JSON.stringify({ deviceId, fingerprint }),\r\n });\r\n return await res.json();\r\n } catch (err: any) {\r\n return { valid: false, needsRePairing: true, error: err.message };\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n const sendEncryptedSms = useCallback(\r\n async (encryptedPayload: string, deviceId: string, is2FA: boolean = false) => {\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/send-encrypted`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', ...authHeaders() },\r\n body: JSON.stringify({ encryptedPayload, deviceId, is2FA }),\r\n });\r\n return await res.json();\r\n } catch (err: any) {\r\n return { success: false, error: err.message };\r\n }\r\n },\r\n [baseUrl, routePrefix, authHeaders],\r\n );\r\n\r\n return {\r\n ...state,\r\n generateQr,\r\n cancelPairing,\r\n unpair,\r\n sendTestSms,\r\n updateServerUrl,\r\n refetch,\r\n // E2E\r\n getDeviceE2EStatus,\r\n getDevicePublicKey,\r\n verifyDeviceKey,\r\n sendEncryptedSms,\r\n };\r\n}\r\n","import { useEffect, useRef } from 'react';\r\nimport qrcode from 'qrcode-generator';\r\n\r\nexport interface QrCodeCanvasProps {\r\n value: string;\r\n size?: number;\r\n}\r\n\r\nexport function QrCodeCanvas({ value, size = 200 }: QrCodeCanvasProps) {\r\n const canvasRef = useRef<HTMLCanvasElement>(null);\r\n\r\n useEffect(() => {\r\n if (!canvasRef.current || !value) return;\r\n\r\n const canvas = canvasRef.current;\r\n const ctx = canvas.getContext('2d');\r\n if (!ctx) return;\r\n\r\n const qr = qrcode(0, 'M');\r\n qr.addData(value);\r\n qr.make();\r\n\r\n const moduleCount = qr.getModuleCount();\r\n const cellSize = size / moduleCount;\r\n\r\n canvas.width = size;\r\n canvas.height = size;\r\n ctx.fillStyle = '#ffffff';\r\n ctx.fillRect(0, 0, size, size);\r\n ctx.fillStyle = '#000000';\r\n\r\n for (let row = 0; row < moduleCount; row++) {\r\n for (let col = 0; col < moduleCount; col++) {\r\n if (qr.isDark(row, col)) {\r\n ctx.fillRect(col * cellSize, row * cellSize, cellSize, cellSize);\r\n }\r\n }\r\n }\r\n }, [value, size]);\r\n\r\n return <canvas ref={canvasRef} width={size} height={size} />;\r\n}\r\n","export interface SmsTunnelLabels {\r\n title: string;\r\n description: string;\r\n serverUrlLabel: string;\r\n serverUrlPlaceholder: string;\r\n saveButton: string;\r\n pairedStatus: string;\r\n deviceLabel: string;\r\n unpairButton: string;\r\n unpairConfirm: string;\r\n notPairedStatus: string;\r\n notPairedDescription: string;\r\n connectButton: string;\r\n scanQrPrompt: string;\r\n waitingForPairing: string;\r\n cancelButton: string;\r\n qrExpired: string;\r\n testSmsTitle: string;\r\n testPhonePlaceholder: string;\r\n testMessagePlaceholder: string;\r\n sendTestButton: string;\r\n smsSentSuccess: string;\r\n smsError: string;\r\n}\r\n\r\nexport const EN_LABELS: SmsTunnelLabels = {\r\n title: 'SMS Configuration (SMSTunnel)',\r\n description: 'Connect an Android phone with the SMSTunnel app to send SMS directly from the application.',\r\n serverUrlLabel: 'SMSTunnel Server',\r\n serverUrlPlaceholder: 'https://smstunnel.io',\r\n saveButton: 'Save',\r\n pairedStatus: 'Connected',\r\n deviceLabel: 'Device',\r\n unpairButton: 'Disconnect',\r\n unpairConfirm: 'Are you sure you want to disconnect the SMS device?',\r\n notPairedStatus: 'Not connected',\r\n notPairedDescription: 'Scan the QR code with the SMSTunnel app on your Android phone.',\r\n connectButton: 'Connect phone',\r\n scanQrPrompt: 'Scan this QR code with the SMSTunnel app on your phone:',\r\n waitingForPairing: 'Waiting for connection...',\r\n cancelButton: 'Cancel',\r\n qrExpired: 'QR code has expired. Generate a new one.',\r\n testSmsTitle: 'Send test SMS',\r\n testPhonePlaceholder: 'Phone number (e.g., +1234567890)',\r\n testMessagePlaceholder: 'Message',\r\n sendTestButton: 'Send',\r\n smsSentSuccess: 'SMS sent successfully!',\r\n smsError: 'Error',\r\n};\r\n\r\nexport const RO_LABELS: SmsTunnelLabels = {\r\n title: 'Configurare SMS (SMSTunnel)',\r\n description: 'Conecteaza un telefon Android cu aplicatia SMSTunnel pentru a trimite SMS-uri direct din aplicatie.',\r\n serverUrlLabel: 'Server SMSTunnel',\r\n serverUrlPlaceholder: 'https://smstunnel.io',\r\n saveButton: 'Salveaza',\r\n pairedStatus: 'Conectat',\r\n deviceLabel: 'Dispozitiv',\r\n unpairButton: 'Deconecteaza',\r\n unpairConfirm: 'Sigur doresti sa deconectezi dispozitivul SMS?',\r\n notPairedStatus: 'Neconectat',\r\n notPairedDescription: 'Scaneaza codul QR cu aplicatia SMSTunnel de pe telefonul Android.',\r\n connectButton: 'Conecteaza telefon',\r\n scanQrPrompt: 'Scaneaza acest cod QR cu aplicatia SMSTunnel de pe telefon:',\r\n waitingForPairing: 'Se asteapta conectarea...',\r\n cancelButton: 'Anuleaza',\r\n qrExpired: 'Codul QR a expirat. Genereaza unul nou.',\r\n testSmsTitle: 'Trimite SMS de test',\r\n testPhonePlaceholder: 'Nr. telefon (ex: 0741234567)',\r\n testMessagePlaceholder: 'Mesaj',\r\n sendTestButton: 'Trimite',\r\n smsSentSuccess: 'SMS trimis cu succes!',\r\n smsError: 'Eroare',\r\n};\r\n"],"mappings":";AAAA,SAAS,YAAAA,iBAAgB;;;ACAzB,SAAS,UAAU,WAAW,QAAQ,mBAAmB;AAGlD,SAAS,aAAa,SAA8B;AACzD,QAAM,EAAE,YAAY,gBAAgB,eAAe,IAAK,IAAI;AAC5D,QAAM,cAAc;AAEpB,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAyB;AAAA,IACjD,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,OAAO;AAAA,EACT,CAAC;AAED,QAAM,UAAU,OAA8C,IAAI;AAClE,QAAM,kBAAkB,OAAe,EAAE;AAEzC,QAAM,UAAU,WAAW,QAAQ,OAAO,EAAE;AAE5C,QAAM,cAAc,YAAY,MAA8B;AAC5D,WAAO,iBAAiB,eAAe,IAAI,CAAC;AAAA,EAC9C,GAAG,CAAC,cAAc,CAAC;AAEnB,QAAM,cAAc,YAAY,YAAY;AAC1C,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,OAAO,IAAI,WAAW,WAAW;AAAA,QAC1D,SAAS,YAAY;AAAA,MACvB,CAAC;AACD,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,eAAS,CAAC,UAAU;AAAA,QAClB,GAAG;AAAA,QACH,QAAQ,KAAK,SAAS,WAAW;AAAA,QACjC,WAAW,KAAK,aAAa;AAAA,QAC7B,YAAY,KAAK,cAAc;AAAA,MACjC,EAAE;AAAA,IACJ,QAAQ;AACN,eAAS,CAAC,UAAU,EAAE,GAAG,MAAM,QAAQ,WAAW,EAAE;AAAA,IACtD;AAAA,EACF,GAAG,CAAC,SAAS,aAAa,WAAW,CAAC;AAEtC,YAAU,MAAM;AACd,gBAAY;AAAA,EACd,GAAG,CAAC,WAAW,CAAC;AAGhB,YAAU,MAAM;AACd,QAAI,CAAC,MAAM,WAAW,CAAC,gBAAgB,QAAS;AAEhD,YAAQ,UAAU,YAAY,YAAY;AACxC,UAAI;AACF,cAAM,MAAM,MAAM;AAAA,UAChB,GAAG,OAAO,IAAI,WAAW,mBAAmB,gBAAgB,OAAO;AAAA,QACrE;AACA,cAAM,OAAO,MAAM,IAAI,KAAK;AAE5B,YAAI,KAAK,WAAW,aAAa;AAC/B,mBAAS,CAAC,UAAU,EAAE,GAAG,MAAM,SAAS,OAAO,QAAQ,MAAM,EAAE;AAC/D,cAAI,QAAQ,QAAS,eAAc,QAAQ,OAAO;AAElD,qBAAW,MAAM,YAAY,GAAG,GAAI;AAAA,QACtC,WAAW,KAAK,WAAW,WAAW;AACpC,mBAAS,CAAC,UAAU;AAAA,YAClB,GAAG;AAAA,YACH,SAAS;AAAA,YACT,QAAQ;AAAA,YACR,OAAO;AAAA,UACT,EAAE;AACF,cAAI,QAAQ,QAAS,eAAc,QAAQ,OAAO;AAAA,QACpD;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF,GAAG,YAAY;AAEf,WAAO,MAAM;AACX,UAAI,QAAQ,QAAS,eAAc,QAAQ,OAAO;AAAA,IACpD;AAAA,EACF,GAAG,CAAC,MAAM,SAAS,SAAS,aAAa,cAAc,WAAW,CAAC;AAEnE,QAAM,aAAa,YAAY,YAAY;AACzC,aAAS,CAAC,UAAU,EAAE,GAAG,MAAM,YAAY,MAAM,OAAO,GAAG,EAAE;AAC7D,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,OAAO,IAAI,WAAW,iBAAiB;AAAA,QAChE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,GAAG,YAAY;AAAA,QACjB;AAAA,MACF,CAAC;AACD,YAAM,OAAO,MAAM,IAAI,KAAK;AAE5B,UAAI,CAAC,KAAK,SAAS;AACjB,iBAAS,CAAC,UAAU;AAAA,UAClB,GAAG;AAAA,UACH,YAAY;AAAA,UACZ,OAAO,KAAK,SAAS;AAAA,QACvB,EAAE;AACF;AAAA,MACF;AAEA,sBAAgB,UAAU,KAAK;AAC/B,eAAS,CAAC,UAAU;AAAA,QAClB,GAAG;AAAA,QACH,QAAQ,KAAK;AAAA,QACb,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,YAAY;AAAA,MACd,EAAE;AAAA,IACJ,SAAS,KAAU;AACjB,eAAS,CAAC,UAAU;AAAA,QAClB,GAAG;AAAA,QACH,YAAY;AAAA,QACZ,OAAO,UAAU,IAAI,OAAO;AAAA,MAC9B,EAAE;AAAA,IACJ;AAAA,EACF,GAAG,CAAC,SAAS,aAAa,WAAW,CAAC;AAEtC,QAAM,gBAAgB,YAAY,MAAM;AACtC,QAAI,QAAQ,QAAS,eAAc,QAAQ,OAAO;AAClD,oBAAgB,UAAU;AAC1B,aAAS,CAAC,UAAU;AAAA,MAClB,GAAG;AAAA,MACH,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,QAAQ;AAAA,IACV,EAAE;AAAA,EACJ,GAAG,CAAC,CAAC;AAEL,QAAM,SAAS,YAAY,YAAY;AACrC,QAAI;AACF,YAAM,MAAM,GAAG,OAAO,IAAI,WAAW,WAAW;AAAA,QAC9C,QAAQ;AAAA,QACR,SAAS,YAAY;AAAA,MACvB,CAAC;AACD,eAAS,CAAC,UAAU;AAAA,QAClB,GAAG;AAAA,QACH,QAAQ;AAAA,QACR,YAAY;AAAA,MACd,EAAE;AAAA,IACJ,SAAS,KAAU;AACjB,eAAS,CAAC,UAAU,EAAE,GAAG,MAAM,OAAO,kBAAkB,IAAI,OAAO,GAAG,EAAE;AAAA,IAC1E;AAAA,EACF,GAAG,CAAC,SAAS,aAAa,WAAW,CAAC;AAEtC,QAAM,cAAc;AAAA,IAClB,OAAO,IAAY,YAAoB;AACrC,UAAI;AACF,cAAM,MAAM,MAAM,MAAM,GAAG,OAAO,IAAI,WAAW,SAAS;AAAA,UACxD,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,gBAAgB;AAAA,YAChB,GAAG,YAAY;AAAA,UACjB;AAAA,UACA,MAAM,KAAK,UAAU,EAAE,IAAI,QAAQ,CAAC;AAAA,QACtC,CAAC;AACD,eAAQ,MAAM,IAAI,KAAK;AAAA,MACzB,SAAS,KAAU;AACjB,eAAO,EAAE,SAAS,OAAO,OAAO,IAAI,QAAQ;AAAA,MAC9C;AAAA,IACF;AAAA,IACA,CAAC,SAAS,aAAa,WAAW;AAAA,EACpC;AAEA,QAAM,kBAAkB;AAAA,IACtB,OAAO,cAAsB;AAC3B,UAAI;AACF,cAAM,MAAM,GAAG,OAAO,IAAI,WAAW,kBAAkB;AAAA,UACrD,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,gBAAgB;AAAA,YAChB,GAAG,YAAY;AAAA,UACjB;AAAA,UACA,MAAM,KAAK,UAAU,EAAE,UAAU,CAAC;AAAA,QACpC,CAAC;AACD,iBAAS,CAAC,UAAU,EAAE,GAAG,MAAM,UAAU,EAAE;AAAA,MAC7C,SAAS,KAAU;AACjB,iBAAS,CAAC,UAAU,EAAE,GAAG,MAAM,OAAO,kBAAkB,IAAI,OAAO,GAAG,EAAE;AAAA,MAC1E;AAAA,IACF;AAAA,IACA,CAAC,SAAS,aAAa,WAAW;AAAA,EACpC;AAEA,QAAM,UAAU,YAAY,MAAM,YAAY,GAAG,CAAC,WAAW,CAAC;AAI9D,QAAM,qBAAqB;AAAA,IACzB,OAAO,aAAqB;AAC1B,UAAI;AACF,cAAM,MAAM,MAAM;AAAA,UAChB,GAAG,OAAO,IAAI,WAAW,eAAe,QAAQ;AAAA,UAChD,EAAE,SAAS,YAAY,EAAE;AAAA,QAC3B;AACA,eAAO,MAAM,IAAI,KAAK;AAAA,MACxB,SAAS,KAAU;AACjB,eAAO,EAAE,SAAS,OAAO,OAAO,IAAI,QAAQ;AAAA,MAC9C;AAAA,IACF;AAAA,IACA,CAAC,SAAS,aAAa,WAAW;AAAA,EACpC;AAEA,QAAM,qBAAqB;AAAA,IACzB,OAAO,aAAqB;AAC1B,UAAI;AACF,cAAM,MAAM,MAAM;AAAA,UAChB,GAAG,OAAO,IAAI,WAAW,eAAe,QAAQ;AAAA,UAChD,EAAE,SAAS,YAAY,EAAE;AAAA,QAC3B;AACA,eAAO,MAAM,IAAI,KAAK;AAAA,MACxB,SAAS,KAAU;AACjB,eAAO,EAAE,SAAS,OAAO,OAAO,IAAI,QAAQ;AAAA,MAC9C;AAAA,IACF;AAAA,IACA,CAAC,SAAS,aAAa,WAAW;AAAA,EACpC;AAEA,QAAM,kBAAkB;AAAA,IACtB,OAAO,UAAkB,gBAAwB;AAC/C,UAAI;AACF,cAAM,MAAM,MAAM,MAAM,GAAG,OAAO,IAAI,WAAW,eAAe;AAAA,UAC9D,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,oBAAoB,GAAG,YAAY,EAAE;AAAA,UAChE,MAAM,KAAK,UAAU,EAAE,UAAU,YAAY,CAAC;AAAA,QAChD,CAAC;AACD,eAAO,MAAM,IAAI,KAAK;AAAA,MACxB,SAAS,KAAU;AACjB,eAAO,EAAE,OAAO,OAAO,gBAAgB,MAAM,OAAO,IAAI,QAAQ;AAAA,MAClE;AAAA,IACF;AAAA,IACA,CAAC,SAAS,aAAa,WAAW;AAAA,EACpC;AAEA,QAAM,mBAAmB;AAAA,IACvB,OAAO,kBAA0B,UAAkB,QAAiB,UAAU;AAC5E,UAAI;AACF,cAAM,MAAM,MAAM,MAAM,GAAG,OAAO,IAAI,WAAW,mBAAmB;AAAA,UAClE,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,oBAAoB,GAAG,YAAY,EAAE;AAAA,UAChE,MAAM,KAAK,UAAU,EAAE,kBAAkB,UAAU,MAAM,CAAC;AAAA,QAC5D,CAAC;AACD,eAAO,MAAM,IAAI,KAAK;AAAA,MACxB,SAAS,KAAU;AACjB,eAAO,EAAE,SAAS,OAAO,OAAO,IAAI,QAAQ;AAAA,MAC9C;AAAA,IACF;AAAA,IACA,CAAC,SAAS,aAAa,WAAW;AAAA,EACpC;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AC1QA,SAAS,aAAAC,YAAW,UAAAC,eAAc;AAClC,OAAO,YAAY;AAuCV;AAhCF,SAAS,aAAa,EAAE,OAAO,OAAO,IAAI,GAAsB;AACrE,QAAM,YAAYA,QAA0B,IAAI;AAEhD,EAAAD,WAAU,MAAM;AACd,QAAI,CAAC,UAAU,WAAW,CAAC,MAAO;AAElC,UAAM,SAAS,UAAU;AACzB,UAAM,MAAM,OAAO,WAAW,IAAI;AAClC,QAAI,CAAC,IAAK;AAEV,UAAM,KAAK,OAAO,GAAG,GAAG;AACxB,OAAG,QAAQ,KAAK;AAChB,OAAG,KAAK;AAER,UAAM,cAAc,GAAG,eAAe;AACtC,UAAM,WAAW,OAAO;AAExB,WAAO,QAAQ;AACf,WAAO,SAAS;AAChB,QAAI,YAAY;AAChB,QAAI,SAAS,GAAG,GAAG,MAAM,IAAI;AAC7B,QAAI,YAAY;AAEhB,aAAS,MAAM,GAAG,MAAM,aAAa,OAAO;AAC1C,eAAS,MAAM,GAAG,MAAM,aAAa,OAAO;AAC1C,YAAI,GAAG,OAAO,KAAK,GAAG,GAAG;AACvB,cAAI,SAAS,MAAM,UAAU,MAAM,UAAU,UAAU,QAAQ;AAAA,QACjE;AAAA,MACF;AAAA,IACF;AAAA,EACF,GAAG,CAAC,OAAO,IAAI,CAAC;AAEhB,SAAO,oBAAC,YAAO,KAAK,WAAW,OAAO,MAAM,QAAQ,MAAM;AAC5D;;;AChBO,IAAM,YAA6B;AAAA,EACxC,OAAO;AAAA,EACP,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,sBAAsB;AAAA,EACtB,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,aAAa;AAAA,EACb,cAAc;AAAA,EACd,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,sBAAsB;AAAA,EACtB,eAAe;AAAA,EACf,cAAc;AAAA,EACd,mBAAmB;AAAA,EACnB,cAAc;AAAA,EACd,WAAW;AAAA,EACX,cAAc;AAAA,EACd,sBAAsB;AAAA,EACtB,wBAAwB;AAAA,EACxB,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,UAAU;AACZ;AAEO,IAAM,YAA6B;AAAA,EACxC,OAAO;AAAA,EACP,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,sBAAsB;AAAA,EACtB,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,aAAa;AAAA,EACb,cAAc;AAAA,EACd,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,sBAAsB;AAAA,EACtB,eAAe;AAAA,EACf,cAAc;AAAA,EACd,mBAAmB;AAAA,EACnB,cAAc;AAAA,EACd,WAAW;AAAA,EACX,cAAc;AAAA,EACd,sBAAsB;AAAA,EACtB,wBAAwB;AAAA,EACxB,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,UAAU;AACZ;;;AHkKY,gBAAAE,MAiBA,YAjBA;AApOZ,IAAM,SAAS;AAAA,EACb,WAAW;AAAA,IACT,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,OAAO;AAAA,EACT;AAAA,EACA,MAAM;AAAA,IACJ,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAS;AAAA,IACT,iBAAiB;AAAA,EACnB;AAAA,EACA,OAAO;AAAA,IACL,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,cAAc;AAAA,EAChB;AAAA,EACA,UAAU;AAAA,IACR,UAAU;AAAA,IACV,OAAO;AAAA,IACP,cAAc;AAAA,EAChB;AAAA,EACA,SAAS;AAAA,IACP,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAS;AAAA,IACT,cAAc;AAAA,EAChB;AAAA,EACA,OAAO;AAAA,IACL,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,cAAc;AAAA,EAChB;AAAA,EACA,UAAU;AAAA,IACR,SAAS;AAAA,IACT,KAAK;AAAA,EACP;AAAA,EACA,OAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAS;AAAA,IACT,UAAU;AAAA,IACV,SAAS;AAAA,EACX;AAAA,EACA,QAAQ;AAAA,IACN,SAAS;AAAA,IACT,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,YAAY;AAAA,EACd;AAAA,EACA,iBAAiB;AAAA,IACf,SAAS;AAAA,IACT,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,iBAAiB;AAAA,IACjB,OAAO;AAAA,EACT;AAAA,EACA,cAAc;AAAA,IACZ,SAAS;AAAA,IACT,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,YAAY;AAAA,EACd;AAAA,EACA,cAAc;AAAA,IACZ,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,iBAAiB;AAAA,IACjB,cAAc;AAAA,IACd,SAAS;AAAA,IACT,cAAc;AAAA,EAChB;AAAA,EACA,YAAY;AAAA,IACV,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,iBAAiB;AAAA,IACjB,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,OAAO;AAAA,IACP,UAAU;AAAA,IACV,YAAY;AAAA,EACd;AAAA,EACA,gBAAgB;AAAA,IACd,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,iBAAiB;AAAA,IACjB,cAAc;AAAA,IACd,SAAS;AAAA,IACT,cAAc;AAAA,EAChB;AAAA,EACA,aAAa;AAAA,IACX,WAAW;AAAA,IACX,SAAS;AAAA,EACX;AAAA,EACA,SAAS;AAAA,IACP,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,cAAc;AAAA,EAChB;AAAA,EACA,SAAS;AAAA,IACP,SAAS;AAAA,IACT,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,WAAW;AAAA,IACX,aAAa;AAAA,IACb,eAAe;AAAA,EACjB;AAAA,EACA,OAAO;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,cAAc;AAAA,EAChB;AAAA,EACA,SAAS;AAAA,IACP,OAAO;AAAA,IACP,UAAU;AAAA,IACV,WAAW;AAAA,EACb;AAAA,EACA,YAAY;AAAA,IACV,OAAO;AAAA,IACP,UAAU;AAAA,IACV,WAAW;AAAA,EACb;AACF;AAGA,IAAI,iBAAiB;AACrB,SAAS,kBAAkB;AACzB,MAAI,kBAAkB,OAAO,aAAa,YAAa;AACvD,QAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,QAAM,cAAc;AACpB,WAAS,KAAK,YAAY,KAAK;AAC/B,mBAAiB;AACnB;AAEO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,qBAAqB;AAAA,EACrB,SAAS;AAAA,EACT;AACF,GAA0B;AACxB,kBAAgB;AAEhB,QAAM,SAAS,aAAa,EAAE,YAAY,eAAe,CAAC;AAE1D,QAAM,CAAC,gBAAgB,iBAAiB,IAAIC,UAAS,EAAE;AACvD,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,KAAK;AAChD,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,EAAE;AAC7C,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,UAAU;AACjD,QAAM,CAAC,YAAY,aAAa,IAAIA,UAI1B,IAAI;AACd,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,KAAK;AAGpD,MAAI,OAAO,aAAa,CAAC,gBAAgB;AACvC,sBAAkB,OAAO,SAAS;AAAA,EACpC;AAEA,QAAM,gBAAgB,YAAY;AAChC,QAAI,CAAC,eAAe,KAAK,EAAG;AAC5B,iBAAa,IAAI;AACjB,UAAM,OAAO,gBAAgB,eAAe,KAAK,EAAE,QAAQ,OAAO,EAAE,CAAC;AACrE,iBAAa,KAAK;AAAA,EACpB;AAEA,QAAM,eAAe,YAAY;AAC/B,QAAI,CAAC,QAAQ,OAAO,aAAa,EAAG;AACpC,UAAM,OAAO,OAAO;AACpB,iBAAa;AAAA,EACf;AAEA,QAAM,mBAAmB,YAAY;AACnC,UAAM,OAAO,WAAW;AAAA,EAC1B;AAGA,QAAM,aAAa,OAAO;AAC1B,MAAI,eAAe,YAAY,OAAO,YAAY;AAAA,EAElD;AAEA,QAAM,gBAAgB,YAAY;AAChC,QAAI,CAAC,UAAU,KAAK,EAAG;AACvB,mBAAe,IAAI;AACnB,kBAAc,IAAI;AAClB,UAAM,SAAS,MAAM,OAAO,YAAY,UAAU,KAAK,GAAG,OAAO;AACjE,kBAAc,MAAM;AACpB,mBAAe,KAAK;AAAA,EACtB;AAEA,MAAI,OAAO,WAAW,WAAW;AAC/B,WACE,gBAAAD,KAAC,SAAI,OAAO,OAAO,WAAW,WAC5B,0BAAAA,KAAC,SAAI,OAAO,OAAO,MACjB,0BAAAA,KAAC,SAAI,OAAO,EAAE,WAAW,UAAU,SAAS,OAAO,GACjD,0BAAAA,KAAC,UAAK,OAAO,OAAO,SAAS,GAC/B,GACF,GACF;AAAA,EAEJ;AAEA,SACE,gBAAAA,KAAC,SAAI,OAAO,OAAO,WAAW,WAC5B,+BAAC,SAAI,OAAO,OAAO,MACjB;AAAA,oBAAAA,KAAC,SAAI,OAAO,OAAO,OAAQ,iBAAO,OAAM;AAAA,IACxC,gBAAAA,KAAC,SAAI,OAAO,OAAO,UAAW,iBAAO,aAAY;AAAA,IAGhD,sBACC,qBAAC,SAAI,OAAO,OAAO,SACjB;AAAA,sBAAAA,KAAC,WAAM,OAAO,OAAO,OAAQ,iBAAO,gBAAe;AAAA,MACnD,qBAAC,SAAI,OAAO,OAAO,UACjB;AAAA,wBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,UAAU,CAAC,MAAM,kBAAkB,EAAE,OAAO,KAAK;AAAA,YACjD,aAAa,OAAO;AAAA,YACpB,OAAO,OAAO;AAAA;AAAA,QAChB;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,cACL,GAAG,OAAO;AAAA,cACV,SAAS,aAAa,CAAC,eAAe,KAAK,IAAI,MAAM;AAAA,YACvD;AAAA,YACA,SAAS;AAAA,YACT,UAAU,aAAa,CAAC,eAAe,KAAK;AAAA,YAE3C,iBAAO;AAAA;AAAA,QACV;AAAA,SACF;AAAA,OACF;AAAA,IAGD,OAAO,SAAS,gBAAAA,KAAC,SAAI,OAAO,OAAO,OAAQ,iBAAO,OAAM;AAAA,IAExD,OAAO,WAAW,WACjB,qBAAC,SAEC;AAAA,2BAAC,SAAI,OAAO,OAAO,cACjB;AAAA,wBAAAA,KAAC,SAAI,OAAO,OAAO,YAAY,oBAAQ;AAAA,QACvC,qBAAC,SAAI,OAAO,EAAE,MAAM,EAAE,GACpB;AAAA,0BAAAA,KAAC,SAAI,OAAO,EAAE,UAAU,QAAQ,YAAY,KAAK,OAAO,UAAU,GAC/D,iBAAO,cACV;AAAA,UACA,qBAAC,SAAI,OAAO,EAAE,UAAU,QAAQ,OAAO,UAAU,GAC9C;AAAA,mBAAO;AAAA,YAAY;AAAA,YAAG,OAAO,cAAc;AAAA,aAC9C;AAAA,WACF;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS;AAAA,YACT,OAAO,OAAO;AAAA,YACd,OAAO,OAAO;AAAA,YACf;AAAA;AAAA,QAED;AAAA,SACF;AAAA,MAGC,eACC,qBAAC,SAAI,OAAO,OAAO,SACjB;AAAA,wBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,cACL,UAAU;AAAA,cACV,YAAY;AAAA,cACZ,OAAO;AAAA,cACP,cAAc;AAAA,YAChB;AAAA,YAEC,iBAAO;AAAA;AAAA,QACV;AAAA,QACA,qBAAC,SAAI,OAAO,OAAO,UACjB;AAAA,0BAAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,UAAU,CAAC,MAAM,aAAa,EAAE,OAAO,KAAK;AAAA,cAC5C,aAAa,OAAO;AAAA,cACpB,OAAO,OAAO;AAAA;AAAA,UAChB;AAAA,UACA,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,UAAU,CAAC,MAAM,WAAW,EAAE,OAAO,KAAK;AAAA,cAC1C,aAAa,OAAO;AAAA,cACpB,OAAO,OAAO;AAAA;AAAA,UAChB;AAAA,UACA,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,gBACL,GAAG,OAAO;AAAA,gBACV,SAAS,eAAe,CAAC,UAAU,KAAK,IAAI,MAAM;AAAA,cACpD;AAAA,cACA,SAAS;AAAA,cACT,UAAU,eAAe,CAAC,UAAU,KAAK;AAAA,cAExC,wBACC,gBAAAA,KAAC,UAAK,OAAO,OAAO,SAAS,IAE7B,OAAO;AAAA;AAAA,UAEX;AAAA,WACF;AAAA,QACC,cACC,gBAAAA,KAAC,SAAI,OAAO,WAAW,UAAU,OAAO,UAAU,OAAO,YACtD,qBAAW,UACR,GAAG,OAAO,cAAc,SAAS,WAAW,SAAS,MACrD,GAAG,OAAO,QAAQ,KAAK,WAAW,KAAK,IAC7C;AAAA,SAEJ;AAAA,OAEJ,IAEA,qBAAC,SAEC;AAAA,2BAAC,SAAI,OAAO,OAAO,gBACjB;AAAA,wBAAAA,KAAC,SAAI,OAAO,EAAE,UAAU,OAAO,GAAG,uBAAS;AAAA,QAC3C,qBAAC,SACC;AAAA,0BAAAA,KAAC,SAAI,OAAO,EAAE,UAAU,QAAQ,YAAY,KAAK,OAAO,UAAU,GAC/D,iBAAO,iBACV;AAAA,UACA,gBAAAA,KAAC,SAAI,OAAO,EAAE,UAAU,QAAQ,OAAO,UAAU,GAC9C,iBAAO,sBACV;AAAA,WACF;AAAA,SACF;AAAA,MAEC,CAAC,OAAO,SACP;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,YACL,GAAG,OAAO;AAAA,YACV,SAAS,OAAO,aAAa,MAAM;AAAA,UACrC;AAAA,UACA,SAAS;AAAA,UACT,UAAU,OAAO;AAAA,UAEhB;AAAA,mBAAO,cAAc,gBAAAA,KAAC,UAAK,OAAO,OAAO,SAAS;AAAA,YAClD,OAAO;AAAA;AAAA;AAAA,MACV,IAEA,qBAAC,SAAI,OAAO,OAAO,aACjB;AAAA,wBAAAA,KAAC,SAAI,OAAO,EAAE,UAAU,QAAQ,OAAO,WAAW,cAAc,OAAO,GACpE,iBAAO,cACV;AAAA,QACA,gBAAAA,KAAC,SAAI,OAAO,OAAO,SACjB,0BAAAA,KAAC,gBAAa,OAAO,OAAO,QAAQ,MAAM,QAAQ,GACpD;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,cACL,SAAS;AAAA,cACT,YAAY;AAAA,cACZ,gBAAgB;AAAA,cAChB,KAAK;AAAA,cACL,UAAU;AAAA,cACV,OAAO;AAAA,cACP,cAAc;AAAA,YAChB;AAAA,YAEA;AAAA,8BAAAA,KAAC,UAAK,OAAO,OAAO,SAAS;AAAA,cAC5B,OAAO;AAAA;AAAA;AAAA,QACV;AAAA,QACA,gBAAAA,KAAC,YAAO,OAAO,OAAO,iBAAiB,SAAS,OAAO,eACpD,iBAAO,cACV;AAAA,SACF;AAAA,OAEJ;AAAA,KAEJ,GACF;AAEJ;","names":["useState","useEffect","useRef","jsx","useState"]}
|
package/dist/server/index.d.mts
CHANGED
|
@@ -16,7 +16,6 @@ interface SmsTunnelModuleOptions {
|
|
|
16
16
|
callbackBaseUrl: string;
|
|
17
17
|
displayName?: string;
|
|
18
18
|
displayUrl?: string;
|
|
19
|
-
routePrefix?: string;
|
|
20
19
|
enableLegacyCallback?: boolean;
|
|
21
20
|
/** Enterprise App API Key for SaaS mode.
|
|
22
21
|
* When set, createPairingToken() uses /api/v1/saas/activate
|
package/dist/server/index.d.ts
CHANGED
|
@@ -16,7 +16,6 @@ interface SmsTunnelModuleOptions {
|
|
|
16
16
|
callbackBaseUrl: string;
|
|
17
17
|
displayName?: string;
|
|
18
18
|
displayUrl?: string;
|
|
19
|
-
routePrefix?: string;
|
|
20
19
|
enableLegacyCallback?: boolean;
|
|
21
20
|
/** Enterprise App API Key for SaaS mode.
|
|
22
21
|
* When set, createPairingToken() uses /api/v1/saas/activate
|
package/dist/server/index.js
CHANGED
|
@@ -98,7 +98,7 @@ var SmsTunnelService = class {
|
|
|
98
98
|
* Public flow: POST /api/v1/pairing/public-create
|
|
99
99
|
*/
|
|
100
100
|
async createPairingTokenPublic(serverUrl) {
|
|
101
|
-
const prefix =
|
|
101
|
+
const prefix = "smstunnel";
|
|
102
102
|
const callbackUrl = `${this.options.callbackBaseUrl.replace(/\/$/, "")}/${prefix}/callback`;
|
|
103
103
|
try {
|
|
104
104
|
const res = await fetch(`${serverUrl}/api/v1/pairing/public-create`, {
|
|
@@ -136,7 +136,7 @@ var SmsTunnelService = class {
|
|
|
136
136
|
* Creates sub-accounts under the Enterprise account.
|
|
137
137
|
*/
|
|
138
138
|
async createPairingTokenEnterprise(serverUrl) {
|
|
139
|
-
const prefix =
|
|
139
|
+
const prefix = "smstunnel";
|
|
140
140
|
const callbackUrl = `${this.options.callbackBaseUrl.replace(/\/$/, "")}/${prefix}/callback`;
|
|
141
141
|
try {
|
|
142
142
|
const res = await fetch(`${serverUrl}/api/v1/saas/activate`, {
|
|
@@ -439,7 +439,7 @@ var SmsTunnelController = class {
|
|
|
439
439
|
this.smsTunnelService = smsTunnelService;
|
|
440
440
|
this.options = options;
|
|
441
441
|
this.logger = new import_common2.Logger(SmsTunnelController.name);
|
|
442
|
-
this.prefix =
|
|
442
|
+
this.prefix = "smstunnel";
|
|
443
443
|
}
|
|
444
444
|
async getStatus() {
|
|
445
445
|
return this.smsTunnelService.getStatus();
|
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/server/index.ts","../../src/server/smstunnel.module.ts","../../src/server/smstunnel.service.ts","../../src/server/smstunnel.constants.ts","../../src/server/smstunnel.controller.ts"],"sourcesContent":["export { SmsTunnelModule } from './smstunnel.module';\r\nexport { SmsTunnelService } from './smstunnel.service';\r\nexport { SmsTunnelController } from './smstunnel.controller';\r\nexport {\r\n SMSTUNNEL_OPTIONS,\r\n SMSTUNNEL_PUBLIC_PATHS,\r\n SMSTUNNEL_DEFAULT_PREFIX,\r\n getSmsTunnelPublicPaths,\r\n} from './smstunnel.constants';\r\nexport type {\r\n SmsTunnelConfig,\r\n SmsTunnelStorageAdapter,\r\n SmsTunnelModuleOptions,\r\n SmsTunnelModuleAsyncOptions,\r\n SmsTunnelStatus,\r\n SendSmsResult,\r\n CreateTokenResult,\r\n PairingStatusResult,\r\n PairingCallbackBody,\r\n LegacyCallbackBody,\r\n SaasActivateResult,\r\n DeviceE2EStatus,\r\n DevicePublicKeyInfo,\r\n VerifyDeviceKeyResult,\r\n} from './smstunnel.interfaces';\r\n","import { DynamicModule, Module, Provider } from '@nestjs/common';\r\nimport { SmsTunnelService } from './smstunnel.service';\r\nimport { SmsTunnelController } from './smstunnel.controller';\r\nimport { SMSTUNNEL_OPTIONS } from './smstunnel.constants';\r\nimport type {\r\n SmsTunnelModuleOptions,\r\n SmsTunnelModuleAsyncOptions,\r\n} from './smstunnel.interfaces';\r\n\r\n@Module({})\r\nexport class SmsTunnelModule {\r\n static forRoot(options: SmsTunnelModuleOptions): DynamicModule {\r\n const optionsProvider: Provider = {\r\n provide: SMSTUNNEL_OPTIONS,\r\n useValue: options,\r\n };\r\n\r\n const controllers = [SmsTunnelController];\r\n if (!options.enableLegacyCallback) {\r\n // Legacy callback is always registered but won't match\r\n // if the consumer doesn't enable it - no-op\r\n }\r\n\r\n return {\r\n module: SmsTunnelModule,\r\n controllers,\r\n providers: [optionsProvider, SmsTunnelService],\r\n exports: [SmsTunnelService],\r\n global: false,\r\n };\r\n }\r\n\r\n static forRootAsync(asyncOptions: SmsTunnelModuleAsyncOptions): DynamicModule {\r\n const optionsProvider: Provider = {\r\n provide: SMSTUNNEL_OPTIONS,\r\n useFactory: asyncOptions.useFactory,\r\n inject: asyncOptions.inject || [],\r\n };\r\n\r\n return {\r\n module: SmsTunnelModule,\r\n controllers: [SmsTunnelController],\r\n providers: [optionsProvider, SmsTunnelService],\r\n exports: [SmsTunnelService],\r\n global: false,\r\n };\r\n }\r\n}\r\n","import { Inject, Injectable, Logger } from '@nestjs/common';\r\nimport { SMSTUNNEL_OPTIONS } from './smstunnel.constants';\r\nimport type {\r\n SmsTunnelModuleOptions,\r\n SmsTunnelStatus,\r\n SendSmsResult,\r\n CreateTokenResult,\r\n PairingStatusResult,\r\n PairingCallbackBody,\r\n SaasActivateResult,\r\n DeviceE2EStatus,\r\n DevicePublicKeyInfo,\r\n VerifyDeviceKeyResult,\r\n} from './smstunnel.interfaces';\r\n\r\n@Injectable()\r\nexport class SmsTunnelService {\r\n private readonly logger = new Logger(SmsTunnelService.name);\r\n\r\n constructor(\r\n @Inject(SMSTUNNEL_OPTIONS)\r\n private readonly options: SmsTunnelModuleOptions,\r\n ) {}\r\n\r\n /**\r\n * Get current pairing status.\r\n */\r\n async getStatus(): Promise<SmsTunnelStatus> {\r\n const config = await this.options.storage.getConfig();\r\n return {\r\n paired: !!config.apiKey,\r\n serverUrl: config.serverUrl,\r\n deviceName: config.deviceName,\r\n };\r\n }\r\n\r\n /**\r\n * Create a pairing token on SMSTunnel server.\r\n * Uses Enterprise/SaaS flow when enterpriseApiKey is configured,\r\n * otherwise falls back to public-create flow.\r\n */\r\n async createPairingToken(): Promise<CreateTokenResult> {\r\n const config = await this.options.storage.getConfig();\r\n if (!config.serverUrl) {\r\n return { success: false, error: 'SMSTunnel server URL is not configured.' };\r\n }\r\n\r\n const serverUrl = config.serverUrl.replace(/\\/$/, '');\r\n\r\n if (this.options.enterpriseApiKey) {\r\n return this.createPairingTokenEnterprise(serverUrl);\r\n }\r\n\r\n return this.createPairingTokenPublic(serverUrl);\r\n }\r\n\r\n /**\r\n * Public flow: POST /api/v1/pairing/public-create\r\n */\r\n private async createPairingTokenPublic(serverUrl: string): Promise<CreateTokenResult> {\r\n const prefix = this.options.routePrefix || 'smstunnel';\r\n const callbackUrl = `${this.options.callbackBaseUrl.replace(/\\/$/, '')}/${prefix}/callback`;\r\n\r\n try {\r\n const res = await fetch(`${serverUrl}/api/v1/pairing/public-create`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n source: 'api',\r\n displayName: this.options.displayName || 'App',\r\n displayUrl: this.options.displayUrl || this.options.callbackBaseUrl,\r\n context: { callbackUrl },\r\n }),\r\n });\r\n\r\n if (!res.ok) {\r\n const errText = await res.text();\r\n this.logger.warn(`SMSTunnel create token failed: ${res.status} ${errText}`);\r\n return { success: false, error: `SMSTunnel returned error: ${res.status}` };\r\n }\r\n\r\n const data: any = await res.json();\r\n\r\n await this.options.storage.updateConfig({ siteToken: data.token });\r\n\r\n this.logger.log(`Pairing token created: ${data.token?.substring(0, 12)}...`);\r\n\r\n return {\r\n success: true,\r\n token: data.token,\r\n qrData: data.qrData,\r\n expiresAt: data.expiresAt,\r\n pollUrl: data.pollUrl || `${serverUrl}/api/v1/pairing/${data.token}`,\r\n };\r\n } catch (err: any) {\r\n this.logger.error(`SMSTunnel create token error: ${err.message}`);\r\n return { success: false, error: `Could not connect to SMSTunnel: ${err.message}` };\r\n }\r\n }\r\n\r\n /**\r\n * Enterprise/SaaS flow: POST /api/v1/saas/activate\r\n * Creates sub-accounts under the Enterprise account.\r\n */\r\n private async createPairingTokenEnterprise(serverUrl: string): Promise<CreateTokenResult> {\r\n const prefix = this.options.routePrefix || 'smstunnel';\r\n const callbackUrl = `${this.options.callbackBaseUrl.replace(/\\/$/, '')}/${prefix}/callback`;\r\n\r\n try {\r\n const res = await fetch(`${serverUrl}/api/v1/saas/activate`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'X-API-Key': this.options.enterpriseApiKey!,\r\n },\r\n body: JSON.stringify({\r\n clientName: this.options.displayName || 'App',\r\n externalId: this.options.externalId || 'default',\r\n callbackUrl,\r\n }),\r\n });\r\n\r\n if (!res.ok) {\r\n const errText = await res.text();\r\n this.logger.warn(`SMSTunnel SaaS activate failed: ${res.status} ${errText}`);\r\n return { success: false, error: `SMSTunnel returned error: ${res.status}` };\r\n }\r\n\r\n const data: SaasActivateResult = await res.json();\r\n\r\n if (!data.success || !data.activation) {\r\n return { success: false, error: data.error || 'SaaS activation failed' };\r\n }\r\n\r\n // Save the activation token as siteToken\r\n const configUpdate: Record<string, any> = { siteToken: data.activation.token };\r\n\r\n // If the server returned an API key, save it too\r\n if (data.apiKey?.key) {\r\n configUpdate.apiKey = data.apiKey.key;\r\n }\r\n\r\n await this.options.storage.updateConfig(configUpdate);\r\n\r\n this.logger.log(\r\n `SaaS pairing token created: ${data.activation.token?.substring(0, 12)}... (isNew: ${data.isNew})`,\r\n );\r\n\r\n return {\r\n success: true,\r\n token: data.activation.token,\r\n qrData: data.activation.qrData,\r\n expiresAt: data.activation.expiresAt,\r\n pollUrl: `${serverUrl}/api/v1/pairing/${data.activation.token}`,\r\n };\r\n } catch (err: any) {\r\n this.logger.error(`SMSTunnel SaaS activate error: ${err.message}`);\r\n return { success: false, error: `Could not connect to SMSTunnel: ${err.message}` };\r\n }\r\n }\r\n\r\n /**\r\n * Check pairing status by polling SMSTunnel.\r\n */\r\n async getPairingStatus(token: string): Promise<PairingStatusResult> {\r\n const config = await this.options.storage.getConfig();\r\n if (!config.serverUrl) {\r\n return { status: 'error', error: 'SMSTunnel server URL not configured' };\r\n }\r\n\r\n const serverUrl = config.serverUrl.replace(/\\/$/, '');\r\n\r\n try {\r\n const res = await fetch(`${serverUrl}/api/v1/pairing/${token}`);\r\n if (!res.ok) {\r\n return { status: 'expired' };\r\n }\r\n return (await res.json()) as PairingStatusResult;\r\n } catch {\r\n return { status: 'error', error: 'Could not contact SMSTunnel' };\r\n }\r\n }\r\n\r\n /**\r\n * Handle callback from SMSTunnel after pairing completes.\r\n */\r\n async handlePairingCallback(body: PairingCallbackBody): Promise<boolean> {\r\n if (body.event !== 'pairing_completed') {\r\n this.logger.warn(`Unknown callback event: ${body.event}`);\r\n return false;\r\n }\r\n\r\n const config = await this.options.storage.getConfig();\r\n\r\n if (config.siteToken && config.siteToken !== body.token) {\r\n this.logger.warn(\r\n `Callback token mismatch: expected ${config.siteToken?.substring(0, 8)}, got ${body.token?.substring(0, 8)}`,\r\n );\r\n return false;\r\n }\r\n\r\n await this.options.storage.updateConfig({\r\n apiKey: body.apiKey,\r\n deviceName: body.deviceName || 'Android Phone',\r\n });\r\n\r\n this.logger.log(\r\n `SMSTunnel paired! Device: ${body.deviceName}, API key: ${body.apiKey?.substring(0, 12)}...`,\r\n );\r\n return true;\r\n }\r\n\r\n /**\r\n * Handle legacy WordPress-compatible callback.\r\n */\r\n async handleLegacyCallback(\r\n siteToken: string,\r\n apiKey: string,\r\n deviceName?: string,\r\n ): Promise<boolean> {\r\n const config = await this.options.storage.getConfig();\r\n\r\n if (config.siteToken !== siteToken) {\r\n this.logger.warn('Legacy callback: site_token mismatch');\r\n return false;\r\n }\r\n\r\n const update: Partial<{ apiKey: string; deviceName: string }> = { apiKey };\r\n if (deviceName) update.deviceName = deviceName;\r\n\r\n await this.options.storage.updateConfig(update);\r\n this.logger.log(`SMSTunnel paired (legacy). Device: ${deviceName || 'unknown'}`);\r\n return true;\r\n }\r\n\r\n /**\r\n * Send SMS via SMSTunnel API.\r\n */\r\n async sendSms(to: string, message: string): Promise<SendSmsResult> {\r\n const config = await this.options.storage.getConfig();\r\n\r\n if (!config.apiKey) {\r\n return {\r\n success: false,\r\n error: 'SMSTunnel is not configured. Pair a device first.',\r\n };\r\n }\r\n\r\n if (!config.serverUrl) {\r\n return {\r\n success: false,\r\n error: 'SMSTunnel server URL is not configured.',\r\n };\r\n }\r\n\r\n const serverUrl = config.serverUrl.replace(/\\/$/, '');\r\n\r\n try {\r\n const res = await fetch(`${serverUrl}/api/v1/sms/send`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'X-API-Key': config.apiKey,\r\n },\r\n body: JSON.stringify({ to, message }),\r\n });\r\n\r\n const data: any = await res.json();\r\n\r\n if (data.success) {\r\n this.logger.log(`SMS sent via SMSTunnel: ${data.messageId} -> ${to}`);\r\n return { success: true, messageId: data.messageId };\r\n }\r\n\r\n return {\r\n success: false,\r\n error: data.error || data.message || 'SMSTunnel returned an error',\r\n };\r\n } catch (err: any) {\r\n this.logger.error(`SMS send error: ${err.message}`);\r\n return { success: false, error: `Could not send SMS: ${err.message}` };\r\n }\r\n }\r\n\r\n /**\r\n * Unpair - clear saved API key and device info.\r\n */\r\n async unpair(): Promise<void> {\r\n await this.options.storage.clearConfig();\r\n this.logger.log('SMSTunnel unpaired');\r\n }\r\n\r\n /**\r\n * Update server URL in config.\r\n */\r\n async updateServerUrl(serverUrl: string): Promise<void> {\r\n await this.options.storage.updateConfig({\r\n serverUrl: serverUrl.replace(/\\/$/, ''),\r\n });\r\n }\r\n\r\n // ─── Extended SMS endpoints ────────────────────────\r\n\r\n /**\r\n * Send 2FA SMS via SMSTunnel API.\r\n */\r\n async send2fa(to: string, code: string, template?: string): Promise<SendSmsResult> {\r\n return this.apiPost('/api/v1/sms/send-2fa', { to, code, template });\r\n }\r\n\r\n /**\r\n * Send bulk SMS via SMSTunnel API.\r\n */\r\n async sendBulk(\r\n messages: Array<{ to: string; message: string }>,\r\n ): Promise<{ success: boolean; results?: any[]; error?: string }> {\r\n return this.apiPost('/api/v1/sms/send-bulk', { messages });\r\n }\r\n\r\n /**\r\n * Get SMS delivery status.\r\n */\r\n async getSmsStatus(messageId: string): Promise<any> {\r\n return this.apiGet(`/api/v1/sms/status/${messageId}`);\r\n }\r\n\r\n /**\r\n * Get received SMS (inbox).\r\n */\r\n async getReceivedSms(): Promise<any> {\r\n return this.apiGet('/api/v1/sms/received');\r\n }\r\n\r\n // ─── Devices & Account ─────────────────────────────\r\n\r\n /**\r\n * List paired devices.\r\n */\r\n async getDevices(): Promise<any> {\r\n return this.apiGet('/api/v1/devices');\r\n }\r\n\r\n /**\r\n * Get account usage stats.\r\n */\r\n async getUsage(): Promise<any> {\r\n return this.apiGet('/api/v1/account/usage');\r\n }\r\n\r\n // ─── E2E Encryption ──────────────────────────────\r\n\r\n /**\r\n * Get E2E encryption status for a device.\r\n */\r\n async getDeviceE2EStatus(deviceId: string): Promise<{ success: boolean; data?: DeviceE2EStatus; error?: string }> {\r\n return this.apiGet(`/api/v1/devices/${deviceId}/e2e-status`);\r\n }\r\n\r\n /**\r\n * Get device public key for E2E encryption.\r\n */\r\n async getDevicePublicKey(deviceId: string): Promise<{ success: boolean; data?: DevicePublicKeyInfo; error?: string }> {\r\n return this.apiGet(`/api/v1/devices/${deviceId}/public-key`);\r\n }\r\n\r\n /**\r\n * Verify that a stored fingerprint matches the current device key.\r\n */\r\n async verifyDeviceKey(deviceId: string, storedFingerprint: string): Promise<VerifyDeviceKeyResult> {\r\n const res = await this.getDeviceE2EStatus(deviceId);\r\n if (!res.success || !res.data) {\r\n return { valid: false, needsRePairing: true };\r\n }\r\n const status = res.data;\r\n if (!status.hasPublicKey || !status.publicKeyFingerprint) {\r\n return { valid: false, needsRePairing: true };\r\n }\r\n const matches = status.publicKeyFingerprint === storedFingerprint;\r\n return {\r\n valid: matches,\r\n currentFingerprint: status.publicKeyFingerprint,\r\n needsRePairing: !matches,\r\n };\r\n }\r\n\r\n /**\r\n * Send encrypted SMS via SMSTunnel API.\r\n * The payload is encrypted client-side with the device's RSA public key.\r\n */\r\n async sendEncryptedSms(\r\n encryptedPayload: string,\r\n deviceId: string,\r\n is2FA: boolean = false,\r\n ): Promise<SendSmsResult> {\r\n return this.apiPost('/api/v1/sms/send', {\r\n encrypted: true,\r\n encryptedPayload,\r\n deviceId,\r\n is2FA,\r\n });\r\n }\r\n\r\n /**\r\n * List pairings for the authenticated user.\r\n */\r\n async getPairings(): Promise<any> {\r\n return this.apiGet('/api/v1/pairings');\r\n }\r\n\r\n // ─── Internal helpers ──────────────────────────────\r\n\r\n /** Make an authenticated GET request to SMSTunnel */\r\n private async apiGet(path: string): Promise<any> {\r\n const config = await this.options.storage.getConfig();\r\n if (!config.apiKey || !config.serverUrl) {\r\n return { success: false, error: 'SMSTunnel is not configured.' };\r\n }\r\n const serverUrl = config.serverUrl.replace(/\\/$/, '');\r\n try {\r\n const res = await fetch(`${serverUrl}${path}`, {\r\n headers: { 'X-API-Key': config.apiKey },\r\n });\r\n return await res.json();\r\n } catch (err: any) {\r\n return { success: false, error: err.message };\r\n }\r\n }\r\n\r\n /** Make an authenticated POST request to SMSTunnel */\r\n private async apiPost(path: string, body: any): Promise<any> {\r\n const config = await this.options.storage.getConfig();\r\n if (!config.apiKey || !config.serverUrl) {\r\n return { success: false, error: 'SMSTunnel is not configured.' };\r\n }\r\n const serverUrl = config.serverUrl.replace(/\\/$/, '');\r\n try {\r\n const res = await fetch(`${serverUrl}${path}`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'X-API-Key': config.apiKey,\r\n },\r\n body: JSON.stringify(body),\r\n });\r\n return await res.json();\r\n } catch (err: any) {\r\n return { success: false, error: err.message };\r\n }\r\n }\r\n}\r\n","export const SMSTUNNEL_OPTIONS = 'SMSTUNNEL_OPTIONS';\r\n\r\nexport const SMSTUNNEL_DEFAULT_PREFIX = 'smstunnel';\r\n\r\n/**\r\n * Public paths that should be excluded from auth guards.\r\n * Use with your global guard to skip auth on callback/polling routes.\r\n *\r\n * Example (NestJS):\r\n * if (SMSTUNNEL_PUBLIC_PATHS.some(p => request.url.includes(p))) return true;\r\n */\r\nexport const SMSTUNNEL_PUBLIC_PATHS = [\r\n '/smstunnel/pairing-status/',\r\n '/smstunnel/callback',\r\n '/wp-json/smstunnel/v1/setup-callback',\r\n];\r\n\r\n/**\r\n * Returns public paths with a custom prefix.\r\n */\r\nexport function getSmsTunnelPublicPaths(prefix: string): string[] {\r\n return [\r\n `/${prefix}/pairing-status/`,\r\n `/${prefix}/callback`,\r\n '/wp-json/smstunnel/v1/setup-callback',\r\n ];\r\n}\r\n","import {\r\n Controller,\r\n Post,\r\n Get,\r\n Body,\r\n Param,\r\n Inject,\r\n Logger,\r\n} from '@nestjs/common';\r\nimport { SmsTunnelService } from './smstunnel.service';\r\nimport { SMSTUNNEL_OPTIONS } from './smstunnel.constants';\r\nimport type {\r\n SmsTunnelModuleOptions,\r\n PairingCallbackBody,\r\n LegacyCallbackBody,\r\n} from './smstunnel.interfaces';\r\n\r\n/**\r\n * Decorator to mark routes as public (no auth).\r\n * If you have your own @Public() decorator, the module re-exports\r\n * SMSTUNNEL_PUBLIC_PATHS so you can exclude them in your guard.\r\n */\r\nfunction SmsTunnelPublic() {\r\n // We use Reflect metadata so consumers can detect public routes\r\n return (target: any, key: string, descriptor: PropertyDescriptor) => {\r\n Reflect.defineMetadata('isPublic', true, descriptor.value);\r\n return descriptor;\r\n };\r\n}\r\n\r\n@Controller()\r\nexport class SmsTunnelController {\r\n private readonly logger = new Logger(SmsTunnelController.name);\r\n private readonly prefix: string;\r\n\r\n constructor(\r\n private readonly smsTunnelService: SmsTunnelService,\r\n @Inject(SMSTUNNEL_OPTIONS)\r\n private readonly options: SmsTunnelModuleOptions,\r\n ) {\r\n this.prefix = options.routePrefix || 'smstunnel';\r\n }\r\n\r\n /**\r\n * Get current pairing status.\r\n * Route: GET /{prefix}/status\r\n */\r\n @Get('smstunnel/status')\r\n async getStatus() {\r\n return this.smsTunnelService.getStatus();\r\n }\r\n\r\n /**\r\n * Create a pairing token on SMSTunnel.\r\n * Route: POST /{prefix}/create-token\r\n */\r\n @Post('smstunnel/create-token')\r\n async createToken() {\r\n return this.smsTunnelService.createPairingToken();\r\n }\r\n\r\n /**\r\n * Proxy polling to SMSTunnel (avoids CORS issues).\r\n * This should be public (no auth) - just proxying SMSTunnel's endpoint.\r\n * Route: GET /{prefix}/pairing-status/:token\r\n */\r\n @SmsTunnelPublic()\r\n @Get('smstunnel/pairing-status/:token')\r\n async pairingStatus(@Param('token') token: string) {\r\n return this.smsTunnelService.getPairingStatus(token);\r\n }\r\n\r\n /**\r\n * Callback from SMSTunnel after pairing completes.\r\n * Route: POST /{prefix}/callback\r\n */\r\n @SmsTunnelPublic()\r\n @Post('smstunnel/callback')\r\n async pairingCallback(@Body() body: PairingCallbackBody) {\r\n this.logger.log(\r\n `SMSTunnel callback: event=${body.event}, token=${body.token?.substring(0, 8)}...`,\r\n );\r\n const success = await this.smsTunnelService.handlePairingCallback(body);\r\n return { success };\r\n }\r\n\r\n /**\r\n * WordPress-compatible legacy callback.\r\n * Route: POST /wp-json/smstunnel/v1/setup-callback\r\n */\r\n @SmsTunnelPublic()\r\n @Post('wp-json/smstunnel/v1/setup-callback')\r\n async setupCallbackWp(@Body() body: LegacyCallbackBody) {\r\n this.logger.log(`SMSTunnel legacy callback. Status: ${body.status || 'unknown'}`);\r\n const success = await this.smsTunnelService.handleLegacyCallback(\r\n body.site_token,\r\n body.api_key,\r\n body.device_name,\r\n );\r\n return { success };\r\n }\r\n\r\n /**\r\n * Unpair the connected device.\r\n * Route: POST /{prefix}/unpair\r\n */\r\n @Post('smstunnel/unpair')\r\n async unpair() {\r\n await this.smsTunnelService.unpair();\r\n return { success: true };\r\n }\r\n\r\n /**\r\n * Send an SMS via SMSTunnel.\r\n * Route: POST /{prefix}/send\r\n */\r\n @Post('smstunnel/send')\r\n async sendSms(@Body() body: { to: string; message: string }) {\r\n return this.smsTunnelService.sendSms(body.to, body.message);\r\n }\r\n\r\n /**\r\n * Update server URL configuration.\r\n * Route: POST /{prefix}/update-config\r\n */\r\n @Post('smstunnel/update-config')\r\n async updateConfig(@Body() body: { serverUrl: string }) {\r\n await this.smsTunnelService.updateServerUrl(body.serverUrl);\r\n return { success: true };\r\n }\r\n\r\n // ─── Extended SMS endpoints ────────────────────────\r\n\r\n /**\r\n * Send a 2FA SMS.\r\n * Route: POST /{prefix}/send-2fa\r\n */\r\n @Post('smstunnel/send-2fa')\r\n async send2fa(@Body() body: { to: string; code: string; template?: string }) {\r\n return this.smsTunnelService.send2fa(body.to, body.code, body.template);\r\n }\r\n\r\n /**\r\n * Send bulk SMS.\r\n * Route: POST /{prefix}/send-bulk\r\n */\r\n @Post('smstunnel/send-bulk')\r\n async sendBulk(@Body() body: { messages: Array<{ to: string; message: string }> }) {\r\n return this.smsTunnelService.sendBulk(body.messages);\r\n }\r\n\r\n /**\r\n * Get SMS delivery status.\r\n * Route: GET /{prefix}/sms-status/:messageId\r\n */\r\n @Get('smstunnel/sms-status/:messageId')\r\n async smsStatus(@Param('messageId') messageId: string) {\r\n return this.smsTunnelService.getSmsStatus(messageId);\r\n }\r\n\r\n /**\r\n * Get received SMS (inbox).\r\n * Route: GET /{prefix}/received\r\n */\r\n @Get('smstunnel/received')\r\n async receivedSms() {\r\n return this.smsTunnelService.getReceivedSms();\r\n }\r\n\r\n // ─── E2E Encryption ────────────────────────────────\r\n\r\n /**\r\n * Get E2E encryption status for a device.\r\n * Route: GET /{prefix}/e2e-status/:deviceId\r\n */\r\n @Get('smstunnel/e2e-status/:deviceId')\r\n async e2eStatus(@Param('deviceId') deviceId: string) {\r\n return this.smsTunnelService.getDeviceE2EStatus(deviceId);\r\n }\r\n\r\n /**\r\n * Get device public key for E2E encryption.\r\n * Route: GET /{prefix}/public-key/:deviceId\r\n */\r\n @Get('smstunnel/public-key/:deviceId')\r\n async publicKey(@Param('deviceId') deviceId: string) {\r\n return this.smsTunnelService.getDevicePublicKey(deviceId);\r\n }\r\n\r\n /**\r\n * Verify device key fingerprint.\r\n * Route: POST /{prefix}/verify-key\r\n */\r\n @Post('smstunnel/verify-key')\r\n async verifyKey(@Body() body: { deviceId: string; fingerprint: string }) {\r\n return this.smsTunnelService.verifyDeviceKey(body.deviceId, body.fingerprint);\r\n }\r\n\r\n /**\r\n * Send encrypted SMS.\r\n * Route: POST /{prefix}/send-encrypted\r\n */\r\n @Post('smstunnel/send-encrypted')\r\n async sendEncrypted(\r\n @Body() body: { encryptedPayload: string; deviceId: string; is2FA?: boolean },\r\n ) {\r\n return this.smsTunnelService.sendEncryptedSms(\r\n body.encryptedPayload,\r\n body.deviceId,\r\n body.is2FA,\r\n );\r\n }\r\n\r\n /**\r\n * List pairings.\r\n * Route: GET /{prefix}/pairings\r\n */\r\n @Get('smstunnel/pairings')\r\n async pairings() {\r\n return this.smsTunnelService.getPairings();\r\n }\r\n\r\n // ─── Devices & Account ─────────────────────────────\r\n\r\n /**\r\n * List paired devices.\r\n * Route: GET /{prefix}/devices\r\n */\r\n @Get('smstunnel/devices')\r\n async devices() {\r\n return this.smsTunnelService.getDevices();\r\n }\r\n\r\n /**\r\n * Get account usage stats.\r\n * Route: GET /{prefix}/usage\r\n */\r\n @Get('smstunnel/usage')\r\n async usage() {\r\n return this.smsTunnelService.getUsage();\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,iBAAgD;;;ACAhD,oBAA2C;;;ACApC,IAAM,oBAAoB;AAE1B,IAAM,2BAA2B;AASjC,IAAM,yBAAyB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AACF;AAKO,SAAS,wBAAwB,QAA0B;AAChE,SAAO;AAAA,IACL,IAAI,MAAM;AAAA,IACV,IAAI,MAAM;AAAA,IACV;AAAA,EACF;AACF;;;ADVO,IAAM,mBAAN,MAAuB;AAAA,EAG5B,YAEmB,SACjB;AADiB;AAJnB,SAAiB,SAAS,IAAI,qBAAO,iBAAiB,IAAI;AAAA,EAKvD;AAAA;AAAA;AAAA;AAAA,EAKH,MAAM,YAAsC;AAC1C,UAAM,SAAS,MAAM,KAAK,QAAQ,QAAQ,UAAU;AACpD,WAAO;AAAA,MACL,QAAQ,CAAC,CAAC,OAAO;AAAA,MACjB,WAAW,OAAO;AAAA,MAClB,YAAY,OAAO;AAAA,IACrB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,qBAAiD;AACrD,UAAM,SAAS,MAAM,KAAK,QAAQ,QAAQ,UAAU;AACpD,QAAI,CAAC,OAAO,WAAW;AACrB,aAAO,EAAE,SAAS,OAAO,OAAO,0CAA0C;AAAA,IAC5E;AAEA,UAAM,YAAY,OAAO,UAAU,QAAQ,OAAO,EAAE;AAEpD,QAAI,KAAK,QAAQ,kBAAkB;AACjC,aAAO,KAAK,6BAA6B,SAAS;AAAA,IACpD;AAEA,WAAO,KAAK,yBAAyB,SAAS;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,yBAAyB,WAA+C;AACpF,UAAM,SAAS,KAAK,QAAQ,eAAe;AAC3C,UAAM,cAAc,GAAG,KAAK,QAAQ,gBAAgB,QAAQ,OAAO,EAAE,CAAC,IAAI,MAAM;AAEhF,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,SAAS,iCAAiC;AAAA,QACnE,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU;AAAA,UACnB,QAAQ;AAAA,UACR,aAAa,KAAK,QAAQ,eAAe;AAAA,UACzC,YAAY,KAAK,QAAQ,cAAc,KAAK,QAAQ;AAAA,UACpD,SAAS,EAAE,YAAY;AAAA,QACzB,CAAC;AAAA,MACH,CAAC;AAED,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,UAAU,MAAM,IAAI,KAAK;AAC/B,aAAK,OAAO,KAAK,kCAAkC,IAAI,MAAM,IAAI,OAAO,EAAE;AAC1E,eAAO,EAAE,SAAS,OAAO,OAAO,6BAA6B,IAAI,MAAM,GAAG;AAAA,MAC5E;AAEA,YAAM,OAAY,MAAM,IAAI,KAAK;AAEjC,YAAM,KAAK,QAAQ,QAAQ,aAAa,EAAE,WAAW,KAAK,MAAM,CAAC;AAEjE,WAAK,OAAO,IAAI,0BAA0B,KAAK,OAAO,UAAU,GAAG,EAAE,CAAC,KAAK;AAE3E,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,KAAK;AAAA,QACZ,QAAQ,KAAK;AAAA,QACb,WAAW,KAAK;AAAA,QAChB,SAAS,KAAK,WAAW,GAAG,SAAS,mBAAmB,KAAK,KAAK;AAAA,MACpE;AAAA,IACF,SAAS,KAAU;AACjB,WAAK,OAAO,MAAM,iCAAiC,IAAI,OAAO,EAAE;AAChE,aAAO,EAAE,SAAS,OAAO,OAAO,mCAAmC,IAAI,OAAO,GAAG;AAAA,IACnF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,6BAA6B,WAA+C;AACxF,UAAM,SAAS,KAAK,QAAQ,eAAe;AAC3C,UAAM,cAAc,GAAG,KAAK,QAAQ,gBAAgB,QAAQ,OAAO,EAAE,CAAC,IAAI,MAAM;AAEhF,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,SAAS,yBAAyB;AAAA,QAC3D,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,aAAa,KAAK,QAAQ;AAAA,QAC5B;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACnB,YAAY,KAAK,QAAQ,eAAe;AAAA,UACxC,YAAY,KAAK,QAAQ,cAAc;AAAA,UACvC;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAED,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,UAAU,MAAM,IAAI,KAAK;AAC/B,aAAK,OAAO,KAAK,mCAAmC,IAAI,MAAM,IAAI,OAAO,EAAE;AAC3E,eAAO,EAAE,SAAS,OAAO,OAAO,6BAA6B,IAAI,MAAM,GAAG;AAAA,MAC5E;AAEA,YAAM,OAA2B,MAAM,IAAI,KAAK;AAEhD,UAAI,CAAC,KAAK,WAAW,CAAC,KAAK,YAAY;AACrC,eAAO,EAAE,SAAS,OAAO,OAAO,KAAK,SAAS,yBAAyB;AAAA,MACzE;AAGA,YAAM,eAAoC,EAAE,WAAW,KAAK,WAAW,MAAM;AAG7E,UAAI,KAAK,QAAQ,KAAK;AACpB,qBAAa,SAAS,KAAK,OAAO;AAAA,MACpC;AAEA,YAAM,KAAK,QAAQ,QAAQ,aAAa,YAAY;AAEpD,WAAK,OAAO;AAAA,QACV,+BAA+B,KAAK,WAAW,OAAO,UAAU,GAAG,EAAE,CAAC,eAAe,KAAK,KAAK;AAAA,MACjG;AAEA,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,KAAK,WAAW;AAAA,QACvB,QAAQ,KAAK,WAAW;AAAA,QACxB,WAAW,KAAK,WAAW;AAAA,QAC3B,SAAS,GAAG,SAAS,mBAAmB,KAAK,WAAW,KAAK;AAAA,MAC/D;AAAA,IACF,SAAS,KAAU;AACjB,WAAK,OAAO,MAAM,kCAAkC,IAAI,OAAO,EAAE;AACjE,aAAO,EAAE,SAAS,OAAO,OAAO,mCAAmC,IAAI,OAAO,GAAG;AAAA,IACnF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBAAiB,OAA6C;AAClE,UAAM,SAAS,MAAM,KAAK,QAAQ,QAAQ,UAAU;AACpD,QAAI,CAAC,OAAO,WAAW;AACrB,aAAO,EAAE,QAAQ,SAAS,OAAO,sCAAsC;AAAA,IACzE;AAEA,UAAM,YAAY,OAAO,UAAU,QAAQ,OAAO,EAAE;AAEpD,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,SAAS,mBAAmB,KAAK,EAAE;AAC9D,UAAI,CAAC,IAAI,IAAI;AACX,eAAO,EAAE,QAAQ,UAAU;AAAA,MAC7B;AACA,aAAQ,MAAM,IAAI,KAAK;AAAA,IACzB,QAAQ;AACN,aAAO,EAAE,QAAQ,SAAS,OAAO,8BAA8B;AAAA,IACjE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,sBAAsB,MAA6C;AACvE,QAAI,KAAK,UAAU,qBAAqB;AACtC,WAAK,OAAO,KAAK,2BAA2B,KAAK,KAAK,EAAE;AACxD,aAAO;AAAA,IACT;AAEA,UAAM,SAAS,MAAM,KAAK,QAAQ,QAAQ,UAAU;AAEpD,QAAI,OAAO,aAAa,OAAO,cAAc,KAAK,OAAO;AACvD,WAAK,OAAO;AAAA,QACV,qCAAqC,OAAO,WAAW,UAAU,GAAG,CAAC,CAAC,SAAS,KAAK,OAAO,UAAU,GAAG,CAAC,CAAC;AAAA,MAC5G;AACA,aAAO;AAAA,IACT;AAEA,UAAM,KAAK,QAAQ,QAAQ,aAAa;AAAA,MACtC,QAAQ,KAAK;AAAA,MACb,YAAY,KAAK,cAAc;AAAA,IACjC,CAAC;AAED,SAAK,OAAO;AAAA,MACV,6BAA6B,KAAK,UAAU,cAAc,KAAK,QAAQ,UAAU,GAAG,EAAE,CAAC;AAAA,IACzF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,qBACJ,WACA,QACA,YACkB;AAClB,UAAM,SAAS,MAAM,KAAK,QAAQ,QAAQ,UAAU;AAEpD,QAAI,OAAO,cAAc,WAAW;AAClC,WAAK,OAAO,KAAK,sCAAsC;AACvD,aAAO;AAAA,IACT;AAEA,UAAM,SAA0D,EAAE,OAAO;AACzE,QAAI,WAAY,QAAO,aAAa;AAEpC,UAAM,KAAK,QAAQ,QAAQ,aAAa,MAAM;AAC9C,SAAK,OAAO,IAAI,sCAAsC,cAAc,SAAS,EAAE;AAC/E,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ,IAAY,SAAyC;AACjE,UAAM,SAAS,MAAM,KAAK,QAAQ,QAAQ,UAAU;AAEpD,QAAI,CAAC,OAAO,QAAQ;AAClB,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAEA,QAAI,CAAC,OAAO,WAAW;AACrB,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAEA,UAAM,YAAY,OAAO,UAAU,QAAQ,OAAO,EAAE;AAEpD,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,SAAS,oBAAoB;AAAA,QACtD,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,aAAa,OAAO;AAAA,QACtB;AAAA,QACA,MAAM,KAAK,UAAU,EAAE,IAAI,QAAQ,CAAC;AAAA,MACtC,CAAC;AAED,YAAM,OAAY,MAAM,IAAI,KAAK;AAEjC,UAAI,KAAK,SAAS;AAChB,aAAK,OAAO,IAAI,2BAA2B,KAAK,SAAS,OAAO,EAAE,EAAE;AACpE,eAAO,EAAE,SAAS,MAAM,WAAW,KAAK,UAAU;AAAA,MACpD;AAEA,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,KAAK,SAAS,KAAK,WAAW;AAAA,MACvC;AAAA,IACF,SAAS,KAAU;AACjB,WAAK,OAAO,MAAM,mBAAmB,IAAI,OAAO,EAAE;AAClD,aAAO,EAAE,SAAS,OAAO,OAAO,uBAAuB,IAAI,OAAO,GAAG;AAAA,IACvE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAwB;AAC5B,UAAM,KAAK,QAAQ,QAAQ,YAAY;AACvC,SAAK,OAAO,IAAI,oBAAoB;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAAgB,WAAkC;AACtD,UAAM,KAAK,QAAQ,QAAQ,aAAa;AAAA,MACtC,WAAW,UAAU,QAAQ,OAAO,EAAE;AAAA,IACxC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,QAAQ,IAAY,MAAc,UAA2C;AACjF,WAAO,KAAK,QAAQ,wBAAwB,EAAE,IAAI,MAAM,SAAS,CAAC;AAAA,EACpE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SACJ,UACgE;AAChE,WAAO,KAAK,QAAQ,yBAAyB,EAAE,SAAS,CAAC;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,WAAiC;AAClD,WAAO,KAAK,OAAO,sBAAsB,SAAS,EAAE;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBAA+B;AACnC,WAAO,KAAK,OAAO,sBAAsB;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,aAA2B;AAC/B,WAAO,KAAK,OAAO,iBAAiB;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAyB;AAC7B,WAAO,KAAK,OAAO,uBAAuB;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,mBAAmB,UAAyF;AAChH,WAAO,KAAK,OAAO,mBAAmB,QAAQ,aAAa;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,mBAAmB,UAA6F;AACpH,WAAO,KAAK,OAAO,mBAAmB,QAAQ,aAAa;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAAgB,UAAkB,mBAA2D;AACjG,UAAM,MAAM,MAAM,KAAK,mBAAmB,QAAQ;AAClD,QAAI,CAAC,IAAI,WAAW,CAAC,IAAI,MAAM;AAC7B,aAAO,EAAE,OAAO,OAAO,gBAAgB,KAAK;AAAA,IAC9C;AACA,UAAM,SAAS,IAAI;AACnB,QAAI,CAAC,OAAO,gBAAgB,CAAC,OAAO,sBAAsB;AACxD,aAAO,EAAE,OAAO,OAAO,gBAAgB,KAAK;AAAA,IAC9C;AACA,UAAM,UAAU,OAAO,yBAAyB;AAChD,WAAO;AAAA,MACL,OAAO;AAAA,MACP,oBAAoB,OAAO;AAAA,MAC3B,gBAAgB,CAAC;AAAA,IACnB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,iBACJ,kBACA,UACA,QAAiB,OACO;AACxB,WAAO,KAAK,QAAQ,oBAAoB;AAAA,MACtC,WAAW;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAA4B;AAChC,WAAO,KAAK,OAAO,kBAAkB;AAAA,EACvC;AAAA;AAAA;AAAA,EAKA,MAAc,OAAO,MAA4B;AAC/C,UAAM,SAAS,MAAM,KAAK,QAAQ,QAAQ,UAAU;AACpD,QAAI,CAAC,OAAO,UAAU,CAAC,OAAO,WAAW;AACvC,aAAO,EAAE,SAAS,OAAO,OAAO,+BAA+B;AAAA,IACjE;AACA,UAAM,YAAY,OAAO,UAAU,QAAQ,OAAO,EAAE;AACpD,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,SAAS,GAAG,IAAI,IAAI;AAAA,QAC7C,SAAS,EAAE,aAAa,OAAO,OAAO;AAAA,MACxC,CAAC;AACD,aAAO,MAAM,IAAI,KAAK;AAAA,IACxB,SAAS,KAAU;AACjB,aAAO,EAAE,SAAS,OAAO,OAAO,IAAI,QAAQ;AAAA,IAC9C;AAAA,EACF;AAAA;AAAA,EAGA,MAAc,QAAQ,MAAc,MAAyB;AAC3D,UAAM,SAAS,MAAM,KAAK,QAAQ,QAAQ,UAAU;AACpD,QAAI,CAAC,OAAO,UAAU,CAAC,OAAO,WAAW;AACvC,aAAO,EAAE,SAAS,OAAO,OAAO,+BAA+B;AAAA,IACjE;AACA,UAAM,YAAY,OAAO,UAAU,QAAQ,OAAO,EAAE;AACpD,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,SAAS,GAAG,IAAI,IAAI;AAAA,QAC7C,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,aAAa,OAAO;AAAA,QACtB;AAAA,QACA,MAAM,KAAK,UAAU,IAAI;AAAA,MAC3B,CAAC;AACD,aAAO,MAAM,IAAI,KAAK;AAAA,IACxB,SAAS,KAAU;AACjB,aAAO,EAAE,SAAS,OAAO,OAAO,IAAI,QAAQ;AAAA,IAC9C;AAAA,EACF;AACF;AAjba,mBAAN;AAAA,MADN,0BAAW;AAAA,EAKP,6CAAO,iBAAiB;AAAA,GAJhB;;;AEhBb,IAAAC,iBAQO;AAcP,SAAS,kBAAkB;AAEzB,SAAO,CAAC,QAAa,KAAa,eAAmC;AACnE,YAAQ,eAAe,YAAY,MAAM,WAAW,KAAK;AACzD,WAAO;AAAA,EACT;AACF;AAGO,IAAM,sBAAN,MAA0B;AAAA,EAI/B,YACmB,kBAEA,SACjB;AAHiB;AAEA;AANnB,SAAiB,SAAS,IAAI,sBAAO,oBAAoB,IAAI;AAQ3D,SAAK,SAAS,QAAQ,eAAe;AAAA,EACvC;AAAA,EAOA,MAAM,YAAY;AAChB,WAAO,KAAK,iBAAiB,UAAU;AAAA,EACzC;AAAA,EAOA,MAAM,cAAc;AAClB,WAAO,KAAK,iBAAiB,mBAAmB;AAAA,EAClD;AAAA,EASA,MAAM,cAA8B,OAAe;AACjD,WAAO,KAAK,iBAAiB,iBAAiB,KAAK;AAAA,EACrD;AAAA,EAQA,MAAM,gBAAwB,MAA2B;AACvD,SAAK,OAAO;AAAA,MACV,6BAA6B,KAAK,KAAK,WAAW,KAAK,OAAO,UAAU,GAAG,CAAC,CAAC;AAAA,IAC/E;AACA,UAAM,UAAU,MAAM,KAAK,iBAAiB,sBAAsB,IAAI;AACtE,WAAO,EAAE,QAAQ;AAAA,EACnB;AAAA,EAQA,MAAM,gBAAwB,MAA0B;AACtD,SAAK,OAAO,IAAI,sCAAsC,KAAK,UAAU,SAAS,EAAE;AAChF,UAAM,UAAU,MAAM,KAAK,iBAAiB;AAAA,MAC1C,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,IACP;AACA,WAAO,EAAE,QAAQ;AAAA,EACnB;AAAA,EAOA,MAAM,SAAS;AACb,UAAM,KAAK,iBAAiB,OAAO;AACnC,WAAO,EAAE,SAAS,KAAK;AAAA,EACzB;AAAA,EAOA,MAAM,QAAgB,MAAuC;AAC3D,WAAO,KAAK,iBAAiB,QAAQ,KAAK,IAAI,KAAK,OAAO;AAAA,EAC5D;AAAA,EAOA,MAAM,aAAqB,MAA6B;AACtD,UAAM,KAAK,iBAAiB,gBAAgB,KAAK,SAAS;AAC1D,WAAO,EAAE,SAAS,KAAK;AAAA,EACzB;AAAA,EASA,MAAM,QAAgB,MAAuD;AAC3E,WAAO,KAAK,iBAAiB,QAAQ,KAAK,IAAI,KAAK,MAAM,KAAK,QAAQ;AAAA,EACxE;AAAA,EAOA,MAAM,SAAiB,MAA4D;AACjF,WAAO,KAAK,iBAAiB,SAAS,KAAK,QAAQ;AAAA,EACrD;AAAA,EAOA,MAAM,UAA8B,WAAmB;AACrD,WAAO,KAAK,iBAAiB,aAAa,SAAS;AAAA,EACrD;AAAA,EAOA,MAAM,cAAc;AAClB,WAAO,KAAK,iBAAiB,eAAe;AAAA,EAC9C;AAAA,EASA,MAAM,UAA6B,UAAkB;AACnD,WAAO,KAAK,iBAAiB,mBAAmB,QAAQ;AAAA,EAC1D;AAAA,EAOA,MAAM,UAA6B,UAAkB;AACnD,WAAO,KAAK,iBAAiB,mBAAmB,QAAQ;AAAA,EAC1D;AAAA,EAOA,MAAM,UAAkB,MAAiD;AACvE,WAAO,KAAK,iBAAiB,gBAAgB,KAAK,UAAU,KAAK,WAAW;AAAA,EAC9E;AAAA,EAOA,MAAM,cACI,MACR;AACA,WAAO,KAAK,iBAAiB;AAAA,MAC3B,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,IACP;AAAA,EACF;AAAA,EAOA,MAAM,WAAW;AACf,WAAO,KAAK,iBAAiB,YAAY;AAAA,EAC3C;AAAA,EASA,MAAM,UAAU;AACd,WAAO,KAAK,iBAAiB,WAAW;AAAA,EAC1C;AAAA,EAOA,MAAM,QAAQ;AACZ,WAAO,KAAK,iBAAiB,SAAS;AAAA,EACxC;AACF;AAjMQ;AAAA,MADL,oBAAI,kBAAkB;AAAA,GAhBZ,oBAiBL;AASA;AAAA,MADL,qBAAK,wBAAwB;AAAA,GAzBnB,oBA0BL;AAWA;AAAA,EAFL,gBAAgB;AAAA,MAChB,oBAAI,iCAAiC;AAAA,EACjB,6CAAM,OAAO;AAAA,GArCvB,oBAqCL;AAUA;AAAA,EAFL,gBAAgB;AAAA,MAChB,qBAAK,oBAAoB;AAAA,EACH,4CAAK;AAAA,GA/CjB,oBA+CL;AAcA;AAAA,EAFL,gBAAgB;AAAA,MAChB,qBAAK,qCAAqC;AAAA,EACpB,4CAAK;AAAA,GA7DjB,oBA6DL;AAeA;AAAA,MADL,qBAAK,kBAAkB;AAAA,GA3Eb,oBA4EL;AAUA;AAAA,MADL,qBAAK,gBAAgB;AAAA,EACP,4CAAK;AAAA,GAtFT,oBAsFL;AASA;AAAA,MADL,qBAAK,yBAAyB;AAAA,EACX,4CAAK;AAAA,GA/Fd,oBA+FL;AAYA;AAAA,MADL,qBAAK,oBAAoB;AAAA,EACX,4CAAK;AAAA,GA3GT,oBA2GL;AASA;AAAA,MADL,qBAAK,qBAAqB;AAAA,EACX,4CAAK;AAAA,GApHV,oBAoHL;AASA;AAAA,MADL,oBAAI,iCAAiC;AAAA,EACrB,6CAAM,WAAW;AAAA,GA7HvB,oBA6HL;AASA;AAAA,MADL,oBAAI,oBAAoB;AAAA,GArId,oBAsIL;AAWA;AAAA,MADL,oBAAI,gCAAgC;AAAA,EACpB,6CAAM,UAAU;AAAA,GAjJtB,oBAiJL;AASA;AAAA,MADL,oBAAI,gCAAgC;AAAA,EACpB,6CAAM,UAAU;AAAA,GA1JtB,oBA0JL;AASA;AAAA,MADL,qBAAK,sBAAsB;AAAA,EACX,4CAAK;AAAA,GAnKX,oBAmKL;AASA;AAAA,MADL,qBAAK,0BAA0B;AAAA,EAE7B,4CAAK;AAAA,GA7KG,oBA4KL;AAeA;AAAA,MADL,oBAAI,oBAAoB;AAAA,GA1Ld,oBA2LL;AAWA;AAAA,MADL,oBAAI,mBAAmB;AAAA,GArMb,oBAsML;AASA;AAAA,MADL,oBAAI,iBAAiB;AAAA,GA9MX,oBA+ML;AA/MK,sBAAN;AAAA,MADN,2BAAW;AAAA,EAOP,8CAAO,iBAAiB;AAAA,GANhB;;;AHrBN,IAAM,kBAAN,MAAsB;AAAA,EAC3B,OAAO,QAAQ,SAAgD;AAC7D,UAAM,kBAA4B;AAAA,MAChC,SAAS;AAAA,MACT,UAAU;AAAA,IACZ;AAEA,UAAM,cAAc,CAAC,mBAAmB;AACxC,QAAI,CAAC,QAAQ,sBAAsB;AAAA,IAGnC;AAEA,WAAO;AAAA,MACL,QAAQ;AAAA,MACR;AAAA,MACA,WAAW,CAAC,iBAAiB,gBAAgB;AAAA,MAC7C,SAAS,CAAC,gBAAgB;AAAA,MAC1B,QAAQ;AAAA,IACV;AAAA,EACF;AAAA,EAEA,OAAO,aAAa,cAA0D;AAC5E,UAAM,kBAA4B;AAAA,MAChC,SAAS;AAAA,MACT,YAAY,aAAa;AAAA,MACzB,QAAQ,aAAa,UAAU,CAAC;AAAA,IAClC;AAEA,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,aAAa,CAAC,mBAAmB;AAAA,MACjC,WAAW,CAAC,iBAAiB,gBAAgB;AAAA,MAC7C,SAAS,CAAC,gBAAgB;AAAA,MAC1B,QAAQ;AAAA,IACV;AAAA,EACF;AACF;AArCa,kBAAN;AAAA,MADN,uBAAO,CAAC,CAAC;AAAA,GACG;","names":["import_common","import_common"]}
|
|
1
|
+
{"version":3,"sources":["../../src/server/index.ts","../../src/server/smstunnel.module.ts","../../src/server/smstunnel.service.ts","../../src/server/smstunnel.constants.ts","../../src/server/smstunnel.controller.ts"],"sourcesContent":["export { SmsTunnelModule } from './smstunnel.module';\r\nexport { SmsTunnelService } from './smstunnel.service';\r\nexport { SmsTunnelController } from './smstunnel.controller';\r\nexport {\r\n SMSTUNNEL_OPTIONS,\r\n SMSTUNNEL_PUBLIC_PATHS,\r\n SMSTUNNEL_DEFAULT_PREFIX,\r\n getSmsTunnelPublicPaths,\r\n} from './smstunnel.constants';\r\nexport type {\r\n SmsTunnelConfig,\r\n SmsTunnelStorageAdapter,\r\n SmsTunnelModuleOptions,\r\n SmsTunnelModuleAsyncOptions,\r\n SmsTunnelStatus,\r\n SendSmsResult,\r\n CreateTokenResult,\r\n PairingStatusResult,\r\n PairingCallbackBody,\r\n LegacyCallbackBody,\r\n SaasActivateResult,\r\n DeviceE2EStatus,\r\n DevicePublicKeyInfo,\r\n VerifyDeviceKeyResult,\r\n} from './smstunnel.interfaces';\r\n","import { DynamicModule, Module, Provider } from '@nestjs/common';\r\nimport { SmsTunnelService } from './smstunnel.service';\r\nimport { SmsTunnelController } from './smstunnel.controller';\r\nimport { SMSTUNNEL_OPTIONS } from './smstunnel.constants';\r\nimport type {\r\n SmsTunnelModuleOptions,\r\n SmsTunnelModuleAsyncOptions,\r\n} from './smstunnel.interfaces';\r\n\r\n@Module({})\r\nexport class SmsTunnelModule {\r\n static forRoot(options: SmsTunnelModuleOptions): DynamicModule {\r\n const optionsProvider: Provider = {\r\n provide: SMSTUNNEL_OPTIONS,\r\n useValue: options,\r\n };\r\n\r\n const controllers = [SmsTunnelController];\r\n if (!options.enableLegacyCallback) {\r\n // Legacy callback is always registered but won't match\r\n // if the consumer doesn't enable it - no-op\r\n }\r\n\r\n return {\r\n module: SmsTunnelModule,\r\n controllers,\r\n providers: [optionsProvider, SmsTunnelService],\r\n exports: [SmsTunnelService],\r\n global: false,\r\n };\r\n }\r\n\r\n static forRootAsync(asyncOptions: SmsTunnelModuleAsyncOptions): DynamicModule {\r\n const optionsProvider: Provider = {\r\n provide: SMSTUNNEL_OPTIONS,\r\n useFactory: asyncOptions.useFactory,\r\n inject: asyncOptions.inject || [],\r\n };\r\n\r\n return {\r\n module: SmsTunnelModule,\r\n controllers: [SmsTunnelController],\r\n providers: [optionsProvider, SmsTunnelService],\r\n exports: [SmsTunnelService],\r\n global: false,\r\n };\r\n }\r\n}\r\n","import { Inject, Injectable, Logger } from '@nestjs/common';\r\nimport { SMSTUNNEL_OPTIONS } from './smstunnel.constants';\r\nimport type {\r\n SmsTunnelModuleOptions,\r\n SmsTunnelStatus,\r\n SendSmsResult,\r\n CreateTokenResult,\r\n PairingStatusResult,\r\n PairingCallbackBody,\r\n SaasActivateResult,\r\n DeviceE2EStatus,\r\n DevicePublicKeyInfo,\r\n VerifyDeviceKeyResult,\r\n} from './smstunnel.interfaces';\r\n\r\n@Injectable()\r\nexport class SmsTunnelService {\r\n private readonly logger = new Logger(SmsTunnelService.name);\r\n\r\n constructor(\r\n @Inject(SMSTUNNEL_OPTIONS)\r\n private readonly options: SmsTunnelModuleOptions,\r\n ) {}\r\n\r\n /**\r\n * Get current pairing status.\r\n */\r\n async getStatus(): Promise<SmsTunnelStatus> {\r\n const config = await this.options.storage.getConfig();\r\n return {\r\n paired: !!config.apiKey,\r\n serverUrl: config.serverUrl,\r\n deviceName: config.deviceName,\r\n };\r\n }\r\n\r\n /**\r\n * Create a pairing token on SMSTunnel server.\r\n * Uses Enterprise/SaaS flow when enterpriseApiKey is configured,\r\n * otherwise falls back to public-create flow.\r\n */\r\n async createPairingToken(): Promise<CreateTokenResult> {\r\n const config = await this.options.storage.getConfig();\r\n if (!config.serverUrl) {\r\n return { success: false, error: 'SMSTunnel server URL is not configured.' };\r\n }\r\n\r\n const serverUrl = config.serverUrl.replace(/\\/$/, '');\r\n\r\n if (this.options.enterpriseApiKey) {\r\n return this.createPairingTokenEnterprise(serverUrl);\r\n }\r\n\r\n return this.createPairingTokenPublic(serverUrl);\r\n }\r\n\r\n /**\r\n * Public flow: POST /api/v1/pairing/public-create\r\n */\r\n private async createPairingTokenPublic(serverUrl: string): Promise<CreateTokenResult> {\r\n const prefix = 'smstunnel';\r\n const callbackUrl = `${this.options.callbackBaseUrl.replace(/\\/$/, '')}/${prefix}/callback`;\r\n\r\n try {\r\n const res = await fetch(`${serverUrl}/api/v1/pairing/public-create`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n source: 'api',\r\n displayName: this.options.displayName || 'App',\r\n displayUrl: this.options.displayUrl || this.options.callbackBaseUrl,\r\n context: { callbackUrl },\r\n }),\r\n });\r\n\r\n if (!res.ok) {\r\n const errText = await res.text();\r\n this.logger.warn(`SMSTunnel create token failed: ${res.status} ${errText}`);\r\n return { success: false, error: `SMSTunnel returned error: ${res.status}` };\r\n }\r\n\r\n const data: any = await res.json();\r\n\r\n await this.options.storage.updateConfig({ siteToken: data.token });\r\n\r\n this.logger.log(`Pairing token created: ${data.token?.substring(0, 12)}...`);\r\n\r\n return {\r\n success: true,\r\n token: data.token,\r\n qrData: data.qrData,\r\n expiresAt: data.expiresAt,\r\n pollUrl: data.pollUrl || `${serverUrl}/api/v1/pairing/${data.token}`,\r\n };\r\n } catch (err: any) {\r\n this.logger.error(`SMSTunnel create token error: ${err.message}`);\r\n return { success: false, error: `Could not connect to SMSTunnel: ${err.message}` };\r\n }\r\n }\r\n\r\n /**\r\n * Enterprise/SaaS flow: POST /api/v1/saas/activate\r\n * Creates sub-accounts under the Enterprise account.\r\n */\r\n private async createPairingTokenEnterprise(serverUrl: string): Promise<CreateTokenResult> {\r\n const prefix = 'smstunnel';\r\n const callbackUrl = `${this.options.callbackBaseUrl.replace(/\\/$/, '')}/${prefix}/callback`;\r\n\r\n try {\r\n const res = await fetch(`${serverUrl}/api/v1/saas/activate`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'X-API-Key': this.options.enterpriseApiKey!,\r\n },\r\n body: JSON.stringify({\r\n clientName: this.options.displayName || 'App',\r\n externalId: this.options.externalId || 'default',\r\n callbackUrl,\r\n }),\r\n });\r\n\r\n if (!res.ok) {\r\n const errText = await res.text();\r\n this.logger.warn(`SMSTunnel SaaS activate failed: ${res.status} ${errText}`);\r\n return { success: false, error: `SMSTunnel returned error: ${res.status}` };\r\n }\r\n\r\n const data: SaasActivateResult = await res.json();\r\n\r\n if (!data.success || !data.activation) {\r\n return { success: false, error: data.error || 'SaaS activation failed' };\r\n }\r\n\r\n // Save the activation token as siteToken\r\n const configUpdate: Record<string, any> = { siteToken: data.activation.token };\r\n\r\n // If the server returned an API key, save it too\r\n if (data.apiKey?.key) {\r\n configUpdate.apiKey = data.apiKey.key;\r\n }\r\n\r\n await this.options.storage.updateConfig(configUpdate);\r\n\r\n this.logger.log(\r\n `SaaS pairing token created: ${data.activation.token?.substring(0, 12)}... (isNew: ${data.isNew})`,\r\n );\r\n\r\n return {\r\n success: true,\r\n token: data.activation.token,\r\n qrData: data.activation.qrData,\r\n expiresAt: data.activation.expiresAt,\r\n pollUrl: `${serverUrl}/api/v1/pairing/${data.activation.token}`,\r\n };\r\n } catch (err: any) {\r\n this.logger.error(`SMSTunnel SaaS activate error: ${err.message}`);\r\n return { success: false, error: `Could not connect to SMSTunnel: ${err.message}` };\r\n }\r\n }\r\n\r\n /**\r\n * Check pairing status by polling SMSTunnel.\r\n */\r\n async getPairingStatus(token: string): Promise<PairingStatusResult> {\r\n const config = await this.options.storage.getConfig();\r\n if (!config.serverUrl) {\r\n return { status: 'error', error: 'SMSTunnel server URL not configured' };\r\n }\r\n\r\n const serverUrl = config.serverUrl.replace(/\\/$/, '');\r\n\r\n try {\r\n const res = await fetch(`${serverUrl}/api/v1/pairing/${token}`);\r\n if (!res.ok) {\r\n return { status: 'expired' };\r\n }\r\n return (await res.json()) as PairingStatusResult;\r\n } catch {\r\n return { status: 'error', error: 'Could not contact SMSTunnel' };\r\n }\r\n }\r\n\r\n /**\r\n * Handle callback from SMSTunnel after pairing completes.\r\n */\r\n async handlePairingCallback(body: PairingCallbackBody): Promise<boolean> {\r\n if (body.event !== 'pairing_completed') {\r\n this.logger.warn(`Unknown callback event: ${body.event}`);\r\n return false;\r\n }\r\n\r\n const config = await this.options.storage.getConfig();\r\n\r\n if (config.siteToken && config.siteToken !== body.token) {\r\n this.logger.warn(\r\n `Callback token mismatch: expected ${config.siteToken?.substring(0, 8)}, got ${body.token?.substring(0, 8)}`,\r\n );\r\n return false;\r\n }\r\n\r\n await this.options.storage.updateConfig({\r\n apiKey: body.apiKey,\r\n deviceName: body.deviceName || 'Android Phone',\r\n });\r\n\r\n this.logger.log(\r\n `SMSTunnel paired! Device: ${body.deviceName}, API key: ${body.apiKey?.substring(0, 12)}...`,\r\n );\r\n return true;\r\n }\r\n\r\n /**\r\n * Handle legacy WordPress-compatible callback.\r\n */\r\n async handleLegacyCallback(\r\n siteToken: string,\r\n apiKey: string,\r\n deviceName?: string,\r\n ): Promise<boolean> {\r\n const config = await this.options.storage.getConfig();\r\n\r\n if (config.siteToken !== siteToken) {\r\n this.logger.warn('Legacy callback: site_token mismatch');\r\n return false;\r\n }\r\n\r\n const update: Partial<{ apiKey: string; deviceName: string }> = { apiKey };\r\n if (deviceName) update.deviceName = deviceName;\r\n\r\n await this.options.storage.updateConfig(update);\r\n this.logger.log(`SMSTunnel paired (legacy). Device: ${deviceName || 'unknown'}`);\r\n return true;\r\n }\r\n\r\n /**\r\n * Send SMS via SMSTunnel API.\r\n */\r\n async sendSms(to: string, message: string): Promise<SendSmsResult> {\r\n const config = await this.options.storage.getConfig();\r\n\r\n if (!config.apiKey) {\r\n return {\r\n success: false,\r\n error: 'SMSTunnel is not configured. Pair a device first.',\r\n };\r\n }\r\n\r\n if (!config.serverUrl) {\r\n return {\r\n success: false,\r\n error: 'SMSTunnel server URL is not configured.',\r\n };\r\n }\r\n\r\n const serverUrl = config.serverUrl.replace(/\\/$/, '');\r\n\r\n try {\r\n const res = await fetch(`${serverUrl}/api/v1/sms/send`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'X-API-Key': config.apiKey,\r\n },\r\n body: JSON.stringify({ to, message }),\r\n });\r\n\r\n const data: any = await res.json();\r\n\r\n if (data.success) {\r\n this.logger.log(`SMS sent via SMSTunnel: ${data.messageId} -> ${to}`);\r\n return { success: true, messageId: data.messageId };\r\n }\r\n\r\n return {\r\n success: false,\r\n error: data.error || data.message || 'SMSTunnel returned an error',\r\n };\r\n } catch (err: any) {\r\n this.logger.error(`SMS send error: ${err.message}`);\r\n return { success: false, error: `Could not send SMS: ${err.message}` };\r\n }\r\n }\r\n\r\n /**\r\n * Unpair - clear saved API key and device info.\r\n */\r\n async unpair(): Promise<void> {\r\n await this.options.storage.clearConfig();\r\n this.logger.log('SMSTunnel unpaired');\r\n }\r\n\r\n /**\r\n * Update server URL in config.\r\n */\r\n async updateServerUrl(serverUrl: string): Promise<void> {\r\n await this.options.storage.updateConfig({\r\n serverUrl: serverUrl.replace(/\\/$/, ''),\r\n });\r\n }\r\n\r\n // ─── Extended SMS endpoints ────────────────────────\r\n\r\n /**\r\n * Send 2FA SMS via SMSTunnel API.\r\n */\r\n async send2fa(to: string, code: string, template?: string): Promise<SendSmsResult> {\r\n return this.apiPost('/api/v1/sms/send-2fa', { to, code, template });\r\n }\r\n\r\n /**\r\n * Send bulk SMS via SMSTunnel API.\r\n */\r\n async sendBulk(\r\n messages: Array<{ to: string; message: string }>,\r\n ): Promise<{ success: boolean; results?: any[]; error?: string }> {\r\n return this.apiPost('/api/v1/sms/send-bulk', { messages });\r\n }\r\n\r\n /**\r\n * Get SMS delivery status.\r\n */\r\n async getSmsStatus(messageId: string): Promise<any> {\r\n return this.apiGet(`/api/v1/sms/status/${messageId}`);\r\n }\r\n\r\n /**\r\n * Get received SMS (inbox).\r\n */\r\n async getReceivedSms(): Promise<any> {\r\n return this.apiGet('/api/v1/sms/received');\r\n }\r\n\r\n // ─── Devices & Account ─────────────────────────────\r\n\r\n /**\r\n * List paired devices.\r\n */\r\n async getDevices(): Promise<any> {\r\n return this.apiGet('/api/v1/devices');\r\n }\r\n\r\n /**\r\n * Get account usage stats.\r\n */\r\n async getUsage(): Promise<any> {\r\n return this.apiGet('/api/v1/account/usage');\r\n }\r\n\r\n // ─── E2E Encryption ──────────────────────────────\r\n\r\n /**\r\n * Get E2E encryption status for a device.\r\n */\r\n async getDeviceE2EStatus(deviceId: string): Promise<{ success: boolean; data?: DeviceE2EStatus; error?: string }> {\r\n return this.apiGet(`/api/v1/devices/${deviceId}/e2e-status`);\r\n }\r\n\r\n /**\r\n * Get device public key for E2E encryption.\r\n */\r\n async getDevicePublicKey(deviceId: string): Promise<{ success: boolean; data?: DevicePublicKeyInfo; error?: string }> {\r\n return this.apiGet(`/api/v1/devices/${deviceId}/public-key`);\r\n }\r\n\r\n /**\r\n * Verify that a stored fingerprint matches the current device key.\r\n */\r\n async verifyDeviceKey(deviceId: string, storedFingerprint: string): Promise<VerifyDeviceKeyResult> {\r\n const res = await this.getDeviceE2EStatus(deviceId);\r\n if (!res.success || !res.data) {\r\n return { valid: false, needsRePairing: true };\r\n }\r\n const status = res.data;\r\n if (!status.hasPublicKey || !status.publicKeyFingerprint) {\r\n return { valid: false, needsRePairing: true };\r\n }\r\n const matches = status.publicKeyFingerprint === storedFingerprint;\r\n return {\r\n valid: matches,\r\n currentFingerprint: status.publicKeyFingerprint,\r\n needsRePairing: !matches,\r\n };\r\n }\r\n\r\n /**\r\n * Send encrypted SMS via SMSTunnel API.\r\n * The payload is encrypted client-side with the device's RSA public key.\r\n */\r\n async sendEncryptedSms(\r\n encryptedPayload: string,\r\n deviceId: string,\r\n is2FA: boolean = false,\r\n ): Promise<SendSmsResult> {\r\n return this.apiPost('/api/v1/sms/send', {\r\n encrypted: true,\r\n encryptedPayload,\r\n deviceId,\r\n is2FA,\r\n });\r\n }\r\n\r\n /**\r\n * List pairings for the authenticated user.\r\n */\r\n async getPairings(): Promise<any> {\r\n return this.apiGet('/api/v1/pairings');\r\n }\r\n\r\n // ─── Internal helpers ──────────────────────────────\r\n\r\n /** Make an authenticated GET request to SMSTunnel */\r\n private async apiGet(path: string): Promise<any> {\r\n const config = await this.options.storage.getConfig();\r\n if (!config.apiKey || !config.serverUrl) {\r\n return { success: false, error: 'SMSTunnel is not configured.' };\r\n }\r\n const serverUrl = config.serverUrl.replace(/\\/$/, '');\r\n try {\r\n const res = await fetch(`${serverUrl}${path}`, {\r\n headers: { 'X-API-Key': config.apiKey },\r\n });\r\n return await res.json();\r\n } catch (err: any) {\r\n return { success: false, error: err.message };\r\n }\r\n }\r\n\r\n /** Make an authenticated POST request to SMSTunnel */\r\n private async apiPost(path: string, body: any): Promise<any> {\r\n const config = await this.options.storage.getConfig();\r\n if (!config.apiKey || !config.serverUrl) {\r\n return { success: false, error: 'SMSTunnel is not configured.' };\r\n }\r\n const serverUrl = config.serverUrl.replace(/\\/$/, '');\r\n try {\r\n const res = await fetch(`${serverUrl}${path}`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'X-API-Key': config.apiKey,\r\n },\r\n body: JSON.stringify(body),\r\n });\r\n return await res.json();\r\n } catch (err: any) {\r\n return { success: false, error: err.message };\r\n }\r\n }\r\n}\r\n","export const SMSTUNNEL_OPTIONS = 'SMSTUNNEL_OPTIONS';\r\n\r\nexport const SMSTUNNEL_DEFAULT_PREFIX = 'smstunnel';\r\n\r\n/**\r\n * Public paths that should be excluded from auth guards.\r\n * Use with your global guard to skip auth on callback/polling routes.\r\n *\r\n * Example (NestJS):\r\n * if (SMSTUNNEL_PUBLIC_PATHS.some(p => request.url.includes(p))) return true;\r\n */\r\nexport const SMSTUNNEL_PUBLIC_PATHS = [\r\n '/smstunnel/pairing-status/',\r\n '/smstunnel/callback',\r\n '/wp-json/smstunnel/v1/setup-callback',\r\n];\r\n\r\n/**\r\n * Returns public paths with a custom prefix.\r\n */\r\nexport function getSmsTunnelPublicPaths(prefix: string): string[] {\r\n return [\r\n `/${prefix}/pairing-status/`,\r\n `/${prefix}/callback`,\r\n '/wp-json/smstunnel/v1/setup-callback',\r\n ];\r\n}\r\n","import {\r\n Controller,\r\n Post,\r\n Get,\r\n Body,\r\n Param,\r\n Inject,\r\n Logger,\r\n} from '@nestjs/common';\r\nimport { SmsTunnelService } from './smstunnel.service';\r\nimport { SMSTUNNEL_OPTIONS } from './smstunnel.constants';\r\nimport type {\r\n SmsTunnelModuleOptions,\r\n PairingCallbackBody,\r\n LegacyCallbackBody,\r\n} from './smstunnel.interfaces';\r\n\r\n/**\r\n * Decorator to mark routes as public (no auth).\r\n * If you have your own @Public() decorator, the module re-exports\r\n * SMSTUNNEL_PUBLIC_PATHS so you can exclude them in your guard.\r\n */\r\nfunction SmsTunnelPublic() {\r\n // We use Reflect metadata so consumers can detect public routes\r\n return (target: any, key: string, descriptor: PropertyDescriptor) => {\r\n Reflect.defineMetadata('isPublic', true, descriptor.value);\r\n return descriptor;\r\n };\r\n}\r\n\r\n@Controller()\r\nexport class SmsTunnelController {\r\n private readonly logger = new Logger(SmsTunnelController.name);\r\n private readonly prefix: string;\r\n\r\n constructor(\r\n private readonly smsTunnelService: SmsTunnelService,\r\n @Inject(SMSTUNNEL_OPTIONS)\r\n private readonly options: SmsTunnelModuleOptions,\r\n ) {\r\n this.prefix = 'smstunnel';\r\n }\r\n\r\n /**\r\n * Get current pairing status.\r\n * Route: GET /{prefix}/status\r\n */\r\n @Get('smstunnel/status')\r\n async getStatus() {\r\n return this.smsTunnelService.getStatus();\r\n }\r\n\r\n /**\r\n * Create a pairing token on SMSTunnel.\r\n * Route: POST /{prefix}/create-token\r\n */\r\n @Post('smstunnel/create-token')\r\n async createToken() {\r\n return this.smsTunnelService.createPairingToken();\r\n }\r\n\r\n /**\r\n * Proxy polling to SMSTunnel (avoids CORS issues).\r\n * This should be public (no auth) - just proxying SMSTunnel's endpoint.\r\n * Route: GET /{prefix}/pairing-status/:token\r\n */\r\n @SmsTunnelPublic()\r\n @Get('smstunnel/pairing-status/:token')\r\n async pairingStatus(@Param('token') token: string) {\r\n return this.smsTunnelService.getPairingStatus(token);\r\n }\r\n\r\n /**\r\n * Callback from SMSTunnel after pairing completes.\r\n * Route: POST /{prefix}/callback\r\n */\r\n @SmsTunnelPublic()\r\n @Post('smstunnel/callback')\r\n async pairingCallback(@Body() body: PairingCallbackBody) {\r\n this.logger.log(\r\n `SMSTunnel callback: event=${body.event}, token=${body.token?.substring(0, 8)}...`,\r\n );\r\n const success = await this.smsTunnelService.handlePairingCallback(body);\r\n return { success };\r\n }\r\n\r\n /**\r\n * WordPress-compatible legacy callback.\r\n * Route: POST /wp-json/smstunnel/v1/setup-callback\r\n */\r\n @SmsTunnelPublic()\r\n @Post('wp-json/smstunnel/v1/setup-callback')\r\n async setupCallbackWp(@Body() body: LegacyCallbackBody) {\r\n this.logger.log(`SMSTunnel legacy callback. Status: ${body.status || 'unknown'}`);\r\n const success = await this.smsTunnelService.handleLegacyCallback(\r\n body.site_token,\r\n body.api_key,\r\n body.device_name,\r\n );\r\n return { success };\r\n }\r\n\r\n /**\r\n * Unpair the connected device.\r\n * Route: POST /{prefix}/unpair\r\n */\r\n @Post('smstunnel/unpair')\r\n async unpair() {\r\n await this.smsTunnelService.unpair();\r\n return { success: true };\r\n }\r\n\r\n /**\r\n * Send an SMS via SMSTunnel.\r\n * Route: POST /{prefix}/send\r\n */\r\n @Post('smstunnel/send')\r\n async sendSms(@Body() body: { to: string; message: string }) {\r\n return this.smsTunnelService.sendSms(body.to, body.message);\r\n }\r\n\r\n /**\r\n * Update server URL configuration.\r\n * Route: POST /{prefix}/update-config\r\n */\r\n @Post('smstunnel/update-config')\r\n async updateConfig(@Body() body: { serverUrl: string }) {\r\n await this.smsTunnelService.updateServerUrl(body.serverUrl);\r\n return { success: true };\r\n }\r\n\r\n // ─── Extended SMS endpoints ────────────────────────\r\n\r\n /**\r\n * Send a 2FA SMS.\r\n * Route: POST /{prefix}/send-2fa\r\n */\r\n @Post('smstunnel/send-2fa')\r\n async send2fa(@Body() body: { to: string; code: string; template?: string }) {\r\n return this.smsTunnelService.send2fa(body.to, body.code, body.template);\r\n }\r\n\r\n /**\r\n * Send bulk SMS.\r\n * Route: POST /{prefix}/send-bulk\r\n */\r\n @Post('smstunnel/send-bulk')\r\n async sendBulk(@Body() body: { messages: Array<{ to: string; message: string }> }) {\r\n return this.smsTunnelService.sendBulk(body.messages);\r\n }\r\n\r\n /**\r\n * Get SMS delivery status.\r\n * Route: GET /{prefix}/sms-status/:messageId\r\n */\r\n @Get('smstunnel/sms-status/:messageId')\r\n async smsStatus(@Param('messageId') messageId: string) {\r\n return this.smsTunnelService.getSmsStatus(messageId);\r\n }\r\n\r\n /**\r\n * Get received SMS (inbox).\r\n * Route: GET /{prefix}/received\r\n */\r\n @Get('smstunnel/received')\r\n async receivedSms() {\r\n return this.smsTunnelService.getReceivedSms();\r\n }\r\n\r\n // ─── E2E Encryption ────────────────────────────────\r\n\r\n /**\r\n * Get E2E encryption status for a device.\r\n * Route: GET /{prefix}/e2e-status/:deviceId\r\n */\r\n @Get('smstunnel/e2e-status/:deviceId')\r\n async e2eStatus(@Param('deviceId') deviceId: string) {\r\n return this.smsTunnelService.getDeviceE2EStatus(deviceId);\r\n }\r\n\r\n /**\r\n * Get device public key for E2E encryption.\r\n * Route: GET /{prefix}/public-key/:deviceId\r\n */\r\n @Get('smstunnel/public-key/:deviceId')\r\n async publicKey(@Param('deviceId') deviceId: string) {\r\n return this.smsTunnelService.getDevicePublicKey(deviceId);\r\n }\r\n\r\n /**\r\n * Verify device key fingerprint.\r\n * Route: POST /{prefix}/verify-key\r\n */\r\n @Post('smstunnel/verify-key')\r\n async verifyKey(@Body() body: { deviceId: string; fingerprint: string }) {\r\n return this.smsTunnelService.verifyDeviceKey(body.deviceId, body.fingerprint);\r\n }\r\n\r\n /**\r\n * Send encrypted SMS.\r\n * Route: POST /{prefix}/send-encrypted\r\n */\r\n @Post('smstunnel/send-encrypted')\r\n async sendEncrypted(\r\n @Body() body: { encryptedPayload: string; deviceId: string; is2FA?: boolean },\r\n ) {\r\n return this.smsTunnelService.sendEncryptedSms(\r\n body.encryptedPayload,\r\n body.deviceId,\r\n body.is2FA,\r\n );\r\n }\r\n\r\n /**\r\n * List pairings.\r\n * Route: GET /{prefix}/pairings\r\n */\r\n @Get('smstunnel/pairings')\r\n async pairings() {\r\n return this.smsTunnelService.getPairings();\r\n }\r\n\r\n // ─── Devices & Account ─────────────────────────────\r\n\r\n /**\r\n * List paired devices.\r\n * Route: GET /{prefix}/devices\r\n */\r\n @Get('smstunnel/devices')\r\n async devices() {\r\n return this.smsTunnelService.getDevices();\r\n }\r\n\r\n /**\r\n * Get account usage stats.\r\n * Route: GET /{prefix}/usage\r\n */\r\n @Get('smstunnel/usage')\r\n async usage() {\r\n return this.smsTunnelService.getUsage();\r\n }\r\n}\r\n"],"mappings":"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","names":["import_common","import_common"]}
|
package/dist/server/index.mjs
CHANGED
|
@@ -69,7 +69,7 @@ var SmsTunnelService = class {
|
|
|
69
69
|
* Public flow: POST /api/v1/pairing/public-create
|
|
70
70
|
*/
|
|
71
71
|
async createPairingTokenPublic(serverUrl) {
|
|
72
|
-
const prefix =
|
|
72
|
+
const prefix = "smstunnel";
|
|
73
73
|
const callbackUrl = `${this.options.callbackBaseUrl.replace(/\/$/, "")}/${prefix}/callback`;
|
|
74
74
|
try {
|
|
75
75
|
const res = await fetch(`${serverUrl}/api/v1/pairing/public-create`, {
|
|
@@ -107,7 +107,7 @@ var SmsTunnelService = class {
|
|
|
107
107
|
* Creates sub-accounts under the Enterprise account.
|
|
108
108
|
*/
|
|
109
109
|
async createPairingTokenEnterprise(serverUrl) {
|
|
110
|
-
const prefix =
|
|
110
|
+
const prefix = "smstunnel";
|
|
111
111
|
const callbackUrl = `${this.options.callbackBaseUrl.replace(/\/$/, "")}/${prefix}/callback`;
|
|
112
112
|
try {
|
|
113
113
|
const res = await fetch(`${serverUrl}/api/v1/saas/activate`, {
|
|
@@ -418,7 +418,7 @@ var SmsTunnelController = class {
|
|
|
418
418
|
this.smsTunnelService = smsTunnelService;
|
|
419
419
|
this.options = options;
|
|
420
420
|
this.logger = new Logger2(SmsTunnelController.name);
|
|
421
|
-
this.prefix =
|
|
421
|
+
this.prefix = "smstunnel";
|
|
422
422
|
}
|
|
423
423
|
async getStatus() {
|
|
424
424
|
return this.smsTunnelService.getStatus();
|