@ -19,6 +19,10 @@ export interface SmsBalanceRow {
balanceText : string | null ; balance : number | null ; low : boolean
reseller : string | null ; status : string
provider : string | null ; hasCreds : boolean
/** Gateway login — username always; password only when the viewer is managerial (D32 plaintext). */
username : string | null ; password : string | null
/** Installation date from the SMS field values (D21), if recorded. */
installationDate : string | null ; phone : string | null
/** Last gateway poll (D28): 'ok' | 'error' | null (never polled). */
checkStatus : string | null ; checkMessage : string | null ; checkedAt : string | null
}
@ -40,17 +44,24 @@ export async function smsLowCount(db: DB): Promise<{ low: number; threshold: num
return { low : b.lowCount , threshold : b.threshold }
}
export async function smsBalances ( db : DB ) : Promise < SmsBalances > {
/ * *
* SMS credit review . ` includeSecrets ` ( set for managerial viewers at the route ) decrypts and
* returns each account ' s gateway password ( D32 : plaintext ) so it can be shown and downloaded ;
* otherwise password stays null . Username , installation date and phone come along for the
* per - client detail the SMS screen shows .
* /
export async function smsBalances ( db : DB , includeSecrets = false ) : Promise < SmsBalances > {
const threshold = await getNumberSetting ( db , 'sms.low_balance_threshold' , 10000 )
const mod = await db . get < { id : string } > ( ` SELECT id FROM module WHERE code='SMS' ` )
if ( mod === undefined ) return { rows : [ ] , threshold , total : 0 , lowCount : 0 , unknownCount : 0 }
const raw = await db . all < {
cm_id : string ; client_id : string ; client_name : string ; client_code : string
field_values : string ; status : string ; provider : string | null ; username : string | null
password_enc : string | null
check_status : string | null ; check_message : string | null ; checked_at : string | null
} > (
` SELECT cm.id AS cm_id, cm.client_id, c.name AS client_name, c.code AS client_code,
cm . field_values , cm . status , cm . provider , cm . username ,
cm . field_values , cm . status , cm . provider , cm . username , cm . password_enc ,
s . status AS check_status , s . message AS check_message , s . checked_at
FROM client_module cm
JOIN client c ON c . id = cm . client_id
@ -63,13 +74,22 @@ export async function smsBalances(db: DB): Promise<SmsBalances> {
try { fv = JSON . parse ( r . field_values ) as Record < string , unknown > } catch { fv = { } }
const balanceText = typeof fv . sms_balance === 'string' && fv . sms_balance . trim ( ) !== '' ? fv . sms_balance . trim ( ) : null
const balance = parseBalance ( balanceText )
const strField = ( k : string ) : string | null = >
typeof fv [ k ] === 'string' && ( fv [ k ] as string ) . trim ( ) !== '' ? ( fv [ k ] as string ) . trim ( ) : null
let password : string | null = null
if ( includeSecrets && r . password_enc !== null ) {
try { password = decrypt ( r . password_enc , '' ) } catch { password = null }
}
return {
cmId : r.cm_id , clientId : r.client_id , clientName : r.client_name , clientCode : r.client_code ,
balanceText , balance , low : balance !== null && balance < threshold ,
reseller : typeof fv . reseller === 'string' && fv . reseller . trim ( ) !== '' ? fv . reseller . trim ( ) : null ,
reseller : strField( 'reseller' ) ,
status : r.status ,
provider : r.provider !== null && r . provider !== '' ? r.provider : null ,
hasCreds : r.username !== null && r . username !== '' ,
username : r.username !== null && r . username !== '' ? r.username : null ,
password ,
installationDate : strField ( 'installation_date' ) , phone : strField ( 'phone' ) ,
checkStatus : r.check_status , checkMessage : r.check_message , checkedAt : r.checked_at ,
}
} )