@narcisbodea/smstunnel-sdk 1.0.0

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.
@@ -0,0 +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}>&#10003;</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 &#10005;\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' }}>&#128241;</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 return {\r\n ...state,\r\n generateQr,\r\n cancelPairing,\r\n unpair,\r\n sendTestSms,\r\n updateServerUrl,\r\n refetch,\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"]}
@@ -0,0 +1,262 @@
1
+ import { DynamicModule } from '@nestjs/common';
2
+
3
+ interface SmsTunnelConfig {
4
+ serverUrl: string;
5
+ apiKey: string;
6
+ siteToken: string;
7
+ deviceName: string;
8
+ }
9
+ interface SmsTunnelStorageAdapter {
10
+ getConfig(): Promise<Partial<SmsTunnelConfig>>;
11
+ updateConfig(partial: Partial<SmsTunnelConfig>): Promise<void>;
12
+ clearConfig(): Promise<void>;
13
+ }
14
+ interface SmsTunnelModuleOptions {
15
+ storage: SmsTunnelStorageAdapter;
16
+ callbackBaseUrl: string;
17
+ displayName?: string;
18
+ displayUrl?: string;
19
+ routePrefix?: string;
20
+ enableLegacyCallback?: boolean;
21
+ }
22
+ interface SmsTunnelModuleAsyncOptions {
23
+ useFactory: (...args: any[]) => Promise<SmsTunnelModuleOptions> | SmsTunnelModuleOptions;
24
+ inject?: any[];
25
+ }
26
+ interface SmsTunnelStatus {
27
+ paired: boolean;
28
+ serverUrl?: string;
29
+ deviceName?: string;
30
+ }
31
+ interface SendSmsResult {
32
+ success: boolean;
33
+ messageId?: string;
34
+ error?: string;
35
+ }
36
+ interface CreateTokenResult {
37
+ success: boolean;
38
+ token?: string;
39
+ qrData?: string;
40
+ expiresAt?: string;
41
+ pollUrl?: string;
42
+ error?: string;
43
+ }
44
+ interface PairingStatusResult {
45
+ status: string;
46
+ displayName?: string;
47
+ error?: string;
48
+ }
49
+ interface PairingCallbackBody {
50
+ event: string;
51
+ token: string;
52
+ apiKey: string;
53
+ deviceId?: string;
54
+ deviceName?: string;
55
+ userId?: string;
56
+ }
57
+ interface LegacyCallbackBody {
58
+ site_token: string;
59
+ api_key: string;
60
+ device_name?: string;
61
+ status?: string;
62
+ }
63
+
64
+ declare class SmsTunnelModule {
65
+ static forRoot(options: SmsTunnelModuleOptions): DynamicModule;
66
+ static forRootAsync(asyncOptions: SmsTunnelModuleAsyncOptions): DynamicModule;
67
+ }
68
+
69
+ declare class SmsTunnelService {
70
+ private readonly options;
71
+ private readonly logger;
72
+ constructor(options: SmsTunnelModuleOptions);
73
+ /**
74
+ * Get current pairing status.
75
+ */
76
+ getStatus(): Promise<SmsTunnelStatus>;
77
+ /**
78
+ * Create a pairing token on SMSTunnel server.
79
+ */
80
+ createPairingToken(): Promise<CreateTokenResult>;
81
+ /**
82
+ * Check pairing status by polling SMSTunnel.
83
+ */
84
+ getPairingStatus(token: string): Promise<PairingStatusResult>;
85
+ /**
86
+ * Handle callback from SMSTunnel after pairing completes.
87
+ */
88
+ handlePairingCallback(body: PairingCallbackBody): Promise<boolean>;
89
+ /**
90
+ * Handle legacy WordPress-compatible callback.
91
+ */
92
+ handleLegacyCallback(siteToken: string, apiKey: string, deviceName?: string): Promise<boolean>;
93
+ /**
94
+ * Send SMS via SMSTunnel API.
95
+ */
96
+ sendSms(to: string, message: string): Promise<SendSmsResult>;
97
+ /**
98
+ * Unpair - clear saved API key and device info.
99
+ */
100
+ unpair(): Promise<void>;
101
+ /**
102
+ * Update server URL in config.
103
+ */
104
+ updateServerUrl(serverUrl: string): Promise<void>;
105
+ /**
106
+ * Send 2FA SMS via SMSTunnel API.
107
+ */
108
+ send2fa(to: string, code: string, template?: string): Promise<SendSmsResult>;
109
+ /**
110
+ * Send bulk SMS via SMSTunnel API.
111
+ */
112
+ sendBulk(messages: Array<{
113
+ to: string;
114
+ message: string;
115
+ }>): Promise<{
116
+ success: boolean;
117
+ results?: any[];
118
+ error?: string;
119
+ }>;
120
+ /**
121
+ * Get SMS delivery status.
122
+ */
123
+ getSmsStatus(messageId: string): Promise<any>;
124
+ /**
125
+ * Get received SMS (inbox).
126
+ */
127
+ getReceivedSms(): Promise<any>;
128
+ /**
129
+ * List paired devices.
130
+ */
131
+ getDevices(): Promise<any>;
132
+ /**
133
+ * Get account usage stats.
134
+ */
135
+ getUsage(): Promise<any>;
136
+ /** Make an authenticated GET request to SMSTunnel */
137
+ private apiGet;
138
+ /** Make an authenticated POST request to SMSTunnel */
139
+ private apiPost;
140
+ }
141
+
142
+ declare class SmsTunnelController {
143
+ private readonly smsTunnelService;
144
+ private readonly options;
145
+ private readonly logger;
146
+ private readonly prefix;
147
+ constructor(smsTunnelService: SmsTunnelService, options: SmsTunnelModuleOptions);
148
+ /**
149
+ * Get current pairing status.
150
+ * Route: GET /{prefix}/status
151
+ */
152
+ getStatus(): Promise<SmsTunnelStatus>;
153
+ /**
154
+ * Create a pairing token on SMSTunnel.
155
+ * Route: POST /{prefix}/create-token
156
+ */
157
+ createToken(): Promise<CreateTokenResult>;
158
+ /**
159
+ * Proxy polling to SMSTunnel (avoids CORS issues).
160
+ * This should be public (no auth) - just proxying SMSTunnel's endpoint.
161
+ * Route: GET /{prefix}/pairing-status/:token
162
+ */
163
+ pairingStatus(token: string): Promise<PairingStatusResult>;
164
+ /**
165
+ * Callback from SMSTunnel after pairing completes.
166
+ * Route: POST /{prefix}/callback
167
+ */
168
+ pairingCallback(body: PairingCallbackBody): Promise<{
169
+ success: boolean;
170
+ }>;
171
+ /**
172
+ * WordPress-compatible legacy callback.
173
+ * Route: POST /wp-json/smstunnel/v1/setup-callback
174
+ */
175
+ setupCallbackWp(body: LegacyCallbackBody): Promise<{
176
+ success: boolean;
177
+ }>;
178
+ /**
179
+ * Unpair the connected device.
180
+ * Route: POST /{prefix}/unpair
181
+ */
182
+ unpair(): Promise<{
183
+ success: boolean;
184
+ }>;
185
+ /**
186
+ * Send an SMS via SMSTunnel.
187
+ * Route: POST /{prefix}/send
188
+ */
189
+ sendSms(body: {
190
+ to: string;
191
+ message: string;
192
+ }): Promise<SendSmsResult>;
193
+ /**
194
+ * Update server URL configuration.
195
+ * Route: POST /{prefix}/update-config
196
+ */
197
+ updateConfig(body: {
198
+ serverUrl: string;
199
+ }): Promise<{
200
+ success: boolean;
201
+ }>;
202
+ /**
203
+ * Send a 2FA SMS.
204
+ * Route: POST /{prefix}/send-2fa
205
+ */
206
+ send2fa(body: {
207
+ to: string;
208
+ code: string;
209
+ template?: string;
210
+ }): Promise<SendSmsResult>;
211
+ /**
212
+ * Send bulk SMS.
213
+ * Route: POST /{prefix}/send-bulk
214
+ */
215
+ sendBulk(body: {
216
+ messages: Array<{
217
+ to: string;
218
+ message: string;
219
+ }>;
220
+ }): Promise<{
221
+ success: boolean;
222
+ results?: any[];
223
+ error?: string;
224
+ }>;
225
+ /**
226
+ * Get SMS delivery status.
227
+ * Route: GET /{prefix}/sms-status/:messageId
228
+ */
229
+ smsStatus(messageId: string): Promise<any>;
230
+ /**
231
+ * Get received SMS (inbox).
232
+ * Route: GET /{prefix}/received
233
+ */
234
+ receivedSms(): Promise<any>;
235
+ /**
236
+ * List paired devices.
237
+ * Route: GET /{prefix}/devices
238
+ */
239
+ devices(): Promise<any>;
240
+ /**
241
+ * Get account usage stats.
242
+ * Route: GET /{prefix}/usage
243
+ */
244
+ usage(): Promise<any>;
245
+ }
246
+
247
+ declare const SMSTUNNEL_OPTIONS = "SMSTUNNEL_OPTIONS";
248
+ declare const SMSTUNNEL_DEFAULT_PREFIX = "smstunnel";
249
+ /**
250
+ * Public paths that should be excluded from auth guards.
251
+ * Use with your global guard to skip auth on callback/polling routes.
252
+ *
253
+ * Example (NestJS):
254
+ * if (SMSTUNNEL_PUBLIC_PATHS.some(p => request.url.includes(p))) return true;
255
+ */
256
+ declare const SMSTUNNEL_PUBLIC_PATHS: string[];
257
+ /**
258
+ * Returns public paths with a custom prefix.
259
+ */
260
+ declare function getSmsTunnelPublicPaths(prefix: string): string[];
261
+
262
+ export { type CreateTokenResult, type LegacyCallbackBody, type PairingCallbackBody, type PairingStatusResult, SMSTUNNEL_DEFAULT_PREFIX, SMSTUNNEL_OPTIONS, SMSTUNNEL_PUBLIC_PATHS, type SendSmsResult, type SmsTunnelConfig, SmsTunnelController, SmsTunnelModule, type SmsTunnelModuleAsyncOptions, type SmsTunnelModuleOptions, SmsTunnelService, type SmsTunnelStatus, type SmsTunnelStorageAdapter, getSmsTunnelPublicPaths };
@@ -0,0 +1,262 @@
1
+ import { DynamicModule } from '@nestjs/common';
2
+
3
+ interface SmsTunnelConfig {
4
+ serverUrl: string;
5
+ apiKey: string;
6
+ siteToken: string;
7
+ deviceName: string;
8
+ }
9
+ interface SmsTunnelStorageAdapter {
10
+ getConfig(): Promise<Partial<SmsTunnelConfig>>;
11
+ updateConfig(partial: Partial<SmsTunnelConfig>): Promise<void>;
12
+ clearConfig(): Promise<void>;
13
+ }
14
+ interface SmsTunnelModuleOptions {
15
+ storage: SmsTunnelStorageAdapter;
16
+ callbackBaseUrl: string;
17
+ displayName?: string;
18
+ displayUrl?: string;
19
+ routePrefix?: string;
20
+ enableLegacyCallback?: boolean;
21
+ }
22
+ interface SmsTunnelModuleAsyncOptions {
23
+ useFactory: (...args: any[]) => Promise<SmsTunnelModuleOptions> | SmsTunnelModuleOptions;
24
+ inject?: any[];
25
+ }
26
+ interface SmsTunnelStatus {
27
+ paired: boolean;
28
+ serverUrl?: string;
29
+ deviceName?: string;
30
+ }
31
+ interface SendSmsResult {
32
+ success: boolean;
33
+ messageId?: string;
34
+ error?: string;
35
+ }
36
+ interface CreateTokenResult {
37
+ success: boolean;
38
+ token?: string;
39
+ qrData?: string;
40
+ expiresAt?: string;
41
+ pollUrl?: string;
42
+ error?: string;
43
+ }
44
+ interface PairingStatusResult {
45
+ status: string;
46
+ displayName?: string;
47
+ error?: string;
48
+ }
49
+ interface PairingCallbackBody {
50
+ event: string;
51
+ token: string;
52
+ apiKey: string;
53
+ deviceId?: string;
54
+ deviceName?: string;
55
+ userId?: string;
56
+ }
57
+ interface LegacyCallbackBody {
58
+ site_token: string;
59
+ api_key: string;
60
+ device_name?: string;
61
+ status?: string;
62
+ }
63
+
64
+ declare class SmsTunnelModule {
65
+ static forRoot(options: SmsTunnelModuleOptions): DynamicModule;
66
+ static forRootAsync(asyncOptions: SmsTunnelModuleAsyncOptions): DynamicModule;
67
+ }
68
+
69
+ declare class SmsTunnelService {
70
+ private readonly options;
71
+ private readonly logger;
72
+ constructor(options: SmsTunnelModuleOptions);
73
+ /**
74
+ * Get current pairing status.
75
+ */
76
+ getStatus(): Promise<SmsTunnelStatus>;
77
+ /**
78
+ * Create a pairing token on SMSTunnel server.
79
+ */
80
+ createPairingToken(): Promise<CreateTokenResult>;
81
+ /**
82
+ * Check pairing status by polling SMSTunnel.
83
+ */
84
+ getPairingStatus(token: string): Promise<PairingStatusResult>;
85
+ /**
86
+ * Handle callback from SMSTunnel after pairing completes.
87
+ */
88
+ handlePairingCallback(body: PairingCallbackBody): Promise<boolean>;
89
+ /**
90
+ * Handle legacy WordPress-compatible callback.
91
+ */
92
+ handleLegacyCallback(siteToken: string, apiKey: string, deviceName?: string): Promise<boolean>;
93
+ /**
94
+ * Send SMS via SMSTunnel API.
95
+ */
96
+ sendSms(to: string, message: string): Promise<SendSmsResult>;
97
+ /**
98
+ * Unpair - clear saved API key and device info.
99
+ */
100
+ unpair(): Promise<void>;
101
+ /**
102
+ * Update server URL in config.
103
+ */
104
+ updateServerUrl(serverUrl: string): Promise<void>;
105
+ /**
106
+ * Send 2FA SMS via SMSTunnel API.
107
+ */
108
+ send2fa(to: string, code: string, template?: string): Promise<SendSmsResult>;
109
+ /**
110
+ * Send bulk SMS via SMSTunnel API.
111
+ */
112
+ sendBulk(messages: Array<{
113
+ to: string;
114
+ message: string;
115
+ }>): Promise<{
116
+ success: boolean;
117
+ results?: any[];
118
+ error?: string;
119
+ }>;
120
+ /**
121
+ * Get SMS delivery status.
122
+ */
123
+ getSmsStatus(messageId: string): Promise<any>;
124
+ /**
125
+ * Get received SMS (inbox).
126
+ */
127
+ getReceivedSms(): Promise<any>;
128
+ /**
129
+ * List paired devices.
130
+ */
131
+ getDevices(): Promise<any>;
132
+ /**
133
+ * Get account usage stats.
134
+ */
135
+ getUsage(): Promise<any>;
136
+ /** Make an authenticated GET request to SMSTunnel */
137
+ private apiGet;
138
+ /** Make an authenticated POST request to SMSTunnel */
139
+ private apiPost;
140
+ }
141
+
142
+ declare class SmsTunnelController {
143
+ private readonly smsTunnelService;
144
+ private readonly options;
145
+ private readonly logger;
146
+ private readonly prefix;
147
+ constructor(smsTunnelService: SmsTunnelService, options: SmsTunnelModuleOptions);
148
+ /**
149
+ * Get current pairing status.
150
+ * Route: GET /{prefix}/status
151
+ */
152
+ getStatus(): Promise<SmsTunnelStatus>;
153
+ /**
154
+ * Create a pairing token on SMSTunnel.
155
+ * Route: POST /{prefix}/create-token
156
+ */
157
+ createToken(): Promise<CreateTokenResult>;
158
+ /**
159
+ * Proxy polling to SMSTunnel (avoids CORS issues).
160
+ * This should be public (no auth) - just proxying SMSTunnel's endpoint.
161
+ * Route: GET /{prefix}/pairing-status/:token
162
+ */
163
+ pairingStatus(token: string): Promise<PairingStatusResult>;
164
+ /**
165
+ * Callback from SMSTunnel after pairing completes.
166
+ * Route: POST /{prefix}/callback
167
+ */
168
+ pairingCallback(body: PairingCallbackBody): Promise<{
169
+ success: boolean;
170
+ }>;
171
+ /**
172
+ * WordPress-compatible legacy callback.
173
+ * Route: POST /wp-json/smstunnel/v1/setup-callback
174
+ */
175
+ setupCallbackWp(body: LegacyCallbackBody): Promise<{
176
+ success: boolean;
177
+ }>;
178
+ /**
179
+ * Unpair the connected device.
180
+ * Route: POST /{prefix}/unpair
181
+ */
182
+ unpair(): Promise<{
183
+ success: boolean;
184
+ }>;
185
+ /**
186
+ * Send an SMS via SMSTunnel.
187
+ * Route: POST /{prefix}/send
188
+ */
189
+ sendSms(body: {
190
+ to: string;
191
+ message: string;
192
+ }): Promise<SendSmsResult>;
193
+ /**
194
+ * Update server URL configuration.
195
+ * Route: POST /{prefix}/update-config
196
+ */
197
+ updateConfig(body: {
198
+ serverUrl: string;
199
+ }): Promise<{
200
+ success: boolean;
201
+ }>;
202
+ /**
203
+ * Send a 2FA SMS.
204
+ * Route: POST /{prefix}/send-2fa
205
+ */
206
+ send2fa(body: {
207
+ to: string;
208
+ code: string;
209
+ template?: string;
210
+ }): Promise<SendSmsResult>;
211
+ /**
212
+ * Send bulk SMS.
213
+ * Route: POST /{prefix}/send-bulk
214
+ */
215
+ sendBulk(body: {
216
+ messages: Array<{
217
+ to: string;
218
+ message: string;
219
+ }>;
220
+ }): Promise<{
221
+ success: boolean;
222
+ results?: any[];
223
+ error?: string;
224
+ }>;
225
+ /**
226
+ * Get SMS delivery status.
227
+ * Route: GET /{prefix}/sms-status/:messageId
228
+ */
229
+ smsStatus(messageId: string): Promise<any>;
230
+ /**
231
+ * Get received SMS (inbox).
232
+ * Route: GET /{prefix}/received
233
+ */
234
+ receivedSms(): Promise<any>;
235
+ /**
236
+ * List paired devices.
237
+ * Route: GET /{prefix}/devices
238
+ */
239
+ devices(): Promise<any>;
240
+ /**
241
+ * Get account usage stats.
242
+ * Route: GET /{prefix}/usage
243
+ */
244
+ usage(): Promise<any>;
245
+ }
246
+
247
+ declare const SMSTUNNEL_OPTIONS = "SMSTUNNEL_OPTIONS";
248
+ declare const SMSTUNNEL_DEFAULT_PREFIX = "smstunnel";
249
+ /**
250
+ * Public paths that should be excluded from auth guards.
251
+ * Use with your global guard to skip auth on callback/polling routes.
252
+ *
253
+ * Example (NestJS):
254
+ * if (SMSTUNNEL_PUBLIC_PATHS.some(p => request.url.includes(p))) return true;
255
+ */
256
+ declare const SMSTUNNEL_PUBLIC_PATHS: string[];
257
+ /**
258
+ * Returns public paths with a custom prefix.
259
+ */
260
+ declare function getSmsTunnelPublicPaths(prefix: string): string[];
261
+
262
+ export { type CreateTokenResult, type LegacyCallbackBody, type PairingCallbackBody, type PairingStatusResult, SMSTUNNEL_DEFAULT_PREFIX, SMSTUNNEL_OPTIONS, SMSTUNNEL_PUBLIC_PATHS, type SendSmsResult, type SmsTunnelConfig, SmsTunnelController, SmsTunnelModule, type SmsTunnelModuleAsyncOptions, type SmsTunnelModuleOptions, SmsTunnelService, type SmsTunnelStatus, type SmsTunnelStorageAdapter, getSmsTunnelPublicPaths };