11'use strict' ;
22
3+ import { loadCredentials , saveCredentials , normalizeTarget } from './cliCredentials.ts' ;
4+ import { isJWTExpired } from '../security/tokenAuthentication.ts' ;
35import * as envMgr from '../utility/environment/environmentManager.ts' ;
46envMgr . initSync ( ) ;
57import * as terms from '../utility/hdbTerms.ts' ;
@@ -56,15 +58,32 @@ function buildRequest(): any {
5658 return req ;
5759}
5860
61+ /**
62+ * Resolves the target URL from various sources.
63+ * @param {Object } req The request object.
64+ * @param {Object } allCredentials Stored credentials.
65+ * @returns {string|null } The resolved target URL.
66+ */
67+ function resolveTarget ( req , allCredentials ) {
68+ return (
69+ req . target ||
70+ process . env . HARPER_CLI_TARGET ||
71+ process . env . CLI_TARGET ||
72+ ( allCredentials && allCredentials . last_target )
73+ ) ;
74+ }
75+
5976/**
6077 * Using a unix domain socket will send a request to hdb operations API server
6178 * @param req
79+ * @param skipResponseLog By default, the response is logged to the console. Set this to true to skip logging it, which can be useful for sensitive responses like login calls!
6280 * @returns {Promise<void> }
6381 */
64- async function cliOperations ( req : any ) {
65- if ( ! req . target ) {
66- req . target = process . env . HARPER_CLI_TARGET || process . env . CLI_TARGET ;
67- }
82+ async function cliOperations ( req : any , skipResponseLog = false ) {
83+ require ( 'dotenv' ) . config ( ) ;
84+
85+ const allCredentials = loadCredentials ( ) ;
86+ req . target = normalizeTarget ( resolveTarget ( req , allCredentials ) ) ;
6887 let target ;
6988 if ( req . target ) {
7089 try {
@@ -76,15 +95,17 @@ async function cliOperations(req: any) {
7695 throw error ;
7796 }
7897 }
98+ const resolvedTarget = req . target ;
7999 target = {
80100 protocol : target . protocol ,
81101 hostname : target . hostname ,
82102 port : target . port ,
83103 username : req . username || target . username || process . env . HARPER_CLI_USERNAME || process . env . CLI_TARGET_USERNAME ,
84104 password : req . password || target . password || process . env . HARPER_CLI_PASSWORD || process . env . CLI_TARGET_PASSWORD ,
85105 rejectUnauthorized : req . rejectUnauthorized ,
106+ resolvedTarget,
86107 } ;
87- console . error ( `Connecting to ${ target . protocol } // ${ target . hostname } : ${ target . port } ` ) ;
108+ console . error ( `Connecting to ${ resolvedTarget } ` ) ;
88109 } else {
89110 // if we aren't doing a targeted operation (like deploy), we initialize the config and verify that local harper
90111 // is running and that we can communicate with it.
@@ -110,6 +131,47 @@ async function cliOperations(req: any) {
110131 options . headers = { 'Content-Type' : 'application/json' } ;
111132 if ( target ?. username ) {
112133 options . headers . Authorization = `Basic ${ Buffer . from ( `${ target . username } :${ target . password } ` ) . toString ( 'base64' ) } ` ;
134+ } else if ( allCredentials ) {
135+ let tokens = null ;
136+ let lookupKey = null ;
137+ if ( target && allCredentials . targets ) {
138+ lookupKey = target . resolvedTarget ;
139+ tokens = allCredentials . targets [ lookupKey ] ?? null ;
140+ }
141+
142+ if ( tokens ?. operation_token ) {
143+ if ( tokens . refresh_token && isJWTExpired ( tokens . operation_token ) ) {
144+ console . error ( 'Operation token expired, attempting to refresh...' ) ;
145+ try {
146+ const refreshOptions = { ...options } ;
147+ refreshOptions . headers = { ...options . headers , Authorization : `Bearer ${ tokens . refresh_token } ` } ;
148+ const refreshResponse = await httpRequest ( refreshOptions , {
149+ operation : 'refresh_operation_token' ,
150+ } ) ;
151+ if ( refreshResponse . statusCode === 200 ) {
152+ const refreshData = JSON . parse ( refreshResponse . body ) ;
153+ if ( refreshData . operation_token ) {
154+ tokens . operation_token = refreshData . operation_token ;
155+ saveCredentials ( lookupKey || target ?. resolvedTarget , {
156+ operation_token : tokens . operation_token ,
157+ refresh_token : tokens . refresh_token ,
158+ } ) ;
159+ console . error ( 'Operation token refreshed successfully.' ) ;
160+ // Update the original request's authorization header with the new token
161+ options . headers . Authorization = `Bearer ${ tokens . operation_token } ` ;
162+ }
163+ } else if ( refreshResponse . statusCode === 401 ) {
164+ console . error ( 'Refresh token expired or invalid. Please run harper login again.' ) ;
165+ process . exit ( 1 ) ;
166+ } else {
167+ console . error ( `Failed to refresh operation token: ${ refreshResponse . statusCode } ` ) ;
168+ }
169+ } catch ( refreshErr ) {
170+ console . error ( `Error refreshing operation token: ${ refreshErr . message } ` ) ;
171+ }
172+ }
173+ options . headers . Authorization = `Bearer ${ tokens . operation_token } ` ;
174+ }
113175 }
114176 if ( req . cborEncode ) {
115177 options . headers [ 'Content-Type' ] = 'application/cbor' ;
@@ -141,7 +203,13 @@ async function cliOperations(req: any) {
141203 process . exit ( 1 ) ;
142204 }
143205
144- console . log ( responseLog ) ;
206+ if ( ! skipResponseLog ) {
207+ console . log ( responseLog ) ;
208+ }
209+
210+ if ( target ) {
211+ responseData . resolvedTarget = target . resolvedTarget ;
212+ }
145213
146214 return responseData ;
147215 } catch ( err ) {
0 commit comments