1+ #!/usr/bin/env node
2+
3+ const net = require ( 'net' ) ;
4+ const readline = require ( 'readline' ) ;
5+
6+ const SOCKET_PATH = process . env . SBC_SOCKET_PATH || '/tmp/sbc-sip-sidecar.sock' ;
7+
8+ function sendCommand ( action , key , value , item , server ) {
9+ return new Promise ( ( resolve , reject ) => {
10+ const socket = net . createConnection ( SOCKET_PATH ) ;
11+
12+ socket . on ( 'connect' , ( ) => {
13+ const cmd = { action } ;
14+ if ( key ) cmd . key = key ;
15+ if ( value ) cmd . value = value ;
16+ if ( item ) cmd . item = item ;
17+ if ( server ) cmd . server = server ;
18+
19+ socket . write ( JSON . stringify ( cmd ) + '\n' ) ;
20+ } ) ;
21+
22+ socket . on ( 'data' , ( data ) => {
23+ try {
24+ const response = JSON . parse ( data . toString ( ) . trim ( ) ) ;
25+ socket . end ( ) ;
26+ resolve ( response ) ;
27+ } catch ( err ) {
28+ socket . end ( ) ;
29+ reject ( new Error ( 'Invalid response' ) ) ;
30+ }
31+ } ) ;
32+
33+ socket . on ( 'error' , reject ) ;
34+ socket . on ( 'timeout' , ( ) => {
35+ socket . end ( ) ;
36+ reject ( new Error ( 'Timeout' ) ) ;
37+ } ) ;
38+
39+ socket . setTimeout ( 5000 ) ;
40+ } ) ;
41+ }
42+
43+ async function handleFeatureServerCommand ( args ) {
44+ const cmd = args [ 0 ] ;
45+
46+ switch ( cmd ) {
47+ case 'drain' :
48+ if ( ! args [ 1 ] ) {
49+ console . error ( 'Error: drain requires server IP address' ) ;
50+ process . exit ( 1 ) ;
51+ }
52+ const drainResult = await sendCommand ( 'fs-drain' , null , null , null , args [ 1 ] ) ;
53+ if ( drainResult . error || ! drainResult . success ) {
54+ console . error ( 'Error:' , drainResult . error || 'Failed to drain server' ) ;
55+ process . exit ( 1 ) ;
56+ }
57+ console . log ( `✓ Drained ${ drainResult . server } ` ) ;
58+ console . log ( `Drained: [${ drainResult . drained . join ( ', ' ) } ]` ) ;
59+ break ;
60+
61+ case 'undrain' :
62+ if ( ! args [ 1 ] ) {
63+ console . error ( 'Error: undrain requires server IP address' ) ;
64+ process . exit ( 1 ) ;
65+ }
66+ const undrainResult = await sendCommand ( 'fs-undrain' , null , null , null , args [ 1 ] ) ;
67+ if ( undrainResult . error || ! undrainResult . success ) {
68+ console . error ( 'Error:' , undrainResult . error || 'Failed to undrain server' ) ;
69+ process . exit ( 1 ) ;
70+ }
71+ console . log ( `✓ Undrained ${ undrainResult . server } ` ) ;
72+ console . log ( `Drained: [${ undrainResult . drained . join ( ', ' ) } ]` ) ;
73+ break ;
74+
75+ case 'drained' :
76+ const drained = await sendCommand ( 'fs-drained' ) ;
77+ if ( drained . error ) {
78+ console . error ( 'Error:' , drained . error ) ;
79+ process . exit ( 1 ) ;
80+ }
81+ if ( drained . drained . length === 0 ) {
82+ console . log ( 'No servers drained' ) ;
83+ } else {
84+ console . log ( 'Drained servers:' ) ;
85+ drained . drained . forEach ( s => console . log ( ` - ${ s } ` ) ) ;
86+ }
87+ break ;
88+
89+ case 'list' :
90+ const list = await sendCommand ( 'fs-list' ) ;
91+ if ( list . error ) {
92+ console . error ( 'Error:' , list . error ) ;
93+ process . exit ( 1 ) ;
94+ }
95+ if ( list . servers . length === 0 ) {
96+ console . log ( 'No servers configured' ) ;
97+ if ( list . drained . length > 0 ) {
98+ console . log ( 'Orphaned drained servers:' ) ;
99+ list . drained . forEach ( s => console . log ( ` - ${ s } (orphaned)` ) ) ;
100+ }
101+ } else {
102+ console . log ( 'Servers:' ) ;
103+ list . servers . forEach ( ( { server, status } ) => {
104+ const icon = status === 'drained' ? '🔴' : '🟢' ;
105+ console . log ( ` ${ icon } ${ server } (${ status } )` ) ;
106+ } ) ;
107+ }
108+ break ;
109+
110+ default :
111+ console . error ( 'Unknown command:' , cmd ) ;
112+ console . error ( 'Use: drain, undrain, drained, list' ) ;
113+ process . exit ( 1 ) ;
114+ }
115+ }
116+
117+ async function main ( ) {
118+ const args = process . argv . slice ( 2 ) ;
119+
120+ if ( args . length === 0 ) {
121+ console . log ( 'SBC CLI' ) ;
122+ console . log ( 'Usage:' ) ;
123+ console . log ( ' sbc-cli fs drain <ip> Drain server by IP' ) ;
124+ console . log ( ' sbc-cli fs undrain <ip> Undrain server by IP' ) ;
125+ console . log ( ' sbc-cli fs drained List drained server IPs' ) ;
126+ console . log ( ' sbc-cli fs list List all servers' ) ;
127+ console . log ( ' sbc-cli set <key> <value> Set config' ) ;
128+ console . log ( ' sbc-cli get <key> Get config' ) ;
129+ console . log ( ' sbc-cli list List all config' ) ;
130+ console . log ( '' ) ;
131+ console . log ( 'Examples:' ) ;
132+ console . log ( ' sbc-cli fs drain 192.168.1.10' ) ;
133+ console . log ( ' sbc-cli fs undrain 192.168.1.10' ) ;
134+ console . log ( ' sbc-cli fs drained' ) ;
135+ return ;
136+ }
137+
138+ const cmd = args [ 0 ] ;
139+
140+ try {
141+ switch ( cmd ) {
142+ case 'fs' :
143+ if ( args . length < 2 ) {
144+ console . error ( 'fs needs: drain, undrain, drained, or list' ) ;
145+ process . exit ( 1 ) ;
146+ }
147+ await handleFeatureServerCommand ( args . slice ( 1 ) ) ;
148+ break ;
149+
150+ case 'set' :
151+ if ( args . length < 3 ) {
152+ console . error ( 'set needs key and value' ) ;
153+ process . exit ( 1 ) ;
154+ }
155+ const setResult = await sendCommand ( 'set' , args [ 1 ] , args [ 2 ] ) ;
156+ if ( setResult . error ) {
157+ console . error ( 'Error:' , setResult . error ) ;
158+ process . exit ( 1 ) ;
159+ }
160+ console . log ( `✓ ${ setResult . key } = ${ JSON . stringify ( setResult . value ) } ` ) ;
161+ break ;
162+
163+ case 'get' :
164+ if ( args . length < 2 ) {
165+ console . error ( 'get needs key' ) ;
166+ process . exit ( 1 ) ;
167+ }
168+ const getResult = await sendCommand ( 'get' , args [ 1 ] ) ;
169+ if ( getResult . error ) {
170+ console . error ( 'Error:' , getResult . error ) ;
171+ process . exit ( 1 ) ;
172+ }
173+ console . log ( `${ getResult . key } = ${ JSON . stringify ( getResult . value ) } ` ) ;
174+ break ;
175+
176+ case 'list' :
177+ const listResult = await sendCommand ( 'list' ) ;
178+ if ( listResult . error ) {
179+ console . error ( 'Error:' , listResult . error ) ;
180+ process . exit ( 1 ) ;
181+ }
182+ console . log ( 'Config:' ) ;
183+ for ( const [ key , value ] of Object . entries ( listResult . config ) ) {
184+ console . log ( ` ${ key } = ${ JSON . stringify ( value ) } ` ) ;
185+ }
186+ break ;
187+
188+ default :
189+ console . error ( 'Unknown command:' , cmd ) ;
190+ process . exit ( 1 ) ;
191+ }
192+ } catch ( err ) {
193+ console . error ( 'Error:' , err . message ) ;
194+ process . exit ( 1 ) ;
195+ }
196+ }
197+
198+ if ( require . main === module ) {
199+ main ( ) . catch ( err => {
200+ console . error ( 'Error:' , err . message ) ;
201+ process . exit ( 1 ) ;
202+ } ) ;
203+ }
0 commit comments