@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.
@@ -85,6 +85,22 @@ interface LegacyCallbackBody {
85
85
  device_name?: string;
86
86
  status?: string;
87
87
  }
88
+ interface DeviceE2EStatus {
89
+ encryptionEnabled: boolean;
90
+ hasPublicKey: boolean;
91
+ publicKeyFingerprint?: string;
92
+ keyCreatedAt?: string;
93
+ }
94
+ interface DevicePublicKeyInfo {
95
+ deviceId: string;
96
+ deviceName: string;
97
+ publicKey: string;
98
+ }
99
+ interface VerifyDeviceKeyResult {
100
+ valid: boolean;
101
+ currentFingerprint?: string;
102
+ needsRePairing: boolean;
103
+ }
88
104
 
89
105
  declare class SmsTunnelModule {
90
106
  static forRoot(options: SmsTunnelModuleOptions): DynamicModule;
@@ -169,6 +185,35 @@ declare class SmsTunnelService {
169
185
  * Get account usage stats.
170
186
  */
171
187
  getUsage(): Promise<any>;
188
+ /**
189
+ * Get E2E encryption status for a device.
190
+ */
191
+ getDeviceE2EStatus(deviceId: string): Promise<{
192
+ success: boolean;
193
+ data?: DeviceE2EStatus;
194
+ error?: string;
195
+ }>;
196
+ /**
197
+ * Get device public key for E2E encryption.
198
+ */
199
+ getDevicePublicKey(deviceId: string): Promise<{
200
+ success: boolean;
201
+ data?: DevicePublicKeyInfo;
202
+ error?: string;
203
+ }>;
204
+ /**
205
+ * Verify that a stored fingerprint matches the current device key.
206
+ */
207
+ verifyDeviceKey(deviceId: string, storedFingerprint: string): Promise<VerifyDeviceKeyResult>;
208
+ /**
209
+ * Send encrypted SMS via SMSTunnel API.
210
+ * The payload is encrypted client-side with the device's RSA public key.
211
+ */
212
+ sendEncryptedSms(encryptedPayload: string, deviceId: string, is2FA?: boolean): Promise<SendSmsResult>;
213
+ /**
214
+ * List pairings for the authenticated user.
215
+ */
216
+ getPairings(): Promise<any>;
172
217
  /** Make an authenticated GET request to SMSTunnel */
173
218
  private apiGet;
174
219
  /** Make an authenticated POST request to SMSTunnel */
@@ -268,6 +313,46 @@ declare class SmsTunnelController {
268
313
  * Route: GET /{prefix}/received
269
314
  */
270
315
  receivedSms(): Promise<any>;
316
+ /**
317
+ * Get E2E encryption status for a device.
318
+ * Route: GET /{prefix}/e2e-status/:deviceId
319
+ */
320
+ e2eStatus(deviceId: string): Promise<{
321
+ success: boolean;
322
+ data?: DeviceE2EStatus;
323
+ error?: string;
324
+ }>;
325
+ /**
326
+ * Get device public key for E2E encryption.
327
+ * Route: GET /{prefix}/public-key/:deviceId
328
+ */
329
+ publicKey(deviceId: string): Promise<{
330
+ success: boolean;
331
+ data?: DevicePublicKeyInfo;
332
+ error?: string;
333
+ }>;
334
+ /**
335
+ * Verify device key fingerprint.
336
+ * Route: POST /{prefix}/verify-key
337
+ */
338
+ verifyKey(body: {
339
+ deviceId: string;
340
+ fingerprint: string;
341
+ }): Promise<VerifyDeviceKeyResult>;
342
+ /**
343
+ * Send encrypted SMS.
344
+ * Route: POST /{prefix}/send-encrypted
345
+ */
346
+ sendEncrypted(body: {
347
+ encryptedPayload: string;
348
+ deviceId: string;
349
+ is2FA?: boolean;
350
+ }): Promise<SendSmsResult>;
351
+ /**
352
+ * List pairings.
353
+ * Route: GET /{prefix}/pairings
354
+ */
355
+ pairings(): Promise<any>;
271
356
  /**
272
357
  * List paired devices.
273
358
  * Route: GET /{prefix}/devices
@@ -295,4 +380,4 @@ declare const SMSTUNNEL_PUBLIC_PATHS: string[];
295
380
  */
296
381
  declare function getSmsTunnelPublicPaths(prefix: string): string[];
297
382
 
298
- export { type CreateTokenResult, type LegacyCallbackBody, type PairingCallbackBody, type PairingStatusResult, SMSTUNNEL_DEFAULT_PREFIX, SMSTUNNEL_OPTIONS, SMSTUNNEL_PUBLIC_PATHS, type SaasActivateResult, type SendSmsResult, type SmsTunnelConfig, SmsTunnelController, SmsTunnelModule, type SmsTunnelModuleAsyncOptions, type SmsTunnelModuleOptions, SmsTunnelService, type SmsTunnelStatus, type SmsTunnelStorageAdapter, getSmsTunnelPublicPaths };
383
+ export { type CreateTokenResult, type DeviceE2EStatus, type DevicePublicKeyInfo, type LegacyCallbackBody, type PairingCallbackBody, type PairingStatusResult, SMSTUNNEL_DEFAULT_PREFIX, SMSTUNNEL_OPTIONS, SMSTUNNEL_PUBLIC_PATHS, type SaasActivateResult, type SendSmsResult, type SmsTunnelConfig, SmsTunnelController, SmsTunnelModule, type SmsTunnelModuleAsyncOptions, type SmsTunnelModuleOptions, SmsTunnelService, type SmsTunnelStatus, type SmsTunnelStorageAdapter, type VerifyDeviceKeyResult, getSmsTunnelPublicPaths };
@@ -85,6 +85,22 @@ interface LegacyCallbackBody {
85
85
  device_name?: string;
86
86
  status?: string;
87
87
  }
88
+ interface DeviceE2EStatus {
89
+ encryptionEnabled: boolean;
90
+ hasPublicKey: boolean;
91
+ publicKeyFingerprint?: string;
92
+ keyCreatedAt?: string;
93
+ }
94
+ interface DevicePublicKeyInfo {
95
+ deviceId: string;
96
+ deviceName: string;
97
+ publicKey: string;
98
+ }
99
+ interface VerifyDeviceKeyResult {
100
+ valid: boolean;
101
+ currentFingerprint?: string;
102
+ needsRePairing: boolean;
103
+ }
88
104
 
89
105
  declare class SmsTunnelModule {
90
106
  static forRoot(options: SmsTunnelModuleOptions): DynamicModule;
@@ -169,6 +185,35 @@ declare class SmsTunnelService {
169
185
  * Get account usage stats.
170
186
  */
171
187
  getUsage(): Promise<any>;
188
+ /**
189
+ * Get E2E encryption status for a device.
190
+ */
191
+ getDeviceE2EStatus(deviceId: string): Promise<{
192
+ success: boolean;
193
+ data?: DeviceE2EStatus;
194
+ error?: string;
195
+ }>;
196
+ /**
197
+ * Get device public key for E2E encryption.
198
+ */
199
+ getDevicePublicKey(deviceId: string): Promise<{
200
+ success: boolean;
201
+ data?: DevicePublicKeyInfo;
202
+ error?: string;
203
+ }>;
204
+ /**
205
+ * Verify that a stored fingerprint matches the current device key.
206
+ */
207
+ verifyDeviceKey(deviceId: string, storedFingerprint: string): Promise<VerifyDeviceKeyResult>;
208
+ /**
209
+ * Send encrypted SMS via SMSTunnel API.
210
+ * The payload is encrypted client-side with the device's RSA public key.
211
+ */
212
+ sendEncryptedSms(encryptedPayload: string, deviceId: string, is2FA?: boolean): Promise<SendSmsResult>;
213
+ /**
214
+ * List pairings for the authenticated user.
215
+ */
216
+ getPairings(): Promise<any>;
172
217
  /** Make an authenticated GET request to SMSTunnel */
173
218
  private apiGet;
174
219
  /** Make an authenticated POST request to SMSTunnel */
@@ -268,6 +313,46 @@ declare class SmsTunnelController {
268
313
  * Route: GET /{prefix}/received
269
314
  */
270
315
  receivedSms(): Promise<any>;
316
+ /**
317
+ * Get E2E encryption status for a device.
318
+ * Route: GET /{prefix}/e2e-status/:deviceId
319
+ */
320
+ e2eStatus(deviceId: string): Promise<{
321
+ success: boolean;
322
+ data?: DeviceE2EStatus;
323
+ error?: string;
324
+ }>;
325
+ /**
326
+ * Get device public key for E2E encryption.
327
+ * Route: GET /{prefix}/public-key/:deviceId
328
+ */
329
+ publicKey(deviceId: string): Promise<{
330
+ success: boolean;
331
+ data?: DevicePublicKeyInfo;
332
+ error?: string;
333
+ }>;
334
+ /**
335
+ * Verify device key fingerprint.
336
+ * Route: POST /{prefix}/verify-key
337
+ */
338
+ verifyKey(body: {
339
+ deviceId: string;
340
+ fingerprint: string;
341
+ }): Promise<VerifyDeviceKeyResult>;
342
+ /**
343
+ * Send encrypted SMS.
344
+ * Route: POST /{prefix}/send-encrypted
345
+ */
346
+ sendEncrypted(body: {
347
+ encryptedPayload: string;
348
+ deviceId: string;
349
+ is2FA?: boolean;
350
+ }): Promise<SendSmsResult>;
351
+ /**
352
+ * List pairings.
353
+ * Route: GET /{prefix}/pairings
354
+ */
355
+ pairings(): Promise<any>;
271
356
  /**
272
357
  * List paired devices.
273
358
  * Route: GET /{prefix}/devices
@@ -295,4 +380,4 @@ declare const SMSTUNNEL_PUBLIC_PATHS: string[];
295
380
  */
296
381
  declare function getSmsTunnelPublicPaths(prefix: string): string[];
297
382
 
298
- export { type CreateTokenResult, type LegacyCallbackBody, type PairingCallbackBody, type PairingStatusResult, SMSTUNNEL_DEFAULT_PREFIX, SMSTUNNEL_OPTIONS, SMSTUNNEL_PUBLIC_PATHS, type SaasActivateResult, type SendSmsResult, type SmsTunnelConfig, SmsTunnelController, SmsTunnelModule, type SmsTunnelModuleAsyncOptions, type SmsTunnelModuleOptions, SmsTunnelService, type SmsTunnelStatus, type SmsTunnelStorageAdapter, getSmsTunnelPublicPaths };
383
+ export { type CreateTokenResult, type DeviceE2EStatus, type DevicePublicKeyInfo, type LegacyCallbackBody, type PairingCallbackBody, type PairingStatusResult, SMSTUNNEL_DEFAULT_PREFIX, SMSTUNNEL_OPTIONS, SMSTUNNEL_PUBLIC_PATHS, type SaasActivateResult, type SendSmsResult, type SmsTunnelConfig, SmsTunnelController, SmsTunnelModule, type SmsTunnelModuleAsyncOptions, type SmsTunnelModuleOptions, SmsTunnelService, type SmsTunnelStatus, type SmsTunnelStorageAdapter, type VerifyDeviceKeyResult, getSmsTunnelPublicPaths };
@@ -332,6 +332,56 @@ var SmsTunnelService = class {
332
332
  async getUsage() {
333
333
  return this.apiGet("/api/v1/account/usage");
334
334
  }
335
+ // ─── E2E Encryption ──────────────────────────────
336
+ /**
337
+ * Get E2E encryption status for a device.
338
+ */
339
+ async getDeviceE2EStatus(deviceId) {
340
+ return this.apiGet(`/api/v1/devices/${deviceId}/e2e-status`);
341
+ }
342
+ /**
343
+ * Get device public key for E2E encryption.
344
+ */
345
+ async getDevicePublicKey(deviceId) {
346
+ return this.apiGet(`/api/v1/devices/${deviceId}/public-key`);
347
+ }
348
+ /**
349
+ * Verify that a stored fingerprint matches the current device key.
350
+ */
351
+ async verifyDeviceKey(deviceId, storedFingerprint) {
352
+ const res = await this.getDeviceE2EStatus(deviceId);
353
+ if (!res.success || !res.data) {
354
+ return { valid: false, needsRePairing: true };
355
+ }
356
+ const status = res.data;
357
+ if (!status.hasPublicKey || !status.publicKeyFingerprint) {
358
+ return { valid: false, needsRePairing: true };
359
+ }
360
+ const matches = status.publicKeyFingerprint === storedFingerprint;
361
+ return {
362
+ valid: matches,
363
+ currentFingerprint: status.publicKeyFingerprint,
364
+ needsRePairing: !matches
365
+ };
366
+ }
367
+ /**
368
+ * Send encrypted SMS via SMSTunnel API.
369
+ * The payload is encrypted client-side with the device's RSA public key.
370
+ */
371
+ async sendEncryptedSms(encryptedPayload, deviceId, is2FA = false) {
372
+ return this.apiPost("/api/v1/sms/send", {
373
+ encrypted: true,
374
+ encryptedPayload,
375
+ deviceId,
376
+ is2FA
377
+ });
378
+ }
379
+ /**
380
+ * List pairings for the authenticated user.
381
+ */
382
+ async getPairings() {
383
+ return this.apiGet("/api/v1/pairings");
384
+ }
335
385
  // ─── Internal helpers ──────────────────────────────
336
386
  /** Make an authenticated GET request to SMSTunnel */
337
387
  async apiGet(path) {
@@ -439,6 +489,25 @@ var SmsTunnelController = class {
439
489
  async receivedSms() {
440
490
  return this.smsTunnelService.getReceivedSms();
441
491
  }
492
+ async e2eStatus(deviceId) {
493
+ return this.smsTunnelService.getDeviceE2EStatus(deviceId);
494
+ }
495
+ async publicKey(deviceId) {
496
+ return this.smsTunnelService.getDevicePublicKey(deviceId);
497
+ }
498
+ async verifyKey(body) {
499
+ return this.smsTunnelService.verifyDeviceKey(body.deviceId, body.fingerprint);
500
+ }
501
+ async sendEncrypted(body) {
502
+ return this.smsTunnelService.sendEncryptedSms(
503
+ body.encryptedPayload,
504
+ body.deviceId,
505
+ body.is2FA
506
+ );
507
+ }
508
+ async pairings() {
509
+ return this.smsTunnelService.getPairings();
510
+ }
442
511
  async devices() {
443
512
  return this.smsTunnelService.getDevices();
444
513
  }
@@ -493,6 +562,25 @@ __decorateClass([
493
562
  __decorateClass([
494
563
  (0, import_common2.Get)("smstunnel/received")
495
564
  ], SmsTunnelController.prototype, "receivedSms", 1);
565
+ __decorateClass([
566
+ (0, import_common2.Get)("smstunnel/e2e-status/:deviceId"),
567
+ __decorateParam(0, (0, import_common2.Param)("deviceId"))
568
+ ], SmsTunnelController.prototype, "e2eStatus", 1);
569
+ __decorateClass([
570
+ (0, import_common2.Get)("smstunnel/public-key/:deviceId"),
571
+ __decorateParam(0, (0, import_common2.Param)("deviceId"))
572
+ ], SmsTunnelController.prototype, "publicKey", 1);
573
+ __decorateClass([
574
+ (0, import_common2.Post)("smstunnel/verify-key"),
575
+ __decorateParam(0, (0, import_common2.Body)())
576
+ ], SmsTunnelController.prototype, "verifyKey", 1);
577
+ __decorateClass([
578
+ (0, import_common2.Post)("smstunnel/send-encrypted"),
579
+ __decorateParam(0, (0, import_common2.Body)())
580
+ ], SmsTunnelController.prototype, "sendEncrypted", 1);
581
+ __decorateClass([
582
+ (0, import_common2.Get)("smstunnel/pairings")
583
+ ], SmsTunnelController.prototype, "pairings", 1);
496
584
  __decorateClass([
497
585
  (0, import_common2.Get)("smstunnel/devices")
498
586
  ], SmsTunnelController.prototype, "devices", 1);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/index.ts","../../src/server/smstunnel.module.ts","../../src/server/smstunnel.service.ts","../../src/server/smstunnel.constants.ts","../../src/server/smstunnel.controller.ts"],"sourcesContent":["export { SmsTunnelModule } from './smstunnel.module';\r\nexport { SmsTunnelService } from './smstunnel.service';\r\nexport { SmsTunnelController } from './smstunnel.controller';\r\nexport {\r\n SMSTUNNEL_OPTIONS,\r\n SMSTUNNEL_PUBLIC_PATHS,\r\n SMSTUNNEL_DEFAULT_PREFIX,\r\n getSmsTunnelPublicPaths,\r\n} from './smstunnel.constants';\r\nexport type {\r\n SmsTunnelConfig,\r\n SmsTunnelStorageAdapter,\r\n SmsTunnelModuleOptions,\r\n SmsTunnelModuleAsyncOptions,\r\n SmsTunnelStatus,\r\n SendSmsResult,\r\n CreateTokenResult,\r\n PairingStatusResult,\r\n PairingCallbackBody,\r\n LegacyCallbackBody,\r\n SaasActivateResult,\r\n} from './smstunnel.interfaces';\r\n","import { DynamicModule, Module, Provider } from '@nestjs/common';\r\nimport { SmsTunnelService } from './smstunnel.service';\r\nimport { SmsTunnelController } from './smstunnel.controller';\r\nimport { SMSTUNNEL_OPTIONS } from './smstunnel.constants';\r\nimport type {\r\n SmsTunnelModuleOptions,\r\n SmsTunnelModuleAsyncOptions,\r\n} from './smstunnel.interfaces';\r\n\r\n@Module({})\r\nexport class SmsTunnelModule {\r\n static forRoot(options: SmsTunnelModuleOptions): DynamicModule {\r\n const optionsProvider: Provider = {\r\n provide: SMSTUNNEL_OPTIONS,\r\n useValue: options,\r\n };\r\n\r\n const controllers = [SmsTunnelController];\r\n if (!options.enableLegacyCallback) {\r\n // Legacy callback is always registered but won't match\r\n // if the consumer doesn't enable it - no-op\r\n }\r\n\r\n return {\r\n module: SmsTunnelModule,\r\n controllers,\r\n providers: [optionsProvider, SmsTunnelService],\r\n exports: [SmsTunnelService],\r\n global: false,\r\n };\r\n }\r\n\r\n static forRootAsync(asyncOptions: SmsTunnelModuleAsyncOptions): DynamicModule {\r\n const optionsProvider: Provider = {\r\n provide: SMSTUNNEL_OPTIONS,\r\n useFactory: asyncOptions.useFactory,\r\n inject: asyncOptions.inject || [],\r\n };\r\n\r\n return {\r\n module: SmsTunnelModule,\r\n controllers: [SmsTunnelController],\r\n providers: [optionsProvider, SmsTunnelService],\r\n exports: [SmsTunnelService],\r\n global: false,\r\n };\r\n }\r\n}\r\n","import { Inject, Injectable, Logger } from '@nestjs/common';\r\nimport { SMSTUNNEL_OPTIONS } from './smstunnel.constants';\r\nimport type {\r\n SmsTunnelModuleOptions,\r\n SmsTunnelStatus,\r\n SendSmsResult,\r\n CreateTokenResult,\r\n PairingStatusResult,\r\n PairingCallbackBody,\r\n SaasActivateResult,\r\n} 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":"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","names":["import_common","import_common"]}
1
+ {"version":3,"sources":["../../src/server/index.ts","../../src/server/smstunnel.module.ts","../../src/server/smstunnel.service.ts","../../src/server/smstunnel.constants.ts","../../src/server/smstunnel.controller.ts"],"sourcesContent":["export { SmsTunnelModule } from './smstunnel.module';\r\nexport { SmsTunnelService } from './smstunnel.service';\r\nexport { SmsTunnelController } from './smstunnel.controller';\r\nexport {\r\n SMSTUNNEL_OPTIONS,\r\n SMSTUNNEL_PUBLIC_PATHS,\r\n SMSTUNNEL_DEFAULT_PREFIX,\r\n getSmsTunnelPublicPaths,\r\n} from './smstunnel.constants';\r\nexport type {\r\n SmsTunnelConfig,\r\n SmsTunnelStorageAdapter,\r\n SmsTunnelModuleOptions,\r\n SmsTunnelModuleAsyncOptions,\r\n SmsTunnelStatus,\r\n SendSmsResult,\r\n CreateTokenResult,\r\n PairingStatusResult,\r\n PairingCallbackBody,\r\n LegacyCallbackBody,\r\n SaasActivateResult,\r\n DeviceE2EStatus,\r\n DevicePublicKeyInfo,\r\n VerifyDeviceKeyResult,\r\n} from './smstunnel.interfaces';\r\n","import { DynamicModule, Module, Provider } from '@nestjs/common';\r\nimport { SmsTunnelService } from './smstunnel.service';\r\nimport { SmsTunnelController } from './smstunnel.controller';\r\nimport { SMSTUNNEL_OPTIONS } from './smstunnel.constants';\r\nimport type {\r\n SmsTunnelModuleOptions,\r\n SmsTunnelModuleAsyncOptions,\r\n} from './smstunnel.interfaces';\r\n\r\n@Module({})\r\nexport class SmsTunnelModule {\r\n static forRoot(options: SmsTunnelModuleOptions): DynamicModule {\r\n const optionsProvider: Provider = {\r\n provide: SMSTUNNEL_OPTIONS,\r\n useValue: options,\r\n };\r\n\r\n const controllers = [SmsTunnelController];\r\n if (!options.enableLegacyCallback) {\r\n // Legacy callback is always registered but won't match\r\n // if the consumer doesn't enable it - no-op\r\n }\r\n\r\n return {\r\n module: SmsTunnelModule,\r\n controllers,\r\n providers: [optionsProvider, SmsTunnelService],\r\n exports: [SmsTunnelService],\r\n global: false,\r\n };\r\n }\r\n\r\n static forRootAsync(asyncOptions: SmsTunnelModuleAsyncOptions): DynamicModule {\r\n const optionsProvider: Provider = {\r\n provide: SMSTUNNEL_OPTIONS,\r\n useFactory: asyncOptions.useFactory,\r\n inject: asyncOptions.inject || [],\r\n };\r\n\r\n return {\r\n module: SmsTunnelModule,\r\n controllers: [SmsTunnelController],\r\n providers: [optionsProvider, SmsTunnelService],\r\n exports: [SmsTunnelService],\r\n global: false,\r\n };\r\n }\r\n}\r\n","import { Inject, Injectable, Logger } from '@nestjs/common';\r\nimport { SMSTUNNEL_OPTIONS } from './smstunnel.constants';\r\nimport type {\r\n SmsTunnelModuleOptions,\r\n SmsTunnelStatus,\r\n SendSmsResult,\r\n CreateTokenResult,\r\n PairingStatusResult,\r\n PairingCallbackBody,\r\n SaasActivateResult,\r\n DeviceE2EStatus,\r\n DevicePublicKeyInfo,\r\n VerifyDeviceKeyResult,\r\n} from './smstunnel.interfaces';\r\n\r\n@Injectable()\r\nexport class SmsTunnelService {\r\n private readonly logger = new Logger(SmsTunnelService.name);\r\n\r\n constructor(\r\n @Inject(SMSTUNNEL_OPTIONS)\r\n private readonly options: SmsTunnelModuleOptions,\r\n ) {}\r\n\r\n /**\r\n * Get current pairing status.\r\n */\r\n async getStatus(): Promise<SmsTunnelStatus> {\r\n const config = await this.options.storage.getConfig();\r\n return {\r\n paired: !!config.apiKey,\r\n serverUrl: config.serverUrl,\r\n deviceName: config.deviceName,\r\n };\r\n }\r\n\r\n /**\r\n * Create a pairing token on SMSTunnel server.\r\n * Uses Enterprise/SaaS flow when enterpriseApiKey is configured,\r\n * otherwise falls back to public-create flow.\r\n */\r\n async createPairingToken(): Promise<CreateTokenResult> {\r\n const config = await this.options.storage.getConfig();\r\n if (!config.serverUrl) {\r\n return { success: false, error: 'SMSTunnel server URL is not configured.' };\r\n }\r\n\r\n const serverUrl = config.serverUrl.replace(/\\/$/, '');\r\n\r\n if (this.options.enterpriseApiKey) {\r\n return this.createPairingTokenEnterprise(serverUrl);\r\n }\r\n\r\n return this.createPairingTokenPublic(serverUrl);\r\n }\r\n\r\n /**\r\n * Public flow: POST /api/v1/pairing/public-create\r\n */\r\n private async createPairingTokenPublic(serverUrl: string): Promise<CreateTokenResult> {\r\n const prefix = 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":["import_common","import_common"]}
@@ -303,6 +303,56 @@ var SmsTunnelService = class {
303
303
  async getUsage() {
304
304
  return this.apiGet("/api/v1/account/usage");
305
305
  }
306
+ // ─── E2E Encryption ──────────────────────────────
307
+ /**
308
+ * Get E2E encryption status for a device.
309
+ */
310
+ async getDeviceE2EStatus(deviceId) {
311
+ return this.apiGet(`/api/v1/devices/${deviceId}/e2e-status`);
312
+ }
313
+ /**
314
+ * Get device public key for E2E encryption.
315
+ */
316
+ async getDevicePublicKey(deviceId) {
317
+ return this.apiGet(`/api/v1/devices/${deviceId}/public-key`);
318
+ }
319
+ /**
320
+ * Verify that a stored fingerprint matches the current device key.
321
+ */
322
+ async verifyDeviceKey(deviceId, storedFingerprint) {
323
+ const res = await this.getDeviceE2EStatus(deviceId);
324
+ if (!res.success || !res.data) {
325
+ return { valid: false, needsRePairing: true };
326
+ }
327
+ const status = res.data;
328
+ if (!status.hasPublicKey || !status.publicKeyFingerprint) {
329
+ return { valid: false, needsRePairing: true };
330
+ }
331
+ const matches = status.publicKeyFingerprint === storedFingerprint;
332
+ return {
333
+ valid: matches,
334
+ currentFingerprint: status.publicKeyFingerprint,
335
+ needsRePairing: !matches
336
+ };
337
+ }
338
+ /**
339
+ * Send encrypted SMS via SMSTunnel API.
340
+ * The payload is encrypted client-side with the device's RSA public key.
341
+ */
342
+ async sendEncryptedSms(encryptedPayload, deviceId, is2FA = false) {
343
+ return this.apiPost("/api/v1/sms/send", {
344
+ encrypted: true,
345
+ encryptedPayload,
346
+ deviceId,
347
+ is2FA
348
+ });
349
+ }
350
+ /**
351
+ * List pairings for the authenticated user.
352
+ */
353
+ async getPairings() {
354
+ return this.apiGet("/api/v1/pairings");
355
+ }
306
356
  // ─── Internal helpers ──────────────────────────────
307
357
  /** Make an authenticated GET request to SMSTunnel */
308
358
  async apiGet(path) {
@@ -418,6 +468,25 @@ var SmsTunnelController = class {
418
468
  async receivedSms() {
419
469
  return this.smsTunnelService.getReceivedSms();
420
470
  }
471
+ async e2eStatus(deviceId) {
472
+ return this.smsTunnelService.getDeviceE2EStatus(deviceId);
473
+ }
474
+ async publicKey(deviceId) {
475
+ return this.smsTunnelService.getDevicePublicKey(deviceId);
476
+ }
477
+ async verifyKey(body) {
478
+ return this.smsTunnelService.verifyDeviceKey(body.deviceId, body.fingerprint);
479
+ }
480
+ async sendEncrypted(body) {
481
+ return this.smsTunnelService.sendEncryptedSms(
482
+ body.encryptedPayload,
483
+ body.deviceId,
484
+ body.is2FA
485
+ );
486
+ }
487
+ async pairings() {
488
+ return this.smsTunnelService.getPairings();
489
+ }
421
490
  async devices() {
422
491
  return this.smsTunnelService.getDevices();
423
492
  }
@@ -472,6 +541,25 @@ __decorateClass([
472
541
  __decorateClass([
473
542
  Get("smstunnel/received")
474
543
  ], SmsTunnelController.prototype, "receivedSms", 1);
544
+ __decorateClass([
545
+ Get("smstunnel/e2e-status/:deviceId"),
546
+ __decorateParam(0, Param("deviceId"))
547
+ ], SmsTunnelController.prototype, "e2eStatus", 1);
548
+ __decorateClass([
549
+ Get("smstunnel/public-key/:deviceId"),
550
+ __decorateParam(0, Param("deviceId"))
551
+ ], SmsTunnelController.prototype, "publicKey", 1);
552
+ __decorateClass([
553
+ Post("smstunnel/verify-key"),
554
+ __decorateParam(0, Body())
555
+ ], SmsTunnelController.prototype, "verifyKey", 1);
556
+ __decorateClass([
557
+ Post("smstunnel/send-encrypted"),
558
+ __decorateParam(0, Body())
559
+ ], SmsTunnelController.prototype, "sendEncrypted", 1);
560
+ __decorateClass([
561
+ Get("smstunnel/pairings")
562
+ ], SmsTunnelController.prototype, "pairings", 1);
475
563
  __decorateClass([
476
564
  Get("smstunnel/devices")
477
565
  ], SmsTunnelController.prototype, "devices", 1);