@@ -6,6 +6,7 @@ import { logger } from '@socketsecurity/registry/lib/logger'
66import constants from '../../constants.mts'
77import { failMsgWithBadge } from '../../utils/fail-msg-with-badge.mts'
88import { mdTable } from '../../utils/markdown.mts'
9+ import { msAtHome } from '../../utils/ms-at-home.mts'
910import { serializeResultJson } from '../../utils/serialize-result-json.mts'
1011
1112import type { CResult , OutputKind } from '../../types.mts'
@@ -67,8 +68,8 @@ export async function outputAuditLog(
6768 const filteredLogs = result . data . results
6869 const formattedOutput = filteredLogs . map ( logs => [
6970 logs . event_id ?? '' ,
70- logs . created_at ?? '' ,
71- ( logs . type || '' ) . padEnd ( 30 , ' ' ) ,
71+ msAtHome ( logs . created_at ?? '' ) ,
72+ logs . type ?? '' ,
7273 logs . user_email ?? '' ,
7374 logs . ip_address ?? '' ,
7475 logs . user_agent ?? '' ,
@@ -95,6 +96,16 @@ export async function outputAuditLog(
9596 screen . key ( [ 'escape' , 'q' , 'C-c' ] , ( ) => process . exit ( 0 ) )
9697
9798 const TableWidget = require ( 'blessed-contrib/lib/widget/table.js' )
99+ const tipsBoxHeight = 1 // 1 row for tips box
100+ const detailsBoxHeight = 20 // bottom N rows for details box. 20 gives 4 lines for condensed payload before it scrolls out of view
101+
102+ const maxWidths = [ 10 , 10 , 10 , 10 , 10 , 10 ]
103+ formattedOutput . forEach ( row => {
104+ row . forEach ( ( str , i ) => {
105+ maxWidths [ i ] = Math . max ( str . length , maxWidths [ i ] ?? str . length )
106+ } )
107+ } )
108+
98109 const table : any = new TableWidget ( {
99110 keys : 'true' ,
100111 fg : 'white' ,
@@ -103,31 +114,40 @@ export async function outputAuditLog(
103114 interactive : 'true' ,
104115 label : `Audit Logs for ${ orgSlug } ` ,
105116 width : '100%' ,
106- height : '70%' , // Changed from 100% to 70%
117+ top : 0 ,
118+ bottom : detailsBoxHeight + tipsBoxHeight ,
107119 border : {
108120 type : 'line' ,
109121 fg : 'cyan' ,
110122 } ,
111- columnWidth : [ 10 , 30 , 30 , 25 , 15 , 200 ] ,
112- // TODO: the truncation doesn't seem to work too well yet but when we add
113- // `pad` alignment fails, when we extend columnSpacing alignment fails
114- columnSpacing : 1 ,
123+ columnWidth : maxWidths , //[10, 30, 40, 25, 15, 200],
124+ // Note: spacing works as long as you don't reserve more than total width
125+ columnSpacing : 4 ,
115126 truncate : '_' ,
116127 } )
117128
118- // Create details box at the bottom
119129 const BoxWidget = require ( 'blessed/lib/widgets/box.js' )
130+ const tipsBox : Widgets . BoxElement = new BoxWidget ( {
131+ bottom : detailsBoxHeight , // sits just above the details box
132+ height : tipsBoxHeight ,
133+ width : '100%' ,
134+ style : {
135+ fg : 'yellow' ,
136+ bg : 'black' ,
137+ } ,
138+ tags : true ,
139+ content : '↑/↓: Move Enter: Select q/ESC: Quit' ,
140+ } )
120141 const detailsBox : Widgets . BoxElement = new BoxWidget ( {
121142 bottom : 0 ,
122- height : '30%' ,
143+ height : detailsBoxHeight ,
123144 width : '100%' ,
124145 border : {
125146 type : 'line' ,
126147 fg : 'cyan' ,
127148 } ,
128149 label : 'Details' ,
129- content :
130- 'Use arrow keys to navigate. Press Enter to select an event. Press q to exit.' ,
150+ content : formatResult ( filteredLogs [ 0 ] , true ) ,
131151 style : {
132152 fg : 'white' ,
133153 } ,
@@ -141,28 +161,18 @@ export async function outputAuditLog(
141161 // allow control the table with the keyboard
142162 table . focus ( )
143163
164+ // Stacking order: table (top), tipsBox (middle), detailsBox (bottom)
144165 screen . append ( table )
166+ screen . append ( tipsBox )
145167 screen . append ( detailsBox )
146168
147169 // Update details box when selection changes
148170 table . rows . on ( 'select item' , ( ) => {
149171 const selectedIndex = table . rows . selected
150172 if ( selectedIndex !== undefined && selectedIndex >= 0 ) {
151173 const selectedRow = filteredLogs [ selectedIndex ]
152- if ( selectedRow ) {
153- // Format the object with spacing but keep the payload compact because
154- // that can contain just about anything and spread many lines.
155- const obj = { ...selectedRow , payload : 'REPLACEME' }
156- const json = JSON . stringify ( obj , null , 2 )
157- . replace (
158- / " p a y l o a d " : " R E P L A C E M E " / ,
159- `"payload": ${ JSON . stringify ( selectedRow . payload ?? { } ) } ` ,
160- )
161- . replace ( / ^ \s * " ( [ ^ " ] + ) ? " / gm, ' $1' )
162- // Note: the spacing works around issues with the table; it refuses to pad!
163- detailsBox . setContent ( json )
164- screen . render ( )
165- }
174+ detailsBox . setContent ( formatResult ( selectedRow ) )
175+ screen . render ( )
166176 }
167177 } )
168178
@@ -172,10 +182,32 @@ export async function outputAuditLog(
172182 const selectedIndex = table . rows . selected
173183 screen . destroy ( )
174184 const selectedRow = formattedOutput [ selectedIndex ]
175- logger . log ( 'Last selection:\n' , selectedRow )
185+ ? formatResult ( filteredLogs [ selectedIndex ] , true )
186+ : '(none)'
187+ logger . log ( `Last selection:\n${ selectedRow . trim ( ) } ` )
176188 } )
177189}
178190
191+ function formatResult (
192+ selectedRow ?: SocketSdkReturnType < 'getAuditLogEvents' > [ 'data' ] [ 'results' ] [ number ] ,
193+ keepQuotes = false ,
194+ ) : string {
195+ if ( ! selectedRow ) {
196+ return '(none)'
197+ }
198+ // Format the object with spacing but keep the payload compact because
199+ // that can contain just about anything and spread many lines.
200+ const obj = { ...selectedRow , payload : 'REPLACEME' }
201+ const json = JSON . stringify ( obj , null , 2 ) . replace (
202+ / " p a y l o a d " : " R E P L A C E M E " / ,
203+ `"payload": ${ JSON . stringify ( selectedRow . payload ?? { } ) } ` ,
204+ )
205+ if ( keepQuotes ) {
206+ return json
207+ }
208+ return json . replace ( / ^ \s * " ( [ ^ " ] + ) ? " / gm, ' $1' )
209+ }
210+
179211export async function outputAsJson (
180212 auditLogs : CResult < SocketSdkReturnType < 'getAuditLogEvents' > [ 'data' ] > ,
181213 {
0 commit comments