Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
346 changes: 343 additions & 3 deletions src/controllers/phone_number_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@ import { getConfig } from '../services/config'
import { SessionStore } from '../services/session_store'
import logger from '../services/logger'
import { getAuthHeaderToken } from '../services/security'
import { UNOAPI_AUTH_TOKEN } from '../defaults'
import { UNOAPI_AUTH_TOKEN , WABA_ID , BUSINESS_ID } from '../defaults'
import { setConfig } from '../services/redis'
import { Reload } from '../services/reload'

export class PhoneNumberController {
private getConfig: getConfig
private sessionStore: SessionStore
private reload: Reload

constructor(getConfig: getConfig, sessionStore: SessionStore) {
constructor(getConfig: getConfig, sessionStore: SessionStore, reload: Reload) {
this.getConfig = getConfig
this.sessionStore = sessionStore
this.reload = reload
}

public async get(req: Request, res: Response) {
Expand Down Expand Up @@ -66,4 +70,340 @@ export class PhoneNumberController {
return res.status(500).json({ status: 'error', message: e.message })
}
}
}

public async accounts(req: Request, res: Response) {
logger.debug('accounts method %s', req.method)
logger.debug('headers %s', JSON.stringify(req.headers))
logger.debug('params %s', JSON.stringify(req.params))
logger.debug('query %s', JSON.stringify(req.query))
logger.debug('PATH %s', JSON.stringify(req.path))
try {
const token = getAuthHeaderToken(req)
const { fields } = req.query

if (fields && (fields as string).split(',').includes('owned_whatsapp_business_accounts') || req.path.endsWith('/owned_whatsapp_business_accounts')) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix operator precedence in condition.

The condition has a precedence issue. The || operator has lower precedence than the method call, so the current logic evaluates as (fields && fields.split(...).includes(...)) || (req.path.endsWith(...)), but the split could fail if fields is not a string.

Apply this diff:

-      if (fields && (fields as string).split(',').includes('owned_whatsapp_business_accounts') || req.path.endsWith('/owned_whatsapp_business_accounts')) {
+      if ((fields && (fields as string).split(',').includes('owned_whatsapp_business_accounts')) || req.path.endsWith('/owned_whatsapp_business_accounts')) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (fields && (fields as string).split(',').includes('owned_whatsapp_business_accounts') || req.path.endsWith('/owned_whatsapp_business_accounts')) {
if ((fields && (fields as string).split(',').includes('owned_whatsapp_business_accounts')) || req.path.endsWith('/owned_whatsapp_business_accounts')) {
🤖 Prompt for AI Agents
In src/controllers/phone_number_controller.ts around line 84, the conditional
suffers from operator precedence and a potential runtime error when calling
split on a non-string; update it to explicitly check that fields is a string
before calling split and group the left side with parentheses so the overall
expression is ((typeof fields === 'string' && (fields as
string).split(',').includes('owned_whatsapp_business_accounts')) ||
req.path.endsWith('/owned_whatsapp_business_accounts')). Ensure the typeof check
comes first to prevent split on non-strings and add parentheses to make the
intended precedence explicit.

logger.debug('Processing owned_whatsapp_business_accounts request')
const phones = await this.sessionStore.getPhones()
const accounts: any[] = []

for (let i = 0; i < phones.length; i++) {
const number = phones[i]
const config = await this.getConfig(number)

if (token === UNOAPI_AUTH_TOKEN || token === config.authToken) {
accounts.push({ id: WABA_ID }) // id = número
break
}
}

// Retorna no formato Graph API
return res.status(200).json({
id: WABA_ID,
name: "UNOAPI WABA ACCOUNT",
timezone_id: "America/Sao_Paulo",
owned_whatsapp_business_accounts: {
data: accounts,
},
})

}
} catch (e) {
return res.status(500).json({ status: 'error', message: e.message })
}
}


public async debugToken(req: Request, res: Response) {
logger.debug('debug_token method %s', req.method)
logger.debug('headers %s', JSON.stringify(req.headers))
logger.debug('params %s', JSON.stringify(req.params))
logger.debug('query %s', JSON.stringify(req.query))

const inputToken = req.query.input_token as string
const accessToken = req.query.access_token as string

try {
if (!inputToken || !accessToken) {
return res.status(400).json({ error: 'Missing input_token or access_token' })
}

const phones = await this.sessionStore.getPhones()
let isValid = false
let matchedConfig: any = null
let appId: any
let label: any

for (let i = 0; i < phones.length; i++) {
const phone = phones[i]
const config = await this.getConfig(phone)

if (inputToken === config.authToken && accessToken === config.authToken) {
isValid = true
matchedConfig = config
label = config.label
appId = phone
break
}
}

return res.status(200).json({
data: {
app_id: appId.replace('+', '') || "APP_ID_NOT_FOUND",
type: "USER",
application: "UNOAPI",
data_access_expires_at: Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 365, // +1 ano
expires_at: Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 425, // +1 ano e 1 mês
is_valid: isValid,
issued_at: Math.floor(Date.now() / 1000) - (60 * 60 * 24), // há 1 dia
scopes: [
"whatsapp_business_management",
"whatsapp_business_messaging",
"business_management",
"whatsapp_business_manage_events"
],
user_id: matchedConfig ? appId.replace('+', '') || "single_user" : "global_user"
}
})
} catch (e: any) {
logger.error('Error in debug_token: ' + e.message)
return res.status(500).json({ status: 'error', message: e.message })
}
}

public async waba(req: Request, res: Response) {
logger.debug('headers %s', JSON.stringify(req.headers))
logger.debug('params %s', JSON.stringify(req.params))
logger.debug('query %s', JSON.stringify(req.query))
logger.debug('path %s', req.path)

try {
const token = getAuthHeaderToken(req)
const { waba_id } = req.params
const phones = await this.sessionStore.getPhones()
const foundConfigs: any[] = []

for (const number of phones) {
const config = await this.getConfig(number)
if (token === config.authToken) {
foundConfigs.push({ ...config, number })
}
}

if (foundConfigs.length === 0) {
return res.status(403).json({ error: 'Invalid or unauthorized token' })
}
Comment on lines +173 to +194
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Validate waba_id parameter against WABA_ID constant.

The method extracts waba_id from req.params (line 181) but never validates that it matches the expected WABA_ID constant. This could allow requests to arbitrary WABA IDs to succeed as long as the token is valid.

Consider adding validation after line 181:

if (waba_id !== WABA_ID) {
  return res.status(404).json({ error: 'WABA ID not found' })
}
🤖 Prompt for AI Agents
In src/controllers/phone_number_controller.ts around lines 173 to 194, after
extracting waba_id from req.params (line ~181) add a validation that compares
waba_id to the WABA_ID constant and if they don't match return a 404 JSON
response; specifically, insert a check that if (waba_id !== WABA_ID) then return
res.status(404).json({ error: 'WABA ID not found' }) so only requests for the
expected WABA ID proceed.


let response: any = {}

// 🔹 Caso: rota de listagem de números
if (req.path.endsWith('/phone_numbers')) {
logger.debug('Processing PHONE_NUMBERS request')

const numbersData = foundConfigs.map(cfg => {
const webhookUrl =
Array.isArray(cfg.webhooks) && cfg.webhooks.length > 0
? cfg.webhooks[0].urlAbsolute
: null

return {
id: cfg.number.replace('+', ''),
display_phone_number: cfg.number.replace('+', ''),
verified_name: String(cfg.label || 'WhatsApp Account').toUpperCase(),
quality_rating: 'GREEN',
code_verification_status: 'VERIFIED',
new_name_status: 'APPROVED',
is_pin_enabled: false,
throughput: {level:'TIER_1'},
platform_type: 'BUSINESS',
status: 'CONNECTED',
webhook_configuration: {application: webhookUrl ? webhookUrl: ''},
}
})

response = { data: numbersData }
}

// 🔹 Caso: informações gerais do WABA
else {
logger.debug('Processing generic WABA info request')
const firstCfg = foundConfigs[0]

response = {
id: waba_id,
name: String(firstCfg.label || 'UNOAPI WABA ACCOUNT').toUpperCase(),
currency: 'BRL',
timezone_id: 42,
message_template_namespace: firstCfg.label.slice(0, 10),
primary_business_location: { country: 'BR' },
creation_time: new Date().toISOString(),
tier: 'TIER_1',
}
}
Comment on lines +227 to +241
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add null safety for label access.

Line 236 accesses firstCfg.label.slice(0, 10) which could throw if label is undefined or null, even though line 233 has a fallback.

Apply this diff:

-          message_template_namespace: firstCfg.label.slice(0, 10),
+          message_template_namespace: (firstCfg.label || 'UNOAPI').slice(0, 10),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
else {
logger.debug('Processing generic WABA info request')
const firstCfg = foundConfigs[0]
response = {
id: waba_id,
name: String(firstCfg.label || 'UNOAPI WABA ACCOUNT').toUpperCase(),
currency: 'BRL',
timezone_id: 42,
message_template_namespace: firstCfg.label.slice(0, 10),
primary_business_location: { country: 'BR' },
creation_time: new Date().toISOString(),
tier: 'TIER_1',
}
}
else {
logger.debug('Processing generic WABA info request')
const firstCfg = foundConfigs[0]
response = {
id: waba_id,
name: String(firstCfg.label || 'UNOAPI WABA ACCOUNT').toUpperCase(),
currency: 'BRL',
timezone_id: 42,
message_template_namespace: (firstCfg.label || 'UNOAPI').slice(0, 10),
primary_business_location: { country: 'BR' },
creation_time: new Date().toISOString(),
tier: 'TIER_1',
}
}
🤖 Prompt for AI Agents
In src/controllers/phone_number_controller.ts around lines 227 to 241, the code
uses firstCfg.label.slice(0, 10) which can throw if label is null/undefined;
change it to safely derive the namespace by coercing label to string with a
fallback before slicing (e.g., use (String(firstCfg.label || 'UNOAPI WABA
ACCOUNT')).slice(0, 10) or optional chaining with a default) so the slice call
never receives null/undefined and preserves the existing fallback behavior.


return res.status(200).json(response)
} catch (e: any) {
logger.error('Erro em WABA:', e)
return res.status(500).json({ status: 'error', message: e.message })
}
}


public async getsubs(req: Request, res: Response) {
logger.debug('get subscriptions -> headers %s', JSON.stringify(req.headers))
logger.debug('get subscriptions -> params %s', JSON.stringify(req.params))
logger.debug('get subscriptions -> query %s', JSON.stringify(req.query))
logger.debug('get subscriptions -> path %s', req.path)

try {
const { phone } = req.params
const accessToken = req.query.access_token as string

// Verificação básica
if (!accessToken || !accessToken.includes('|')) {
return res.status(400).json({ error: 'Access token inválido ou ausente' })
}

const [appId, userToken] = accessToken.split('|')

if (appId !== phone) {
return res.status(403).json({ error: 'appId não corresponde ao número de telefone' })
}
Comment on lines +262 to +270
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add safer token parsing with error handling.

The token split at line 266 assumes exactly two parts, but doesn't handle cases where the token might contain multiple pipe characters or be malformed in other ways.

Apply this diff:

       // Verificação básica
       if (!accessToken || !accessToken.includes('|')) {
         return res.status(400).json({ error: 'Access token inválido ou ausente' })
       }
 
       const [appId, userToken] = accessToken.split('|')
+      
+      if (!appId || !userToken) {
+        return res.status(400).json({ error: 'Access token mal formatado' })
+      }
 
       if (appId !== phone) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!accessToken || !accessToken.includes('|')) {
return res.status(400).json({ error: 'Access token inválido ou ausente' })
}
const [appId, userToken] = accessToken.split('|')
if (appId !== phone) {
return res.status(403).json({ error: 'appId não corresponde ao número de telefone' })
}
if (!accessToken || !accessToken.includes('|')) {
return res.status(400).json({ error: 'Access token inválido ou ausente' })
}
const [appId, userToken] = accessToken.split('|')
if (!appId || !userToken) {
return res.status(400).json({ error: 'Access token mal formatado' })
}
if (appId !== phone) {
return res.status(403).json({ error: 'appId não corresponde ao número de telefone' })
}
🤖 Prompt for AI Agents
In src/controllers/phone_number_controller.ts around lines 262 to 270, the
current accessToken parsing assumes exactly one '|' and does a blind split;
change it to robustly validate and extract the two parts: first check
accessToken exists and contains a '|' then split with a limit of 2 (or use
indexOf to split into left/right only), trim both parts, verify neither part is
empty, and only then assign appId and userToken; if any validation fails return
a 400 with a clear error; keep the existing appId !== phone check and return 403
as before.


// Obter a configuração do número
const config = await this.getConfig(phone)
if (!config) {
return res.status(404).json({ error: 'Número não encontrado' })
}

// Validar token
if (userToken !== config.authToken && userToken !== UNOAPI_AUTH_TOKEN) {
return res.status(403).json({ error: 'Token inválido para este número' })
}

// Procurar webhook da Meta
const webhooks = config.webhooks || []
const metaWebhook = webhooks.find((w: any) => w.id === 'metaId')

// Se não houver webhook "metaId", retorna vazio
if (!metaWebhook) {
logger.debug(`Nenhum webhook metaId encontrado para ${phone}`)
return res.status(200).json({ data: [] })
}

// Montar resposta no formato esperado da Graph API
const response = {
data: [
{
object: 'whatsapp_business_account',
id: metaWebhook.id,
callback_url: metaWebhook.urlAbsolute,
fields: [{name:"UNOAPI",version:"20.0"}],
active: true,
verification_token: metaWebhook.token,
webhook_format: 'json'
}
]
}

return res.status(200).json(response)

} catch (e: any) {
logger.error('Erro em getsubs:', e)
return res.status(500).json({ status: 'error', message: e.message })
}
}

public async addsubs(req: Request, res: Response) {
logger.info('POST SUBS -> headers %s', JSON.stringify(req.headers))
logger.info('POST SUBS -> params %s', JSON.stringify(req.params))
logger.info('POST SUBS -> body %s', JSON.stringify(req.body))
logger.info('POST SUBS -> query %s', JSON.stringify(req.query))
logger.info('POST SUBS -> path %s', req.path)

try {
const { phone } = req.params
const config = await this.getConfig(phone)
const accessToken = req.query.access_token as string
const body = req.body || {}
const [appId, userToken] = accessToken.split('|')

// --- Validações ---
if (!config) {
return res.status(404).json({ error: 'Número não encontrado' })
}
if (!accessToken || !accessToken.includes('|')) {
return res.status(400).json({ error: 'Access token inválido ou ausente' })
}
if (appId !== phone || userToken !== config.authToken) {
return res.status(403).json({ error: 'appId ou Token não corresponde' })
}
Comment on lines +326 to +339
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add safer token parsing and align validation with getsubs.

Two issues:

  1. The token split at line 328 lacks validation (same issue as in getsubs).
  2. The token validation at line 337 is stricter than in getsubs (line 279), which also accepts UNOAPI_AUTH_TOKEN. This inconsistency could cause unexpected behavior.

Apply this diff:

       const accessToken = req.query.access_token as string
       const body = req.body || {}
-      const [appId, userToken] = accessToken.split('|')
 
       // --- Validações ---
       if (!config) {
         return res.status(404).json({ error: 'Número não encontrado' })
       }
       if (!accessToken || !accessToken.includes('|')) {
         return res.status(400).json({ error: 'Access token inválido ou ausente' })
       }
-      if (appId !== phone || userToken !== config.authToken) {
+      
+      const [appId, userToken] = accessToken.split('|')
+      if (!appId || !userToken) {
+        return res.status(400).json({ error: 'Access token mal formatado' })
+      }
+      if (appId !== phone || (userToken !== config.authToken && userToken !== UNOAPI_AUTH_TOKEN)) {
         return res.status(403).json({ error: 'appId ou Token não corresponde' })
       }
🤖 Prompt for AI Agents
In src/controllers/phone_number_controller.ts around lines 326 to 339, the
access token is split without checking it first and the validation rejects
tokens that would be accepted by getsubs; first ensure access_token exists and
contains '|' before calling split, parse into two parts safely (limit split to
two parts), and validate that appId matches phone and that userToken equals
either config.authToken or process.env.UNOAPI_AUTH_TOKEN to match getsubs
behavior; return the same error codes/messages already used, keeping the config
existence check and token-format check ordered so you never split undefined.


const {
object,
fields,
callback_url,
verify_token
} = body

if (!callback_url || !verify_token) {
return res.status(400).json({ error: 'callback_url e verify_token são obrigatórios' })
}

// --- 🔄 criar ou atualizar webhook "metaId" ---
const newWebhook = {
sendNewMessages: true,
id: 'metaId',
urlAbsolute: callback_url,
token: verify_token,
header: 'Authorization',
sendGroupMessages: false,
sendNewsletterMessages: false,
sendOutgoingMessages: true,
sendUpdateMessages: true,
sendIncomingMessages: true,
sendTranscribeAudio: false,
url:"http://localhost:9876/webhooks/fake",
addToBlackListOnOutgoingMessageWithTtl:0,
timeoutMs:360000
}
Comment on lines +353 to +368
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove hard-coded localhost URL and make webhook configuration more flexible.

Line 365 contains a hard-coded url: "http://localhost:9876/webhooks/fake" which appears incorrect for a production webhook subscription. Additionally, many webhook settings are hard-coded and might need to be configurable based on the subscription request.

At minimum, apply this diff to remove the hard-coded URL:

       const newWebhook = {
         sendNewMessages: true,
         id: 'metaId',
         urlAbsolute: callback_url,
         token: verify_token,
         header: 'Authorization',
         sendGroupMessages: false,
         sendNewsletterMessages: false,
         sendOutgoingMessages: true,
         sendUpdateMessages: true,
         sendIncomingMessages: true,
         sendTranscribeAudio: false,
-        url:"http://localhost:9876/webhooks/fake",
         addToBlackListOnOutgoingMessageWithTtl:0,
         timeoutMs:360000
       }

Consider whether other hard-coded boolean flags should be derived from the fields parameter in the request body.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const newWebhook = {
sendNewMessages: true,
id: 'metaId',
urlAbsolute: callback_url,
token: verify_token,
header: 'Authorization',
sendGroupMessages: false,
sendNewsletterMessages: false,
sendOutgoingMessages: true,
sendUpdateMessages: true,
sendIncomingMessages: true,
sendTranscribeAudio: false,
url:"http://localhost:9876/webhooks/fake",
addToBlackListOnOutgoingMessageWithTtl:0,
timeoutMs:360000
}
const newWebhook = {
sendNewMessages: true,
id: 'metaId',
urlAbsolute: callback_url,
token: verify_token,
header: 'Authorization',
sendGroupMessages: false,
sendNewsletterMessages: false,
sendOutgoingMessages: true,
sendUpdateMessages: true,
sendIncomingMessages: true,
sendTranscribeAudio: false,
addToBlackListOnOutgoingMessageWithTtl:0,
timeoutMs:360000
}
🤖 Prompt for AI Agents
In src/controllers/phone_number_controller.ts around lines 353 to 368, remove
the hard-coded url ("http://localhost:9876/webhooks/fake") and replace it with
the incoming callback_url (already present) so the webhook points to the
request-supplied endpoint; also stop hard-coding boolean flags and timeout by
reading them from the request body (e.g., a fields or options object) with
sensible default fallbacks (keep current true/false values if the request does
not supply them) so webhook configuration is flexible for production and tests.
Ensure id/token/header remain set from existing variables, validate/normalize
the incoming values (types and allowed ranges like timeoutMs), and keep backward
compatibility by defaulting to the current values when fields are absent.


const webhooks = config.webhooks || []
const index = webhooks.findIndex((w: any) => w.id === 'metaId')

if (index >= 0) {
webhooks[index] = { ...webhooks[index], ...newWebhook }
logger.info(`Webhook metaId atualizado para ${phone}`)

} else {
webhooks.push(newWebhook)
logger.info(`Webhook metaId adicionado para ${phone}`)
}
// Merge config
const mergedConfig = {
...config,
webhooks,
}
logger.info('POST SUBS -> MERGED CONFIG ---------------- %s', JSON.stringify(mergedConfig))

// save config updated
await setConfig(phone, mergedConfig)
this.reload.run(phone)

return res.status(200).json({
success: true,
data: {
object: object || 'whatsapp_business_account',
callback_url,
fields: fields || 'messages',
active: true,
verify_token,
}
})

} catch (e: any) {
logger.error('Erro em addsubs:', e)
return res.status(500).json({ status: 'error', message: e.message })
}
}

}
2 changes: 2 additions & 0 deletions src/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ export const WEBHOOK_SESSION = process.env.WEBHOOK_SESSION || ''
export const AMQP_URL = process.env.AMQP_URL || 'amqp://guest:guest@localhost:5672'
export const REDIS_URL = process.env.REDIS_URL || 'redis://localhost:6379'
export const PROXY_URL = process.env.PROXY_URL
export const BUSINESS_ID = process.env.FB_ID || 'FB_UNOAPI'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Consider aligning the environment variable name with the constant name.

The environment variable FB_ID maps to the constant BUSINESS_ID, which could confuse developers during configuration. Additionally, the default value 'FB_UNOAPI' appears to be a placeholder. Since this constant is used in route patterns (per the PR summary), ensure that either:

  • The default is appropriate for development/testing, or
  • Validation is added to require this environment variable in production

Consider renaming the environment variable to BUSINESS_ID for consistency, or add a comment explaining the naming:

-export const BUSINESS_ID = process.env.FB_ID || 'FB_UNOAPI'
+// Maps to FB_ID environment variable for backward compatibility
+export const BUSINESS_ID = process.env.FB_ID || 'FB_UNOAPI'

To verify whether this default value is validated elsewhere in the codebase:


🏁 Script executed:

#!/bin/bash
# Search for validation or usage of BUSINESS_ID to confirm if the default is safe
rg -n --type=ts -C3 'BUSINESS_ID' -g '!src/defaults.ts'

Length of output: 1666




🏁 Script executed:

#!/bin/bash
# Search for validation of these environment variables or constants
rg -n --type=ts 'FB_ID|validate|process.env' src/defaults.ts -A 2 -B 2

Length of output: 13763


🏁 Script executed:

#!/bin/bash
# Search for any validation or checks on BUSINESS_ID or WABA_ID values in the entire codebase
rg -n --type=ts '(BUSINESS_ID|WABA_ID|FB_ID)' -C 2 | rg -i 'validate|check|if.*BUSINESS_ID|if.*WABA_ID|if.*FB_ID' -C 3

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Check if there's a config validation file or startup validation
fd -e ts -g '*valid*' -g '*config*' -g '*startup*' -g '*init*' | head -20

Length of output: 204


🏁 Script executed:

#!/bin/bash
# Look for comments explaining BUSINESS_ID usage
rg -n --type=ts 'BUSINESS_ID.*COMPATIBILITY|BUSINESS.*ID' -B 3 -A 3

Length of output: 4612


🏁 Script executed:

#!/bin/bash
# Check the entire defaults.ts file to understand the context and patterns
cat src/defaults.ts

Length of output: 15456


Add a comment explaining the naming mismatch and compatibility route purpose.

The environment variable FB_ID maps to the constant BUSINESS_ID due to a naming inconsistency, which could confuse developers during configuration. The defaults are used as literal route identifiers in compatibility routes (per the router comments), so they serve a functional purpose—but this design should be documented.

Suggested change:

-export const BUSINESS_ID = process.env.FB_ID || 'FB_UNOAPI'
-export const WABA_ID = process.env.WABA_ID || 'WABA_UNOAPI'
+// Routes identifiers for backward compatibility; FB_ID env var controls BUSINESS_ID for legacy naming reasons
+export const BUSINESS_ID = process.env.FB_ID || 'FB_UNOAPI'
+export const WABA_ID = process.env.WABA_ID || 'WABA_UNOAPI'
🤖 Prompt for AI Agents
In src/defaults.ts around line 70, add an inline comment above the BUSINESS_ID
export that explains the naming mismatch (the environment var FB_ID is mapped to
the BUSINESS_ID constant for backwards compatibility) and state that this
constant is used as a literal route identifier in compatibility routes; mention
the expected env var name (FB_ID) and the default value ('FB_UNOAPI') so future
developers understand why the mapping exists and that changing the constant
affects route behavior.

export const WABA_ID = process.env.WABA_ID || 'WABA_UNOAPI'

// behavior of unoapi
export const UNOAPI_SERVER_NAME = process.env.UNOAPI_SERVER_NAME || 'server_1'
Expand Down
Loading