@narcisbodea/smstunnel-sdk 1.1.0 → 1.1.2
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 +144 -0
- package/dist/angular/index.d.ts +144 -0
- package/dist/angular/index.js +166 -0
- package/dist/angular/index.js.map +1 -0
- package/dist/angular/index.mjs +140 -0
- package/dist/angular/index.mjs.map +1 -0
- package/dist/index.d.mts +33 -1
- package/dist/index.d.ts +33 -1
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +41 -0
- package/dist/index.mjs.map +1 -1
- package/dist/node/index.d.mts +228 -0
- package/dist/node/index.d.ts +228 -0
- package/dist/node/index.js +208 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/index.mjs +183 -0
- package/dist/node/index.mjs.map +1 -0
- package/dist/react/index.d.mts +4 -0
- package/dist/react/index.d.ts +4 -0
- package/dist/react/index.js +64 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +64 -1
- package/dist/react/index.mjs.map +1 -1
- package/dist/server/index.d.mts +86 -1
- package/dist/server/index.d.ts +86 -1
- package/dist/server/index.js +88 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +88 -0
- package/dist/server/index.mjs.map +1 -1
- package/dist/vue/index.d.mts +4 -0
- package/dist/vue/index.d.ts +4 -0
- package/dist/vue/index.js +52 -1
- package/dist/vue/index.js.map +1 -1
- package/dist/vue/index.mjs +52 -1
- package/dist/vue/index.mjs.map +1 -1
- package/package.json +19 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/server/smstunnel.module.ts","../../src/server/smstunnel.service.ts","../../src/server/smstunnel.constants.ts","../../src/server/smstunnel.controller.ts"],"sourcesContent":["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} 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 // ─── 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 // ─── 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,SAAwB,cAAwB;;;ACAhD,SAAS,QAAQ,YAAY,cAAc;;;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;;;ADbO,IAAM,mBAAN,MAAuB;AAAA,EAG5B,YAEmB,SACjB;AADiB;AAJnB,SAAiB,SAAS,IAAI,OAAO,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,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;AArXa,mBAAN;AAAA,EADN,WAAW;AAAA,EAKP,0BAAO,iBAAiB;AAAA,GAJhB;;;AEbb;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAAA;AAAA,EACA,UAAAC;AAAA,OACK;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,IAAIC,QAAO,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,UAAU;AACd,WAAO,KAAK,iBAAiB,WAAW;AAAA,EAC1C;AAAA,EAOA,MAAM,QAAQ;AACZ,WAAO,KAAK,iBAAiB,SAAS;AAAA,EACxC;AACF;AA5IQ;AAAA,EADL,IAAI,kBAAkB;AAAA,GAhBZ,oBAiBL;AASA;AAAA,EADL,KAAK,wBAAwB;AAAA,GAzBnB,oBA0BL;AAWA;AAAA,EAFL,gBAAgB;AAAA,EAChB,IAAI,iCAAiC;AAAA,EACjB,yBAAM,OAAO;AAAA,GArCvB,oBAqCL;AAUA;AAAA,EAFL,gBAAgB;AAAA,EAChB,KAAK,oBAAoB;AAAA,EACH,wBAAK;AAAA,GA/CjB,oBA+CL;AAcA;AAAA,EAFL,gBAAgB;AAAA,EAChB,KAAK,qCAAqC;AAAA,EACpB,wBAAK;AAAA,GA7DjB,oBA6DL;AAeA;AAAA,EADL,KAAK,kBAAkB;AAAA,GA3Eb,oBA4EL;AAUA;AAAA,EADL,KAAK,gBAAgB;AAAA,EACP,wBAAK;AAAA,GAtFT,oBAsFL;AASA;AAAA,EADL,KAAK,yBAAyB;AAAA,EACX,wBAAK;AAAA,GA/Fd,oBA+FL;AAYA;AAAA,EADL,KAAK,oBAAoB;AAAA,EACX,wBAAK;AAAA,GA3GT,oBA2GL;AASA;AAAA,EADL,KAAK,qBAAqB;AAAA,EACX,wBAAK;AAAA,GApHV,oBAoHL;AASA;AAAA,EADL,IAAI,iCAAiC;AAAA,EACrB,yBAAM,WAAW;AAAA,GA7HvB,oBA6HL;AASA;AAAA,EADL,IAAI,oBAAoB;AAAA,GArId,oBAsIL;AAWA;AAAA,EADL,IAAI,mBAAmB;AAAA,GAhJb,oBAiJL;AASA;AAAA,EADL,IAAI,iBAAiB;AAAA,GAzJX,oBA0JL;AA1JK,sBAAN;AAAA,EADN,WAAW;AAAA,EAOP,mBAAAC,QAAO,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,EADN,OAAO,CAAC,CAAC;AAAA,GACG;","names":["Inject","Logger","Logger","Inject"]}
|
|
1
|
+
{"version":3,"sources":["../../src/server/smstunnel.module.ts","../../src/server/smstunnel.service.ts","../../src/server/smstunnel.constants.ts","../../src/server/smstunnel.controller.ts"],"sourcesContent":["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":"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","names":["Inject","Logger","Logger","Inject"]}
|
package/dist/vue/index.d.mts
CHANGED
|
@@ -150,6 +150,10 @@ declare function useSmsTunnel(options: UseSmsTunnelOptions): {
|
|
|
150
150
|
sendTestSms: (to: string, message: string) => Promise<SendSmsResult>;
|
|
151
151
|
updateServerUrl: (url: string) => Promise<void>;
|
|
152
152
|
refetch: () => Promise<void>;
|
|
153
|
+
getDeviceE2EStatus: (deviceId: string) => Promise<any>;
|
|
154
|
+
getDevicePublicKey: (deviceId: string) => Promise<any>;
|
|
155
|
+
verifyDeviceKey: (deviceId: string, fingerprint: string) => Promise<any>;
|
|
156
|
+
sendEncryptedSms: (encryptedPayload: string, deviceId: string, is2FA?: boolean) => Promise<any>;
|
|
153
157
|
};
|
|
154
158
|
|
|
155
159
|
export { EN_LABELS, QrCodeCanvas, RO_LABELS, type SendSmsResult, type SmsTunnelLabels, SmsTunnelPairing, type UseSmsTunnelOptions, useSmsTunnel };
|
package/dist/vue/index.d.ts
CHANGED
|
@@ -150,6 +150,10 @@ declare function useSmsTunnel(options: UseSmsTunnelOptions): {
|
|
|
150
150
|
sendTestSms: (to: string, message: string) => Promise<SendSmsResult>;
|
|
151
151
|
updateServerUrl: (url: string) => Promise<void>;
|
|
152
152
|
refetch: () => Promise<void>;
|
|
153
|
+
getDeviceE2EStatus: (deviceId: string) => Promise<any>;
|
|
154
|
+
getDevicePublicKey: (deviceId: string) => Promise<any>;
|
|
155
|
+
verifyDeviceKey: (deviceId: string, fingerprint: string) => Promise<any>;
|
|
156
|
+
sendEncryptedSms: (encryptedPayload: string, deviceId: string, is2FA?: boolean) => Promise<any>;
|
|
153
157
|
};
|
|
154
158
|
|
|
155
159
|
export { EN_LABELS, QrCodeCanvas, RO_LABELS, type SendSmsResult, type SmsTunnelLabels, SmsTunnelPairing, type UseSmsTunnelOptions, useSmsTunnel };
|
package/dist/vue/index.js
CHANGED
|
@@ -178,6 +178,52 @@ function useSmsTunnel(options) {
|
|
|
178
178
|
error.value = `Update failed: ${err.message}`;
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
|
+
async function getDeviceE2EStatus(deviceId) {
|
|
182
|
+
try {
|
|
183
|
+
const res = await fetch(
|
|
184
|
+
`${baseUrl}/${routePrefix}/e2e-status/${deviceId}`,
|
|
185
|
+
{ headers: authHeaders() }
|
|
186
|
+
);
|
|
187
|
+
return await res.json();
|
|
188
|
+
} catch (err) {
|
|
189
|
+
return { success: false, error: err.message };
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
async function getDevicePublicKey(deviceId) {
|
|
193
|
+
try {
|
|
194
|
+
const res = await fetch(
|
|
195
|
+
`${baseUrl}/${routePrefix}/public-key/${deviceId}`,
|
|
196
|
+
{ headers: authHeaders() }
|
|
197
|
+
);
|
|
198
|
+
return await res.json();
|
|
199
|
+
} catch (err) {
|
|
200
|
+
return { success: false, error: err.message };
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async function verifyDeviceKey(deviceId, fingerprint) {
|
|
204
|
+
try {
|
|
205
|
+
const res = await fetch(`${baseUrl}/${routePrefix}/verify-key`, {
|
|
206
|
+
method: "POST",
|
|
207
|
+
headers: { "Content-Type": "application/json", ...authHeaders() },
|
|
208
|
+
body: JSON.stringify({ deviceId, fingerprint })
|
|
209
|
+
});
|
|
210
|
+
return await res.json();
|
|
211
|
+
} catch (err) {
|
|
212
|
+
return { valid: false, needsRePairing: true, error: err.message };
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
async function sendEncryptedSms(encryptedPayload, deviceId, is2FA = false) {
|
|
216
|
+
try {
|
|
217
|
+
const res = await fetch(`${baseUrl}/${routePrefix}/send-encrypted`, {
|
|
218
|
+
method: "POST",
|
|
219
|
+
headers: { "Content-Type": "application/json", ...authHeaders() },
|
|
220
|
+
body: JSON.stringify({ encryptedPayload, deviceId, is2FA })
|
|
221
|
+
});
|
|
222
|
+
return await res.json();
|
|
223
|
+
} catch (err) {
|
|
224
|
+
return { success: false, error: err.message };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
181
227
|
(0, import_vue.onMounted)(() => fetchStatus());
|
|
182
228
|
(0, import_vue.onUnmounted)(() => stopPolling());
|
|
183
229
|
return {
|
|
@@ -194,7 +240,12 @@ function useSmsTunnel(options) {
|
|
|
194
240
|
unpair,
|
|
195
241
|
sendTestSms,
|
|
196
242
|
updateServerUrl,
|
|
197
|
-
refetch: fetchStatus
|
|
243
|
+
refetch: fetchStatus,
|
|
244
|
+
// E2E
|
|
245
|
+
getDeviceE2EStatus,
|
|
246
|
+
getDevicePublicKey,
|
|
247
|
+
verifyDeviceKey,
|
|
248
|
+
sendEncryptedSms
|
|
198
249
|
};
|
|
199
250
|
}
|
|
200
251
|
|
package/dist/vue/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/vue/index.ts","../../src/vue/SmsTunnelPairing.ts","../../src/vue/useSmsTunnel.ts","../../src/vue/QrCodeCanvas.ts","../../src/shared/labels.ts"],"sourcesContent":["export { SmsTunnelPairing } from './SmsTunnelPairing';\r\nexport { QrCodeCanvas } from './QrCodeCanvas';\r\nexport { useSmsTunnel } from './useSmsTunnel';\r\nexport { EN_LABELS, RO_LABELS } from '../shared/labels';\r\nexport type { SmsTunnelLabels } from '../shared/labels';\r\nexport type { UseSmsTunnelOptions, SendSmsResult } from './useSmsTunnel';\r\n","import { defineComponent, ref, watch, h, type PropType } from 'vue';\r\nimport { useSmsTunnel } from './useSmsTunnel';\r\nimport { QrCodeCanvas } from './QrCodeCanvas';\r\nimport { EN_LABELS, type SmsTunnelLabels } from '../shared/labels';\r\n\r\n// Inline styles (same as React version - no CSS dependency)\r\nconst s = {\r\n container: { fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif', fontSize: '14px', color: '#1f2937' },\r\n card: { border: '1px solid #e5e7eb', borderRadius: '12px', padding: '24px', backgroundColor: '#ffffff' },\r\n title: { fontSize: '18px', fontWeight: '600', color: '#111827', marginBottom: '4px' },\r\n subtitle: { fontSize: '13px', color: '#6b7280', marginBottom: '20px' },\r\n section: { border: '1px solid #e5e7eb', borderRadius: '8px', padding: '16px', marginBottom: '16px' },\r\n label: { display: 'block', fontSize: '13px', fontWeight: '500', color: '#374151', marginBottom: '6px' },\r\n inputRow: { display: 'flex', gap: '8px' },\r\n input: { flex: '1', border: '1px solid #d1d5db', borderRadius: '8px', padding: '6px 12px', fontSize: '13px', outline: 'none' },\r\n button: { padding: '6px 16px', borderRadius: '8px', border: 'none', fontSize: '13px', fontWeight: '500', cursor: 'pointer', backgroundColor: '#4f46e5', color: '#ffffff' },\r\n buttonSecondary: { padding: '6px 16px', borderRadius: '8px', border: '1px solid #d1d5db', fontSize: '13px', fontWeight: '500', cursor: 'pointer', backgroundColor: '#ffffff', color: '#374151' },\r\n pairedBanner: { display: 'flex', alignItems: 'center', gap: '12px', border: '1px solid #bbf7d0', backgroundColor: '#f0fdf4', borderRadius: '8px', padding: '16px', marginBottom: '16px' },\r\n pairedIcon: { width: '40px', height: '40px', borderRadius: '50%', backgroundColor: '#dcfce7', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#16a34a', fontSize: '18px', flexShrink: '0' },\r\n unpairedBanner: { display: 'flex', alignItems: 'center', gap: '12px', border: '1px solid #fde68a', backgroundColor: '#fefce8', borderRadius: '8px', padding: '16px', marginBottom: '16px' },\r\n qrContainer: { textAlign: 'center', padding: '16px' } as Record<string, string>,\r\n qrFrame: { display: 'inline-block', border: '2px dashed #a5b4fc', borderRadius: '12px', padding: '16px', backgroundColor: '#ffffff', marginBottom: '12px' },\r\n spinner: { display: 'inline-block', width: '12px', height: '12px', border: '2px solid #d1d5db', borderTopColor: '#4f46e5', borderRadius: '50%', animation: 'smstunnel-spin 0.8s linear infinite', marginRight: '8px', verticalAlign: 'middle' },\r\n error: { color: '#dc2626', fontSize: '13px', marginBottom: '12px' },\r\n success: { color: '#16a34a', fontSize: '12px', marginTop: '8px' },\r\n errorSmall: { color: '#dc2626', fontSize: '12px', marginTop: '8px' },\r\n};\r\n\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 const SmsTunnelPairing = defineComponent({\r\n name: 'SmsTunnelPairing',\r\n props: {\r\n apiBaseUrl: { type: String, required: true },\r\n getAuthHeaders: { type: Function as PropType<() => Record<string, string>>, default: undefined },\r\n labels: { type: Object as PropType<SmsTunnelLabels>, default: () => EN_LABELS },\r\n showTestSms: { type: Boolean, default: true },\r\n showServerUrlInput: { type: Boolean, default: true },\r\n qrSize: { type: Number, default: 220 },\r\n routePrefix: { type: String, default: 'smstunnel' },\r\n },\r\n emits: ['paired', 'unpaired'],\r\n setup(props, { emit }) {\r\n injectKeyframes();\r\n\r\n const tunnel = useSmsTunnel({\r\n apiBaseUrl: props.apiBaseUrl,\r\n getAuthHeaders: props.getAuthHeaders,\r\n routePrefix: props.routePrefix,\r\n });\r\n\r\n const localServerUrl = ref('');\r\n const savingUrl = ref(false);\r\n const testPhone = ref('');\r\n const testMsg = ref('Test SMS');\r\n const testResult = ref<{ success: boolean; error?: string; messageId?: string } | null>(null);\r\n const sendingTest = ref(false);\r\n\r\n watch(tunnel.serverUrl, (val) => {\r\n if (val && !localServerUrl.value) localServerUrl.value = val;\r\n }, { immediate: true });\r\n\r\n watch(tunnel.status, (val, old) => {\r\n if (val === 'paired' && old !== 'paired') {\r\n emit('paired', { deviceName: tunnel.deviceName.value });\r\n }\r\n });\r\n\r\n async function saveUrl() {\r\n if (!localServerUrl.value.trim()) return;\r\n savingUrl.value = true;\r\n await tunnel.updateServerUrl(localServerUrl.value.trim().replace(/\\/$/, ''));\r\n savingUrl.value = false;\r\n }\r\n\r\n async function handleUnpair() {\r\n if (!confirm(props.labels.unpairConfirm)) return;\r\n await tunnel.unpair();\r\n emit('unpaired');\r\n }\r\n\r\n async function handleTestSms() {\r\n if (!testPhone.value.trim()) return;\r\n sendingTest.value = true;\r\n testResult.value = null;\r\n testResult.value = await tunnel.sendTestSms(testPhone.value.trim(), testMsg.value);\r\n sendingTest.value = false;\r\n }\r\n\r\n return () => {\r\n const L = props.labels;\r\n\r\n if (tunnel.status.value === 'loading') {\r\n return h('div', { style: s.container }, [\r\n h('div', { style: s.card }, [\r\n h('div', { style: { textAlign: 'center', padding: '24px' } }, [\r\n h('span', { style: s.spinner }),\r\n ]),\r\n ]),\r\n ]);\r\n }\r\n\r\n const children: any[] = [];\r\n\r\n // Title\r\n children.push(h('div', { style: s.title }, L.title));\r\n children.push(h('div', { style: s.subtitle }, L.description));\r\n\r\n // Server URL\r\n if (props.showServerUrlInput) {\r\n children.push(\r\n h('div', { style: s.section }, [\r\n h('label', { style: s.label }, L.serverUrlLabel),\r\n h('div', { style: s.inputRow }, [\r\n h('input', {\r\n value: localServerUrl.value,\r\n onInput: (e: Event) => { localServerUrl.value = (e.target as HTMLInputElement).value; },\r\n placeholder: L.serverUrlPlaceholder,\r\n style: s.input,\r\n }),\r\n h('button', {\r\n style: { ...s.button, opacity: savingUrl.value || !localServerUrl.value.trim() ? '0.5' : '1' },\r\n onClick: saveUrl,\r\n disabled: savingUrl.value || !localServerUrl.value.trim(),\r\n }, L.saveButton),\r\n ]),\r\n ]),\r\n );\r\n }\r\n\r\n // Error\r\n if (tunnel.error.value) {\r\n children.push(h('div', { style: s.error }, tunnel.error.value));\r\n }\r\n\r\n if (tunnel.status.value === 'paired') {\r\n // Paired banner\r\n children.push(\r\n h('div', { style: s.pairedBanner }, [\r\n h('div', { style: s.pairedIcon }, '\\u2713'),\r\n h('div', { style: { flex: '1' } }, [\r\n h('div', { style: { fontSize: '14px', fontWeight: '600', color: '#166534' } }, L.pairedStatus),\r\n h('div', { style: { fontSize: '12px', color: '#16a34a' } },\r\n `${L.deviceLabel}: ${tunnel.deviceName.value || 'Android Phone'}`),\r\n ]),\r\n h('button', {\r\n onClick: handleUnpair,\r\n style: { padding: '6px', borderRadius: '8px', border: 'none', cursor: 'pointer', backgroundColor: 'transparent', color: '#9ca3af' },\r\n title: L.unpairButton,\r\n }, '\\u2715'),\r\n ]),\r\n );\r\n\r\n // Test SMS\r\n if (props.showTestSms) {\r\n const testChildren: any[] = [\r\n h('div', { style: { fontSize: '13px', fontWeight: '600', color: '#374151', marginBottom: '12px' } }, L.testSmsTitle),\r\n h('div', { style: s.inputRow }, [\r\n h('input', {\r\n value: testPhone.value,\r\n onInput: (e: Event) => { testPhone.value = (e.target as HTMLInputElement).value; },\r\n placeholder: L.testPhonePlaceholder,\r\n style: s.input,\r\n }),\r\n h('input', {\r\n value: testMsg.value,\r\n onInput: (e: Event) => { testMsg.value = (e.target as HTMLInputElement).value; },\r\n placeholder: L.testMessagePlaceholder,\r\n style: s.input,\r\n }),\r\n h('button', {\r\n style: { ...s.button, opacity: sendingTest.value || !testPhone.value.trim() ? '0.5' : '1' },\r\n onClick: handleTestSms,\r\n disabled: sendingTest.value || !testPhone.value.trim(),\r\n }, sendingTest.value ? h('span', { style: s.spinner }) : L.sendTestButton),\r\n ]),\r\n ];\r\n\r\n if (testResult.value) {\r\n testChildren.push(\r\n h('div', { style: testResult.value.success ? s.success : s.errorSmall },\r\n testResult.value.success\r\n ? `${L.smsSentSuccess} (ID: ${testResult.value.messageId})`\r\n : `${L.smsError}: ${testResult.value.error}`),\r\n );\r\n }\r\n\r\n children.push(h('div', { style: s.section }, testChildren));\r\n }\r\n } else {\r\n // Unpaired banner\r\n children.push(\r\n h('div', { style: s.unpairedBanner }, [\r\n h('div', { style: { fontSize: '24px' } }, '\\uD83D\\uDCF1'),\r\n h('div', {}, [\r\n h('div', { style: { fontSize: '14px', fontWeight: '600', color: '#854d0e' } }, L.notPairedStatus),\r\n h('div', { style: { fontSize: '12px', color: '#a16207' } }, L.notPairedDescription),\r\n ]),\r\n ]),\r\n );\r\n\r\n if (!tunnel.showQr.value) {\r\n children.push(\r\n h('button', {\r\n style: { ...s.button, opacity: tunnel.generating.value ? '0.5' : '1' },\r\n onClick: tunnel.generateQr,\r\n disabled: tunnel.generating.value,\r\n }, [\r\n tunnel.generating.value ? h('span', { style: s.spinner }) : null,\r\n L.connectButton,\r\n ]),\r\n );\r\n } else {\r\n children.push(\r\n h('div', { style: s.qrContainer }, [\r\n h('div', { style: { fontSize: '13px', color: '#4b5563', marginBottom: '12px' } }, L.scanQrPrompt),\r\n h('div', { style: s.qrFrame }, [\r\n h(QrCodeCanvas, { value: tunnel.qrData.value, size: props.qrSize }),\r\n ]),\r\n h('div', {\r\n style: { display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '8px', fontSize: '12px', color: '#9ca3af', marginBottom: '12px' },\r\n }, [\r\n h('span', { style: s.spinner }),\r\n L.waitingForPairing,\r\n ]),\r\n h('button', { style: s.buttonSecondary, onClick: tunnel.cancelPairing }, L.cancelButton),\r\n ]),\r\n );\r\n }\r\n }\r\n\r\n return h('div', { style: s.container }, [\r\n h('div', { style: s.card }, children),\r\n ]);\r\n };\r\n },\r\n});\r\n","import { ref, onMounted, onUnmounted, watch } from 'vue';\r\nimport type { SmsTunnelLabels } from '../shared/labels';\r\n\r\nexport interface UseSmsTunnelOptions {\r\n apiBaseUrl: string;\r\n getAuthHeaders?: () => Record<string, string>;\r\n routePrefix?: string;\r\n pollInterval?: number;\r\n}\r\n\r\nexport interface SendSmsResult {\r\n success: boolean;\r\n messageId?: string;\r\n error?: string;\r\n}\r\n\r\nexport function useSmsTunnel(options: UseSmsTunnelOptions) {\r\n const {\r\n apiBaseUrl,\r\n getAuthHeaders,\r\n routePrefix = 'smstunnel',\r\n pollInterval = 3000,\r\n } = options;\r\n\r\n const baseUrl = apiBaseUrl.replace(/\\/$/, '');\r\n\r\n const status = ref<'loading' | 'unpaired' | 'paired'>('loading');\r\n const serverUrl = ref('');\r\n const deviceName = ref('');\r\n const showQr = ref(false);\r\n const qrData = ref('');\r\n const generating = ref(false);\r\n const polling = ref(false);\r\n const error = ref('');\r\n\r\n let pairingToken = '';\r\n let pollTimer: ReturnType<typeof setInterval> | null = null;\r\n\r\n function authHeaders(): Record<string, string> {\r\n return getAuthHeaders ? getAuthHeaders() : {};\r\n }\r\n\r\n async function fetchStatus() {\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 status.value = data.paired ? 'paired' : 'unpaired';\r\n serverUrl.value = data.serverUrl || '';\r\n deviceName.value = data.deviceName || '';\r\n } catch {\r\n status.value = 'unpaired';\r\n }\r\n }\r\n\r\n function startPolling() {\r\n stopPolling();\r\n pollTimer = setInterval(async () => {\r\n if (!pairingToken) return;\r\n try {\r\n const res = await fetch(\r\n `${baseUrl}/${routePrefix}/pairing-status/${pairingToken}`,\r\n );\r\n const data = await res.json();\r\n if (data.status === 'completed') {\r\n polling.value = false;\r\n showQr.value = false;\r\n stopPolling();\r\n setTimeout(() => fetchStatus(), 2000);\r\n } else if (data.status === 'expired') {\r\n polling.value = false;\r\n showQr.value = false;\r\n error.value = 'QR code expired';\r\n stopPolling();\r\n }\r\n } catch {\r\n // ignore\r\n }\r\n }, pollInterval);\r\n }\r\n\r\n function stopPolling() {\r\n if (pollTimer) {\r\n clearInterval(pollTimer);\r\n pollTimer = null;\r\n }\r\n }\r\n\r\n watch(polling, (val) => {\r\n if (val) startPolling();\r\n else stopPolling();\r\n });\r\n\r\n async function generateQr() {\r\n generating.value = true;\r\n error.value = '';\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/create-token`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', ...authHeaders() },\r\n });\r\n const data = await res.json();\r\n if (!data.success) {\r\n error.value = data.error || 'Failed to create QR code';\r\n generating.value = false;\r\n return;\r\n }\r\n pairingToken = data.token;\r\n qrData.value = data.qrData;\r\n showQr.value = true;\r\n polling.value = true;\r\n } catch (err: any) {\r\n error.value = `Error: ${err.message}`;\r\n } finally {\r\n generating.value = false;\r\n }\r\n }\r\n\r\n function cancelPairing() {\r\n stopPolling();\r\n pairingToken = '';\r\n showQr.value = false;\r\n polling.value = false;\r\n qrData.value = '';\r\n }\r\n\r\n async function unpair() {\r\n try {\r\n await fetch(`${baseUrl}/${routePrefix}/unpair`, {\r\n method: 'POST',\r\n headers: authHeaders(),\r\n });\r\n status.value = 'unpaired';\r\n deviceName.value = '';\r\n } catch (err: any) {\r\n error.value = `Unpair failed: ${err.message}`;\r\n }\r\n }\r\n\r\n async function sendTestSms(to: string, message: string): Promise<SendSmsResult> {\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/send`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', ...authHeaders() },\r\n body: JSON.stringify({ to, message }),\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 async function updateServerUrl(url: string) {\r\n try {\r\n await fetch(`${baseUrl}/${routePrefix}/update-config`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', ...authHeaders() },\r\n body: JSON.stringify({ serverUrl: url }),\r\n });\r\n serverUrl.value = url;\r\n } catch (err: any) {\r\n error.value = `Update failed: ${err.message}`;\r\n }\r\n }\r\n\r\n onMounted(() => fetchStatus());\r\n onUnmounted(() => stopPolling());\r\n\r\n return {\r\n status,\r\n serverUrl,\r\n deviceName,\r\n showQr,\r\n qrData,\r\n generating,\r\n polling,\r\n error,\r\n generateQr,\r\n cancelPairing,\r\n unpair,\r\n sendTestSms,\r\n updateServerUrl,\r\n refetch: fetchStatus,\r\n };\r\n}\r\n","import { defineComponent, ref, onMounted, watch, h } from 'vue';\r\nimport qrcode from 'qrcode-generator';\r\n\r\nexport const QrCodeCanvas = defineComponent({\r\n name: 'QrCodeCanvas',\r\n props: {\r\n value: { type: String, required: true },\r\n size: { type: Number, default: 200 },\r\n },\r\n setup(props) {\r\n const canvasRef = ref<HTMLCanvasElement | null>(null);\r\n\r\n function render() {\r\n if (!canvasRef.value || !props.value) return;\r\n const canvas = canvasRef.value;\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(props.value);\r\n qr.make();\r\n\r\n const moduleCount = qr.getModuleCount();\r\n const cellSize = props.size / moduleCount;\r\n\r\n canvas.width = props.size;\r\n canvas.height = props.size;\r\n ctx.fillStyle = '#ffffff';\r\n ctx.fillRect(0, 0, props.size, props.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 }\r\n\r\n onMounted(render);\r\n watch(() => [props.value, props.size], render);\r\n\r\n return () =>\r\n h('canvas', {\r\n ref: canvasRef,\r\n width: props.size,\r\n height: props.size,\r\n });\r\n },\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":["import_vue","import_vue","qrcode"]}
|
|
1
|
+
{"version":3,"sources":["../../src/vue/index.ts","../../src/vue/SmsTunnelPairing.ts","../../src/vue/useSmsTunnel.ts","../../src/vue/QrCodeCanvas.ts","../../src/shared/labels.ts"],"sourcesContent":["export { SmsTunnelPairing } from './SmsTunnelPairing';\r\nexport { QrCodeCanvas } from './QrCodeCanvas';\r\nexport { useSmsTunnel } from './useSmsTunnel';\r\nexport { EN_LABELS, RO_LABELS } from '../shared/labels';\r\nexport type { SmsTunnelLabels } from '../shared/labels';\r\nexport type { UseSmsTunnelOptions, SendSmsResult } from './useSmsTunnel';\r\n","import { defineComponent, ref, watch, h, type PropType } from 'vue';\r\nimport { useSmsTunnel } from './useSmsTunnel';\r\nimport { QrCodeCanvas } from './QrCodeCanvas';\r\nimport { EN_LABELS, type SmsTunnelLabels } from '../shared/labels';\r\n\r\n// Inline styles (same as React version - no CSS dependency)\r\nconst s = {\r\n container: { fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif', fontSize: '14px', color: '#1f2937' },\r\n card: { border: '1px solid #e5e7eb', borderRadius: '12px', padding: '24px', backgroundColor: '#ffffff' },\r\n title: { fontSize: '18px', fontWeight: '600', color: '#111827', marginBottom: '4px' },\r\n subtitle: { fontSize: '13px', color: '#6b7280', marginBottom: '20px' },\r\n section: { border: '1px solid #e5e7eb', borderRadius: '8px', padding: '16px', marginBottom: '16px' },\r\n label: { display: 'block', fontSize: '13px', fontWeight: '500', color: '#374151', marginBottom: '6px' },\r\n inputRow: { display: 'flex', gap: '8px' },\r\n input: { flex: '1', border: '1px solid #d1d5db', borderRadius: '8px', padding: '6px 12px', fontSize: '13px', outline: 'none' },\r\n button: { padding: '6px 16px', borderRadius: '8px', border: 'none', fontSize: '13px', fontWeight: '500', cursor: 'pointer', backgroundColor: '#4f46e5', color: '#ffffff' },\r\n buttonSecondary: { padding: '6px 16px', borderRadius: '8px', border: '1px solid #d1d5db', fontSize: '13px', fontWeight: '500', cursor: 'pointer', backgroundColor: '#ffffff', color: '#374151' },\r\n pairedBanner: { display: 'flex', alignItems: 'center', gap: '12px', border: '1px solid #bbf7d0', backgroundColor: '#f0fdf4', borderRadius: '8px', padding: '16px', marginBottom: '16px' },\r\n pairedIcon: { width: '40px', height: '40px', borderRadius: '50%', backgroundColor: '#dcfce7', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#16a34a', fontSize: '18px', flexShrink: '0' },\r\n unpairedBanner: { display: 'flex', alignItems: 'center', gap: '12px', border: '1px solid #fde68a', backgroundColor: '#fefce8', borderRadius: '8px', padding: '16px', marginBottom: '16px' },\r\n qrContainer: { textAlign: 'center', padding: '16px' } as Record<string, string>,\r\n qrFrame: { display: 'inline-block', border: '2px dashed #a5b4fc', borderRadius: '12px', padding: '16px', backgroundColor: '#ffffff', marginBottom: '12px' },\r\n spinner: { display: 'inline-block', width: '12px', height: '12px', border: '2px solid #d1d5db', borderTopColor: '#4f46e5', borderRadius: '50%', animation: 'smstunnel-spin 0.8s linear infinite', marginRight: '8px', verticalAlign: 'middle' },\r\n error: { color: '#dc2626', fontSize: '13px', marginBottom: '12px' },\r\n success: { color: '#16a34a', fontSize: '12px', marginTop: '8px' },\r\n errorSmall: { color: '#dc2626', fontSize: '12px', marginTop: '8px' },\r\n};\r\n\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 const SmsTunnelPairing = defineComponent({\r\n name: 'SmsTunnelPairing',\r\n props: {\r\n apiBaseUrl: { type: String, required: true },\r\n getAuthHeaders: { type: Function as PropType<() => Record<string, string>>, default: undefined },\r\n labels: { type: Object as PropType<SmsTunnelLabels>, default: () => EN_LABELS },\r\n showTestSms: { type: Boolean, default: true },\r\n showServerUrlInput: { type: Boolean, default: true },\r\n qrSize: { type: Number, default: 220 },\r\n routePrefix: { type: String, default: 'smstunnel' },\r\n },\r\n emits: ['paired', 'unpaired'],\r\n setup(props, { emit }) {\r\n injectKeyframes();\r\n\r\n const tunnel = useSmsTunnel({\r\n apiBaseUrl: props.apiBaseUrl,\r\n getAuthHeaders: props.getAuthHeaders,\r\n routePrefix: props.routePrefix,\r\n });\r\n\r\n const localServerUrl = ref('');\r\n const savingUrl = ref(false);\r\n const testPhone = ref('');\r\n const testMsg = ref('Test SMS');\r\n const testResult = ref<{ success: boolean; error?: string; messageId?: string } | null>(null);\r\n const sendingTest = ref(false);\r\n\r\n watch(tunnel.serverUrl, (val) => {\r\n if (val && !localServerUrl.value) localServerUrl.value = val;\r\n }, { immediate: true });\r\n\r\n watch(tunnel.status, (val, old) => {\r\n if (val === 'paired' && old !== 'paired') {\r\n emit('paired', { deviceName: tunnel.deviceName.value });\r\n }\r\n });\r\n\r\n async function saveUrl() {\r\n if (!localServerUrl.value.trim()) return;\r\n savingUrl.value = true;\r\n await tunnel.updateServerUrl(localServerUrl.value.trim().replace(/\\/$/, ''));\r\n savingUrl.value = false;\r\n }\r\n\r\n async function handleUnpair() {\r\n if (!confirm(props.labels.unpairConfirm)) return;\r\n await tunnel.unpair();\r\n emit('unpaired');\r\n }\r\n\r\n async function handleTestSms() {\r\n if (!testPhone.value.trim()) return;\r\n sendingTest.value = true;\r\n testResult.value = null;\r\n testResult.value = await tunnel.sendTestSms(testPhone.value.trim(), testMsg.value);\r\n sendingTest.value = false;\r\n }\r\n\r\n return () => {\r\n const L = props.labels;\r\n\r\n if (tunnel.status.value === 'loading') {\r\n return h('div', { style: s.container }, [\r\n h('div', { style: s.card }, [\r\n h('div', { style: { textAlign: 'center', padding: '24px' } }, [\r\n h('span', { style: s.spinner }),\r\n ]),\r\n ]),\r\n ]);\r\n }\r\n\r\n const children: any[] = [];\r\n\r\n // Title\r\n children.push(h('div', { style: s.title }, L.title));\r\n children.push(h('div', { style: s.subtitle }, L.description));\r\n\r\n // Server URL\r\n if (props.showServerUrlInput) {\r\n children.push(\r\n h('div', { style: s.section }, [\r\n h('label', { style: s.label }, L.serverUrlLabel),\r\n h('div', { style: s.inputRow }, [\r\n h('input', {\r\n value: localServerUrl.value,\r\n onInput: (e: Event) => { localServerUrl.value = (e.target as HTMLInputElement).value; },\r\n placeholder: L.serverUrlPlaceholder,\r\n style: s.input,\r\n }),\r\n h('button', {\r\n style: { ...s.button, opacity: savingUrl.value || !localServerUrl.value.trim() ? '0.5' : '1' },\r\n onClick: saveUrl,\r\n disabled: savingUrl.value || !localServerUrl.value.trim(),\r\n }, L.saveButton),\r\n ]),\r\n ]),\r\n );\r\n }\r\n\r\n // Error\r\n if (tunnel.error.value) {\r\n children.push(h('div', { style: s.error }, tunnel.error.value));\r\n }\r\n\r\n if (tunnel.status.value === 'paired') {\r\n // Paired banner\r\n children.push(\r\n h('div', { style: s.pairedBanner }, [\r\n h('div', { style: s.pairedIcon }, '\\u2713'),\r\n h('div', { style: { flex: '1' } }, [\r\n h('div', { style: { fontSize: '14px', fontWeight: '600', color: '#166534' } }, L.pairedStatus),\r\n h('div', { style: { fontSize: '12px', color: '#16a34a' } },\r\n `${L.deviceLabel}: ${tunnel.deviceName.value || 'Android Phone'}`),\r\n ]),\r\n h('button', {\r\n onClick: handleUnpair,\r\n style: { padding: '6px', borderRadius: '8px', border: 'none', cursor: 'pointer', backgroundColor: 'transparent', color: '#9ca3af' },\r\n title: L.unpairButton,\r\n }, '\\u2715'),\r\n ]),\r\n );\r\n\r\n // Test SMS\r\n if (props.showTestSms) {\r\n const testChildren: any[] = [\r\n h('div', { style: { fontSize: '13px', fontWeight: '600', color: '#374151', marginBottom: '12px' } }, L.testSmsTitle),\r\n h('div', { style: s.inputRow }, [\r\n h('input', {\r\n value: testPhone.value,\r\n onInput: (e: Event) => { testPhone.value = (e.target as HTMLInputElement).value; },\r\n placeholder: L.testPhonePlaceholder,\r\n style: s.input,\r\n }),\r\n h('input', {\r\n value: testMsg.value,\r\n onInput: (e: Event) => { testMsg.value = (e.target as HTMLInputElement).value; },\r\n placeholder: L.testMessagePlaceholder,\r\n style: s.input,\r\n }),\r\n h('button', {\r\n style: { ...s.button, opacity: sendingTest.value || !testPhone.value.trim() ? '0.5' : '1' },\r\n onClick: handleTestSms,\r\n disabled: sendingTest.value || !testPhone.value.trim(),\r\n }, sendingTest.value ? h('span', { style: s.spinner }) : L.sendTestButton),\r\n ]),\r\n ];\r\n\r\n if (testResult.value) {\r\n testChildren.push(\r\n h('div', { style: testResult.value.success ? s.success : s.errorSmall },\r\n testResult.value.success\r\n ? `${L.smsSentSuccess} (ID: ${testResult.value.messageId})`\r\n : `${L.smsError}: ${testResult.value.error}`),\r\n );\r\n }\r\n\r\n children.push(h('div', { style: s.section }, testChildren));\r\n }\r\n } else {\r\n // Unpaired banner\r\n children.push(\r\n h('div', { style: s.unpairedBanner }, [\r\n h('div', { style: { fontSize: '24px' } }, '\\uD83D\\uDCF1'),\r\n h('div', {}, [\r\n h('div', { style: { fontSize: '14px', fontWeight: '600', color: '#854d0e' } }, L.notPairedStatus),\r\n h('div', { style: { fontSize: '12px', color: '#a16207' } }, L.notPairedDescription),\r\n ]),\r\n ]),\r\n );\r\n\r\n if (!tunnel.showQr.value) {\r\n children.push(\r\n h('button', {\r\n style: { ...s.button, opacity: tunnel.generating.value ? '0.5' : '1' },\r\n onClick: tunnel.generateQr,\r\n disabled: tunnel.generating.value,\r\n }, [\r\n tunnel.generating.value ? h('span', { style: s.spinner }) : null,\r\n L.connectButton,\r\n ]),\r\n );\r\n } else {\r\n children.push(\r\n h('div', { style: s.qrContainer }, [\r\n h('div', { style: { fontSize: '13px', color: '#4b5563', marginBottom: '12px' } }, L.scanQrPrompt),\r\n h('div', { style: s.qrFrame }, [\r\n h(QrCodeCanvas, { value: tunnel.qrData.value, size: props.qrSize }),\r\n ]),\r\n h('div', {\r\n style: { display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '8px', fontSize: '12px', color: '#9ca3af', marginBottom: '12px' },\r\n }, [\r\n h('span', { style: s.spinner }),\r\n L.waitingForPairing,\r\n ]),\r\n h('button', { style: s.buttonSecondary, onClick: tunnel.cancelPairing }, L.cancelButton),\r\n ]),\r\n );\r\n }\r\n }\r\n\r\n return h('div', { style: s.container }, [\r\n h('div', { style: s.card }, children),\r\n ]);\r\n };\r\n },\r\n});\r\n","import { ref, onMounted, onUnmounted, watch } from 'vue';\r\nimport type { SmsTunnelLabels } from '../shared/labels';\r\n\r\nexport interface UseSmsTunnelOptions {\r\n apiBaseUrl: string;\r\n getAuthHeaders?: () => Record<string, string>;\r\n routePrefix?: string;\r\n pollInterval?: number;\r\n}\r\n\r\nexport interface SendSmsResult {\r\n success: boolean;\r\n messageId?: string;\r\n error?: string;\r\n}\r\n\r\nexport function useSmsTunnel(options: UseSmsTunnelOptions) {\r\n const {\r\n apiBaseUrl,\r\n getAuthHeaders,\r\n routePrefix = 'smstunnel',\r\n pollInterval = 3000,\r\n } = options;\r\n\r\n const baseUrl = apiBaseUrl.replace(/\\/$/, '');\r\n\r\n const status = ref<'loading' | 'unpaired' | 'paired'>('loading');\r\n const serverUrl = ref('');\r\n const deviceName = ref('');\r\n const showQr = ref(false);\r\n const qrData = ref('');\r\n const generating = ref(false);\r\n const polling = ref(false);\r\n const error = ref('');\r\n\r\n let pairingToken = '';\r\n let pollTimer: ReturnType<typeof setInterval> | null = null;\r\n\r\n function authHeaders(): Record<string, string> {\r\n return getAuthHeaders ? getAuthHeaders() : {};\r\n }\r\n\r\n async function fetchStatus() {\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 status.value = data.paired ? 'paired' : 'unpaired';\r\n serverUrl.value = data.serverUrl || '';\r\n deviceName.value = data.deviceName || '';\r\n } catch {\r\n status.value = 'unpaired';\r\n }\r\n }\r\n\r\n function startPolling() {\r\n stopPolling();\r\n pollTimer = setInterval(async () => {\r\n if (!pairingToken) return;\r\n try {\r\n const res = await fetch(\r\n `${baseUrl}/${routePrefix}/pairing-status/${pairingToken}`,\r\n );\r\n const data = await res.json();\r\n if (data.status === 'completed') {\r\n polling.value = false;\r\n showQr.value = false;\r\n stopPolling();\r\n setTimeout(() => fetchStatus(), 2000);\r\n } else if (data.status === 'expired') {\r\n polling.value = false;\r\n showQr.value = false;\r\n error.value = 'QR code expired';\r\n stopPolling();\r\n }\r\n } catch {\r\n // ignore\r\n }\r\n }, pollInterval);\r\n }\r\n\r\n function stopPolling() {\r\n if (pollTimer) {\r\n clearInterval(pollTimer);\r\n pollTimer = null;\r\n }\r\n }\r\n\r\n watch(polling, (val) => {\r\n if (val) startPolling();\r\n else stopPolling();\r\n });\r\n\r\n async function generateQr() {\r\n generating.value = true;\r\n error.value = '';\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/create-token`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', ...authHeaders() },\r\n });\r\n const data = await res.json();\r\n if (!data.success) {\r\n error.value = data.error || 'Failed to create QR code';\r\n generating.value = false;\r\n return;\r\n }\r\n pairingToken = data.token;\r\n qrData.value = data.qrData;\r\n showQr.value = true;\r\n polling.value = true;\r\n } catch (err: any) {\r\n error.value = `Error: ${err.message}`;\r\n } finally {\r\n generating.value = false;\r\n }\r\n }\r\n\r\n function cancelPairing() {\r\n stopPolling();\r\n pairingToken = '';\r\n showQr.value = false;\r\n polling.value = false;\r\n qrData.value = '';\r\n }\r\n\r\n async function unpair() {\r\n try {\r\n await fetch(`${baseUrl}/${routePrefix}/unpair`, {\r\n method: 'POST',\r\n headers: authHeaders(),\r\n });\r\n status.value = 'unpaired';\r\n deviceName.value = '';\r\n } catch (err: any) {\r\n error.value = `Unpair failed: ${err.message}`;\r\n }\r\n }\r\n\r\n async function sendTestSms(to: string, message: string): Promise<SendSmsResult> {\r\n try {\r\n const res = await fetch(`${baseUrl}/${routePrefix}/send`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', ...authHeaders() },\r\n body: JSON.stringify({ to, message }),\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 async function updateServerUrl(url: string) {\r\n try {\r\n await fetch(`${baseUrl}/${routePrefix}/update-config`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json', ...authHeaders() },\r\n body: JSON.stringify({ serverUrl: url }),\r\n });\r\n serverUrl.value = url;\r\n } catch (err: any) {\r\n error.value = `Update failed: ${err.message}`;\r\n }\r\n }\r\n\r\n // ─── E2E Encryption ────────────────────────────────\r\n\r\n async function getDeviceE2EStatus(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\r\n async function getDevicePublicKey(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\r\n async function verifyDeviceKey(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\r\n async function sendEncryptedSms(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\r\n onMounted(() => fetchStatus());\r\n onUnmounted(() => stopPolling());\r\n\r\n return {\r\n status,\r\n serverUrl,\r\n deviceName,\r\n showQr,\r\n qrData,\r\n generating,\r\n polling,\r\n error,\r\n generateQr,\r\n cancelPairing,\r\n unpair,\r\n sendTestSms,\r\n updateServerUrl,\r\n refetch: fetchStatus,\r\n // E2E\r\n getDeviceE2EStatus,\r\n getDevicePublicKey,\r\n verifyDeviceKey,\r\n sendEncryptedSms,\r\n };\r\n}\r\n","import { defineComponent, ref, onMounted, watch, h } from 'vue';\r\nimport qrcode from 'qrcode-generator';\r\n\r\nexport const QrCodeCanvas = defineComponent({\r\n name: 'QrCodeCanvas',\r\n props: {\r\n value: { type: String, required: true },\r\n size: { type: Number, default: 200 },\r\n },\r\n setup(props) {\r\n const canvasRef = ref<HTMLCanvasElement | null>(null);\r\n\r\n function render() {\r\n if (!canvasRef.value || !props.value) return;\r\n const canvas = canvasRef.value;\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(props.value);\r\n qr.make();\r\n\r\n const moduleCount = qr.getModuleCount();\r\n const cellSize = props.size / moduleCount;\r\n\r\n canvas.width = props.size;\r\n canvas.height = props.size;\r\n ctx.fillStyle = '#ffffff';\r\n ctx.fillRect(0, 0, props.size, props.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 }\r\n\r\n onMounted(render);\r\n watch(() => [props.value, props.size], render);\r\n\r\n return () =>\r\n h('canvas', {\r\n ref: canvasRef,\r\n width: props.size,\r\n height: props.size,\r\n });\r\n },\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":["import_vue","import_vue","qrcode"]}
|
package/dist/vue/index.mjs
CHANGED
|
@@ -138,6 +138,52 @@ function useSmsTunnel(options) {
|
|
|
138
138
|
error.value = `Update failed: ${err.message}`;
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
|
+
async function getDeviceE2EStatus(deviceId) {
|
|
142
|
+
try {
|
|
143
|
+
const res = await fetch(
|
|
144
|
+
`${baseUrl}/${routePrefix}/e2e-status/${deviceId}`,
|
|
145
|
+
{ headers: authHeaders() }
|
|
146
|
+
);
|
|
147
|
+
return await res.json();
|
|
148
|
+
} catch (err) {
|
|
149
|
+
return { success: false, error: err.message };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async function getDevicePublicKey(deviceId) {
|
|
153
|
+
try {
|
|
154
|
+
const res = await fetch(
|
|
155
|
+
`${baseUrl}/${routePrefix}/public-key/${deviceId}`,
|
|
156
|
+
{ headers: authHeaders() }
|
|
157
|
+
);
|
|
158
|
+
return await res.json();
|
|
159
|
+
} catch (err) {
|
|
160
|
+
return { success: false, error: err.message };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
async function verifyDeviceKey(deviceId, fingerprint) {
|
|
164
|
+
try {
|
|
165
|
+
const res = await fetch(`${baseUrl}/${routePrefix}/verify-key`, {
|
|
166
|
+
method: "POST",
|
|
167
|
+
headers: { "Content-Type": "application/json", ...authHeaders() },
|
|
168
|
+
body: JSON.stringify({ deviceId, fingerprint })
|
|
169
|
+
});
|
|
170
|
+
return await res.json();
|
|
171
|
+
} catch (err) {
|
|
172
|
+
return { valid: false, needsRePairing: true, error: err.message };
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
async function sendEncryptedSms(encryptedPayload, deviceId, is2FA = false) {
|
|
176
|
+
try {
|
|
177
|
+
const res = await fetch(`${baseUrl}/${routePrefix}/send-encrypted`, {
|
|
178
|
+
method: "POST",
|
|
179
|
+
headers: { "Content-Type": "application/json", ...authHeaders() },
|
|
180
|
+
body: JSON.stringify({ encryptedPayload, deviceId, is2FA })
|
|
181
|
+
});
|
|
182
|
+
return await res.json();
|
|
183
|
+
} catch (err) {
|
|
184
|
+
return { success: false, error: err.message };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
141
187
|
onMounted(() => fetchStatus());
|
|
142
188
|
onUnmounted(() => stopPolling());
|
|
143
189
|
return {
|
|
@@ -154,7 +200,12 @@ function useSmsTunnel(options) {
|
|
|
154
200
|
unpair,
|
|
155
201
|
sendTestSms,
|
|
156
202
|
updateServerUrl,
|
|
157
|
-
refetch: fetchStatus
|
|
203
|
+
refetch: fetchStatus,
|
|
204
|
+
// E2E
|
|
205
|
+
getDeviceE2EStatus,
|
|
206
|
+
getDevicePublicKey,
|
|
207
|
+
verifyDeviceKey,
|
|
208
|
+
sendEncryptedSms
|
|
158
209
|
};
|
|
159
210
|
}
|
|
160
211
|
|