Skip to content

Commit 240d128

Browse files
committed
remove fuzzy, indent-string, axios dependency and implement fetch-based token handling
1 parent 1375b92 commit 240d128

3 files changed

Lines changed: 78 additions & 98 deletions

File tree

package-lock.json

Lines changed: 3 additions & 55 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,12 @@
1616
"@oclif/core": "^4.0.24",
1717
"@rocket.chat/apps-compiler": "^0.5.1",
1818
"@rocket.chat/apps-engine": "^1.55.0",
19-
"axios": "^1.12.2",
2019
"chalk": "^5.6.2",
2120
"chokidar": "^4.0.3",
2221
"conf": "^14.0.0",
2322
"fetch-with-proxy": "^3.0.1",
2423
"figures": "^6.1.0",
2524
"form-data": "^4.0.4",
26-
"fuzzy": "^0.1.3",
27-
"indent-string": "^5.0.0",
2825
"@inquirer/prompts": "^7.8.6",
2926
"open": "^10.2.0",
3027
"pascal-case": "^3.1.2",
@@ -115,4 +112,4 @@
115112
"view-coverage": "npm run test && http-server coverage -p 9083 -c-1"
116113
},
117114
"types": "lib/index.d.ts"
118-
}
115+
}

src/misc/cloudAuth.ts

Lines changed: 74 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import {Request, Server} from '@hapi/hapi'
2-
import axios, {isAxiosError} from 'axios'
32
import chalk from 'chalk'
43
import Conf from 'conf'
54
import {createHash, randomUUID} from 'crypto'
@@ -33,6 +32,16 @@ type CloudConfig = {
3332
'rcc.expiresAt'?: Date
3433
}
3534

35+
class CloudAuthRequestError extends Error {
36+
constructor(
37+
public readonly status: number,
38+
public readonly body: unknown,
39+
message?: string,
40+
) {
41+
super(message ?? `Request failed with status ${status}`)
42+
}
43+
}
44+
3645
export class CloudAuth {
3746
private config?: Conf<CloudConfig>
3847
private codeVerifier: string
@@ -134,14 +143,7 @@ export class CloudAuth {
134143
code_verifier: this.codeVerifier,
135144
}
136145

137-
const res = await axios.post<ICloudToken>(
138-
`${cloudUrl}/api/oauth/token`,
139-
new URLSearchParams(request).toString(),
140-
{
141-
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
142-
},
143-
)
144-
const tokenInfo = res.data
146+
const tokenInfo = await this.postForm<ICloudToken>('/api/oauth/token', request)
145147

146148
const expiresAt = new Date()
147149
expiresAt.setSeconds(expiresAt.getSeconds() + tokenInfo.expires_in)
@@ -155,12 +157,8 @@ export class CloudAuth {
155157

156158
return tokenInfo
157159
} catch (err) {
158-
if (isAxiosError(err) && err.response) {
159-
const {status, data} = err.response
160-
const errorMessage = (data as {error?: string; requestId?: string}) || {}
161-
this.logError(
162-
`[${status}] error getting token: ${errorMessage.error ?? 'unknown'} (${errorMessage.requestId ?? 'n/a'})`,
163-
)
160+
if (err instanceof CloudAuthRequestError) {
161+
this.logHttpError('error getting token', err)
164162
} else {
165163
this.logError(`Error getting token: ${this.formatError(err)}`)
166164
}
@@ -181,14 +179,7 @@ export class CloudAuth {
181179
}
182180

183181
try {
184-
const res = await axios.post<ICloudToken>(
185-
`${cloudUrl}/api/oauth/token`,
186-
new URLSearchParams(request).toString(),
187-
{
188-
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
189-
},
190-
)
191-
const tokenInfo = res.data
182+
const tokenInfo = await this.postForm<ICloudToken>('/api/oauth/token', request)
192183

193184
const expiresAt = new Date()
194185
expiresAt.setSeconds(expiresAt.getSeconds() + tokenInfo.expires_in)
@@ -200,12 +191,8 @@ export class CloudAuth {
200191
config.set('rcc.token.token_type', tokenInfo.token_type)
201192
config.set('rcc.expiresAt', expiresAt)
202193
} catch (err) {
203-
if (isAxiosError(err) && err.response) {
204-
const {status, data} = err.response
205-
const errorMessage = (data as {error?: string; requestId?: string}) || {}
206-
this.logError(
207-
`[${status}] error refreshing token: ${errorMessage.error ?? 'unknown'} (${errorMessage.requestId ?? 'n/a'})`,
208-
)
194+
if (err instanceof CloudAuthRequestError) {
195+
this.logHttpError('error refreshing token', err)
209196
} else {
210197
this.logError(`Error refreshing token: ${this.formatError(err)}`)
211198
}
@@ -224,22 +211,16 @@ export class CloudAuth {
224211
}
225212

226213
try {
227-
await axios.post(`${cloudUrl}/api/oauth/revoke`, new URLSearchParams(request).toString(), {
228-
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
229-
})
214+
await this.postForm('/api/oauth/revoke', request)
230215
this.getConfig().delete('rcc')
231216
} catch (err) {
232-
if (isAxiosError(err) && err.response) {
233-
const {status, data} = err.response
234-
if (status === 401) {
217+
if (err instanceof CloudAuthRequestError) {
218+
if (err.status === 401) {
235219
this.getConfig().delete('rcc')
236220
return
237221
}
238222

239-
const errorMessage = (data as {error?: string; requestId?: string}) || {}
240-
this.logError(
241-
`[${status}] error revoking the token: ${errorMessage.error ?? 'unknown'} (${errorMessage.requestId ?? 'n/a'})`,
242-
)
223+
this.logHttpError('error revoking the token', err)
243224
} else {
244225
this.logError(`Error revoking token: ${this.formatError(err)}`)
245226
}
@@ -302,6 +283,60 @@ export class CloudAuth {
302283
return url.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
303284
}
304285

286+
private async postForm<T>(path: string, payload: Record<string, string>): Promise<T> {
287+
const response = await fetch(`${cloudUrl}${path}`, {
288+
method: 'POST',
289+
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
290+
body: new URLSearchParams(payload).toString(),
291+
})
292+
293+
const rawBody = await response.text()
294+
let parsedBody: unknown = undefined
295+
296+
if (rawBody) {
297+
try {
298+
parsedBody = JSON.parse(rawBody)
299+
} catch {
300+
parsedBody = rawBody
301+
}
302+
}
303+
304+
if (!response.ok) {
305+
throw new CloudAuthRequestError(response.status, parsedBody ?? rawBody)
306+
}
307+
308+
return parsedBody as T
309+
}
310+
311+
private logHttpError(prefix: string, err: CloudAuthRequestError): void {
312+
const {error, requestId} = this.extractErrorDetails(err.body)
313+
this.logError(`[${err.status}] ${prefix}: ${error ?? 'unknown'} (${requestId ?? 'n/a'})`)
314+
}
315+
316+
private extractErrorDetails(body: unknown): {error?: string; requestId?: string} {
317+
if (body && typeof body === 'object') {
318+
const info = body as Record<string, unknown>
319+
return {
320+
error: typeof info.error === 'string' ? info.error : undefined,
321+
requestId: typeof info.requestId === 'string' ? info.requestId : undefined,
322+
}
323+
}
324+
325+
if (typeof body === 'string') {
326+
try {
327+
const parsed = JSON.parse(body) as Record<string, unknown>
328+
return {
329+
error: typeof parsed.error === 'string' ? parsed.error : undefined,
330+
requestId: typeof parsed.requestId === 'string' ? parsed.requestId : undefined,
331+
}
332+
} catch {
333+
return {error: body}
334+
}
335+
}
336+
337+
return {}
338+
}
339+
305340
private getConfig(): Conf<CloudConfig> {
306341
if (!this.config) {
307342
throw new Error('Cloud configuration not initialized')

0 commit comments

Comments
 (0)