@dishantlangayan/sc-cli-core 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/lib/auth/auth-manager.js +5 -2
- package/lib/auth/index.d.ts +4 -2
- package/lib/auth/index.js +4 -2
- package/lib/auth/org-manager.d.ts +8 -0
- package/lib/auth/org-manager.js +44 -0
- package/lib/auth/org-types.d.ts +4 -0
- package/lib/auth/org-types.js +2 -0
- package/lib/config/env-vars.d.ts +1 -2
- package/lib/config/env-vars.js +0 -1
- package/lib/exported.d.ts +2 -2
- package/lib/exported.js +1 -1
- package/lib/sc-command.d.ts +14 -0
- package/lib/sc-command.js +26 -11
- package/lib/util/sc-connection.d.ts +9 -1
- package/lib/util/sc-connection.js +19 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,6 +23,8 @@ The ScConnection class provide abstraction functions for Solace Cloud API REST c
|
|
|
23
23
|
## OrgManager
|
|
24
24
|
The OrgManager class provides utility functions to store and retrieve Solace Cloud authentication information from user's home directory: `~/.sc/` or `%USERPROFILE%\sc\`. The implementation uses AES-256-GCM for authenticated encryption and provides machine-bound encryption that combines OS-level security (keychain) with machine-specific identifiers, making credentials non-transferable between machines.
|
|
25
25
|
|
|
26
|
+
Supports changing of the Solace Cloud REST API base url using the environment variable `SC_BASE_URL` and API version using `SC_API_VERSION`.
|
|
27
|
+
|
|
26
28
|
## BrokerAuthManager
|
|
27
29
|
The BrokerAuthManager class provides utility functions to store and retrieve broker SEMP management authentication information similar to the `OrgManager` class. It supports Basic and OAuth authentication schemes.
|
|
28
30
|
|
package/lib/auth/auth-manager.js
CHANGED
|
@@ -85,8 +85,11 @@ export class BrokerAuthManager {
|
|
|
85
85
|
}
|
|
86
86
|
const baseURL = `${broker.sempEndpoint}:${broker.sempPort}`;
|
|
87
87
|
const accessToken = broker.authType === AuthType.OAUTH ? broker.accessToken : broker.encodedCredentials;
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
return new ScConnection(baseURL, accessToken, {
|
|
89
|
+
apiType: 'semp',
|
|
90
|
+
authType: broker.authType === AuthType.BASIC ? 'basic' : 'bearer',
|
|
91
|
+
timeout,
|
|
92
|
+
});
|
|
90
93
|
}
|
|
91
94
|
/**
|
|
92
95
|
* Get all broker configurations
|
package/lib/auth/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* Provides encrypted storage for
|
|
2
|
+
* Authentication management module
|
|
3
|
+
* Provides encrypted storage for broker and organization credentials
|
|
4
4
|
*/
|
|
5
5
|
export { BrokerAuthEncryption } from './auth-encryption.js';
|
|
6
6
|
export { BrokerAuthManager } from './auth-manager.js';
|
|
7
7
|
export { AuthType, type BasicBrokerAuth, type BrokerAuth, type BrokerAuthBase, BrokerAuthError, BrokerAuthErrorCode, type BrokerAuthStorage, type EncryptedData, type EncryptionMetadata, type OAuthBrokerAuth, } from './auth-types.js';
|
|
8
|
+
export { OrgManager } from './org-manager.js';
|
|
9
|
+
export { type OrgConfig, OrgError, OrgErrorCode, type OrgStorage } from './org-types.js';
|
package/lib/auth/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* Provides encrypted storage for
|
|
2
|
+
* Authentication management module
|
|
3
|
+
* Provides encrypted storage for broker and organization credentials
|
|
4
4
|
*/
|
|
5
5
|
export { BrokerAuthEncryption } from './auth-encryption.js';
|
|
6
6
|
export { BrokerAuthManager } from './auth-manager.js';
|
|
7
7
|
export { AuthType, BrokerAuthError, BrokerAuthErrorCode, } from './auth-types.js';
|
|
8
|
+
export { OrgManager } from './org-manager.js';
|
|
9
|
+
export { OrgError, OrgErrorCode } from './org-types.js';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ScConnection } from '../util/sc-connection.js';
|
|
1
2
|
import { KeychainService } from './keychain.js';
|
|
2
3
|
import { type OrgConfig } from './org-types.js';
|
|
3
4
|
/**
|
|
@@ -28,6 +29,13 @@ export declare class OrgManager {
|
|
|
28
29
|
* Clear all organization configurations
|
|
29
30
|
*/
|
|
30
31
|
clearAll(): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Create ScConnection instance from stored org config
|
|
34
|
+
* @param identifier - Organization ID or alias
|
|
35
|
+
* @param timeout - Optional timeout override (default: 10000ms)
|
|
36
|
+
* @returns Configured ScConnection instance
|
|
37
|
+
*/
|
|
38
|
+
createConnection(identifier: string, timeout?: number): Promise<ScConnection>;
|
|
31
39
|
/**
|
|
32
40
|
* Get all organization configurations
|
|
33
41
|
* @returns Array of all organization configurations
|
package/lib/auth/org-manager.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
+
import { DefaultBaseUrl, EnvironmentVariable, envVars } from '../config/env-vars.js';
|
|
5
|
+
import { ScConnection } from '../util/sc-connection.js';
|
|
4
6
|
import { BrokerAuthEncryption } from './auth-encryption.js';
|
|
5
7
|
import { KeychainService } from './keychain.js';
|
|
6
8
|
import { OrgError, OrgErrorCode } from './org-types.js';
|
|
@@ -71,6 +73,29 @@ export class OrgManager {
|
|
|
71
73
|
this.storage.orgs = [];
|
|
72
74
|
await this.saveStorage();
|
|
73
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Create ScConnection instance from stored org config
|
|
78
|
+
* @param identifier - Organization ID or alias
|
|
79
|
+
* @param timeout - Optional timeout override (default: 10000ms)
|
|
80
|
+
* @returns Configured ScConnection instance
|
|
81
|
+
*/
|
|
82
|
+
async createConnection(identifier, timeout = 10_000) {
|
|
83
|
+
this.ensureInitialized();
|
|
84
|
+
const org = await this.getOrg(identifier);
|
|
85
|
+
if (!org) {
|
|
86
|
+
throw new OrgError(`Organization '${identifier}' not found`, OrgErrorCode.ORG_NOT_FOUND);
|
|
87
|
+
}
|
|
88
|
+
// Get base URL: org config → env var → default
|
|
89
|
+
const baseURL = org.baseUrl ?? envVars.getString(EnvironmentVariable.SC_BASE_URL, DefaultBaseUrl);
|
|
90
|
+
// Get API version: org config → env var → default 'v2'
|
|
91
|
+
const apiVersion = org.apiVersion ?? envVars.getString(EnvironmentVariable.SC_API_VERSION, 'v2');
|
|
92
|
+
return new ScConnection(baseURL, org.accessToken, {
|
|
93
|
+
apiType: 'cloud',
|
|
94
|
+
apiVersion,
|
|
95
|
+
authType: 'bearer',
|
|
96
|
+
timeout,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
74
99
|
/**
|
|
75
100
|
* Get all organization configurations
|
|
76
101
|
* @returns Array of all organization configurations
|
|
@@ -332,5 +357,24 @@ export class OrgManager {
|
|
|
332
357
|
if (org.alias !== undefined && org.alias.trim() === '') {
|
|
333
358
|
throw new OrgError('Alias cannot be empty if provided', OrgErrorCode.INVALID_ORG_ID);
|
|
334
359
|
}
|
|
360
|
+
// Validate apiVersion if provided
|
|
361
|
+
if (org.apiVersion !== undefined && org.apiVersion.trim() === '') {
|
|
362
|
+
throw new OrgError('API version cannot be empty if provided', OrgErrorCode.INVALID_API_VERSION);
|
|
363
|
+
}
|
|
364
|
+
// Validate baseUrl if provided
|
|
365
|
+
if (org.baseUrl !== undefined) {
|
|
366
|
+
// Must not be empty or whitespace
|
|
367
|
+
if (org.baseUrl.trim() === '') {
|
|
368
|
+
throw new OrgError('Base URL cannot be empty if provided', OrgErrorCode.INVALID_BASE_URL);
|
|
369
|
+
}
|
|
370
|
+
// Must start with http:// or https://
|
|
371
|
+
if (!(org.baseUrl.startsWith('http://') || org.baseUrl.startsWith('https://'))) {
|
|
372
|
+
throw new OrgError('Base URL must start with http:// or https://', OrgErrorCode.INVALID_BASE_URL);
|
|
373
|
+
}
|
|
374
|
+
// Should not end with trailing slash
|
|
375
|
+
if (org.baseUrl.endsWith('/')) {
|
|
376
|
+
throw new OrgError('Base URL should not end with a slash', OrgErrorCode.INVALID_BASE_URL);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
335
379
|
}
|
|
336
380
|
}
|
package/lib/auth/org-types.d.ts
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
export interface OrgConfig {
|
|
5
5
|
accessToken: string;
|
|
6
6
|
alias?: string;
|
|
7
|
+
apiVersion?: string;
|
|
8
|
+
baseUrl?: string;
|
|
7
9
|
isDefault?: boolean;
|
|
8
10
|
orgId: string;
|
|
9
11
|
}
|
|
@@ -23,6 +25,8 @@ export declare enum OrgErrorCode {
|
|
|
23
25
|
FILE_READ_ERROR = "FILE_READ_ERROR",
|
|
24
26
|
FILE_WRITE_ERROR = "FILE_WRITE_ERROR",
|
|
25
27
|
INVALID_ACCESS_TOKEN = "INVALID_ACCESS_TOKEN",
|
|
28
|
+
INVALID_API_VERSION = "INVALID_API_VERSION",
|
|
29
|
+
INVALID_BASE_URL = "INVALID_BASE_URL",
|
|
26
30
|
INVALID_ORG_ID = "INVALID_ORG_ID",
|
|
27
31
|
NOT_INITIALIZED = "NOT_INITIALIZED",
|
|
28
32
|
ORG_ALREADY_EXISTS = "ORG_ALREADY_EXISTS",
|
package/lib/auth/org-types.js
CHANGED
|
@@ -8,6 +8,8 @@ export var OrgErrorCode;
|
|
|
8
8
|
OrgErrorCode["FILE_READ_ERROR"] = "FILE_READ_ERROR";
|
|
9
9
|
OrgErrorCode["FILE_WRITE_ERROR"] = "FILE_WRITE_ERROR";
|
|
10
10
|
OrgErrorCode["INVALID_ACCESS_TOKEN"] = "INVALID_ACCESS_TOKEN";
|
|
11
|
+
OrgErrorCode["INVALID_API_VERSION"] = "INVALID_API_VERSION";
|
|
12
|
+
OrgErrorCode["INVALID_BASE_URL"] = "INVALID_BASE_URL";
|
|
11
13
|
OrgErrorCode["INVALID_ORG_ID"] = "INVALID_ORG_ID";
|
|
12
14
|
OrgErrorCode["NOT_INITIALIZED"] = "NOT_INITIALIZED";
|
|
13
15
|
OrgErrorCode["ORG_ALREADY_EXISTS"] = "ORG_ALREADY_EXISTS";
|
package/lib/config/env-vars.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
export declare enum EnvironmentVariable {
|
|
2
2
|
'SC_ACCESS_TOKEN' = "SC_ACCESS_TOKEN",
|
|
3
3
|
'SC_API_VERSION' = "SC_API_VERSION",
|
|
4
|
-
'SC_BASE_URL' = "SC_BASE_URL"
|
|
5
|
-
'SEMP_API_VERSION' = "SEMP_API_VERSION"
|
|
4
|
+
'SC_BASE_URL' = "SC_BASE_URL"
|
|
6
5
|
}
|
|
7
6
|
export declare const DefaultBaseUrl = "https://api.solace.cloud";
|
|
8
7
|
/**
|
package/lib/config/env-vars.js
CHANGED
|
@@ -3,7 +3,6 @@ export var EnvironmentVariable;
|
|
|
3
3
|
EnvironmentVariable["SC_ACCESS_TOKEN"] = "SC_ACCESS_TOKEN";
|
|
4
4
|
EnvironmentVariable["SC_API_VERSION"] = "SC_API_VERSION";
|
|
5
5
|
EnvironmentVariable["SC_BASE_URL"] = "SC_BASE_URL";
|
|
6
|
-
EnvironmentVariable["SEMP_API_VERSION"] = "SEMP_API_VERSION";
|
|
7
6
|
})(EnvironmentVariable || (EnvironmentVariable = {}));
|
|
8
7
|
export const DefaultBaseUrl = 'https://api.solace.cloud';
|
|
9
8
|
/**
|
package/lib/exported.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { AuthType, type BasicBrokerAuth, type BrokerAuth, BrokerAuthError, BrokerAuthErrorCode, BrokerAuthManager, type OAuthBrokerAuth, } from './auth/index.js';
|
|
1
|
+
export { AuthType, type BasicBrokerAuth, type BrokerAuth, BrokerAuthError, BrokerAuthErrorCode, BrokerAuthManager, type OAuthBrokerAuth, type OrgConfig, OrgError, OrgErrorCode, OrgManager, type OrgStorage, } from './auth/index.js';
|
|
2
2
|
export { EnvironmentVariable, envVars } from './config/env-vars.js';
|
|
3
3
|
export { ScCommand } from './sc-command.js';
|
|
4
|
-
export { ScConnection } from './util/sc-connection.js';
|
|
4
|
+
export { type ApiType, type HttpAuthType, ScConnection, type ScConnectionOptions } from './util/sc-connection.js';
|
|
5
5
|
export { sleep } from './util/util.js';
|
|
6
6
|
export * from './ux/table.js';
|
package/lib/exported.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { AuthType, BrokerAuthError, BrokerAuthErrorCode, BrokerAuthManager, } from './auth/index.js';
|
|
1
|
+
export { AuthType, BrokerAuthError, BrokerAuthErrorCode, BrokerAuthManager, OrgError, OrgErrorCode, OrgManager, } from './auth/index.js';
|
|
2
2
|
export { EnvironmentVariable, envVars } from './config/env-vars.js';
|
|
3
3
|
export { ScCommand } from './sc-command.js';
|
|
4
4
|
export { ScConnection } from './util/sc-connection.js';
|
package/lib/sc-command.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Command, Interfaces } from '@oclif/core';
|
|
2
|
+
import { BrokerAuthManager } from './auth/auth-manager.js';
|
|
3
|
+
import { OrgManager } from './auth/org-manager.js';
|
|
2
4
|
export type Flags<T extends typeof Command> = Interfaces.InferredFlags<(typeof ScCommand)['baseFlags'] & T['flags']>;
|
|
3
5
|
export type Args<T extends typeof Command> = Interfaces.InferredArgs<T['args']>;
|
|
4
6
|
/**
|
|
@@ -14,9 +16,21 @@ export declare abstract class ScCommand<T extends typeof Command> extends Comman
|
|
|
14
16
|
static enableJsonFlag: boolean;
|
|
15
17
|
protected args: Args<T>;
|
|
16
18
|
protected flags: Flags<T>;
|
|
19
|
+
private _brokerAuthManager?;
|
|
20
|
+
private _orgManager?;
|
|
17
21
|
protected catch(err: Error & {
|
|
18
22
|
exitCode?: number;
|
|
19
23
|
}): Promise<unknown>;
|
|
20
24
|
protected finally(_: Error | undefined): Promise<unknown>;
|
|
25
|
+
/**
|
|
26
|
+
* Get BrokerAuthManager instance with lazy initialization
|
|
27
|
+
* @returns Initialized BrokerAuthManager instance
|
|
28
|
+
*/
|
|
29
|
+
protected getBrokerAuthManager(): Promise<BrokerAuthManager>;
|
|
30
|
+
/**
|
|
31
|
+
* Get OrgManager instance with lazy initialization
|
|
32
|
+
* @returns Initialized OrgManager instance
|
|
33
|
+
*/
|
|
34
|
+
protected getOrgManager(): Promise<OrgManager>;
|
|
21
35
|
init(): Promise<void>;
|
|
22
36
|
}
|
package/lib/sc-command.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Command, Flags } from '@oclif/core';
|
|
2
|
-
import {
|
|
2
|
+
import { BrokerAuthManager } from './auth/auth-manager.js';
|
|
3
|
+
import { OrgManager } from './auth/org-manager.js';
|
|
3
4
|
/**
|
|
4
5
|
* A base command that provided common functionality for all sc commands.
|
|
5
6
|
*
|
|
@@ -20,6 +21,8 @@ export class ScCommand extends Command {
|
|
|
20
21
|
static enableJsonFlag = true;
|
|
21
22
|
args;
|
|
22
23
|
flags;
|
|
24
|
+
_brokerAuthManager;
|
|
25
|
+
_orgManager;
|
|
23
26
|
async catch(err) {
|
|
24
27
|
// add any custom logic to handle errors from the command
|
|
25
28
|
// or simply return the parent class error handling
|
|
@@ -29,6 +32,28 @@ export class ScCommand extends Command {
|
|
|
29
32
|
// called after run and catch regardless of whether or not the command errored
|
|
30
33
|
return super.finally(_);
|
|
31
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Get BrokerAuthManager instance with lazy initialization
|
|
37
|
+
* @returns Initialized BrokerAuthManager instance
|
|
38
|
+
*/
|
|
39
|
+
async getBrokerAuthManager() {
|
|
40
|
+
if (!this._brokerAuthManager) {
|
|
41
|
+
this._brokerAuthManager = BrokerAuthManager.getInstance();
|
|
42
|
+
await this._brokerAuthManager.initialize();
|
|
43
|
+
}
|
|
44
|
+
return this._brokerAuthManager;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get OrgManager instance with lazy initialization
|
|
48
|
+
* @returns Initialized OrgManager instance
|
|
49
|
+
*/
|
|
50
|
+
async getOrgManager() {
|
|
51
|
+
if (!this._orgManager) {
|
|
52
|
+
this._orgManager = OrgManager.getInstance();
|
|
53
|
+
await this._orgManager.initialize();
|
|
54
|
+
}
|
|
55
|
+
return this._orgManager;
|
|
56
|
+
}
|
|
32
57
|
async init() {
|
|
33
58
|
await super.init();
|
|
34
59
|
const { args, flags } = await this.parse({
|
|
@@ -40,15 +65,5 @@ export class ScCommand extends Command {
|
|
|
40
65
|
});
|
|
41
66
|
this.flags = flags;
|
|
42
67
|
this.args = args;
|
|
43
|
-
// set base url if not defined in environment variables
|
|
44
|
-
if (!envVars.getString(EnvironmentVariable.SC_BASE_URL)) {
|
|
45
|
-
this.debug(`Environment variable '${EnvironmentVariable.SC_BASE_URL}' not set, using default '${DefaultBaseUrl}'`);
|
|
46
|
-
envVars.setString(EnvironmentVariable.SC_BASE_URL, DefaultBaseUrl);
|
|
47
|
-
}
|
|
48
|
-
// Check if Access Token is set
|
|
49
|
-
const value = envVars.getString(EnvironmentVariable.SC_ACCESS_TOKEN);
|
|
50
|
-
if (!value) {
|
|
51
|
-
this.error(`Environment variable ${EnvironmentVariable.SC_ACCESS_TOKEN} is not set and required for any API operations.`);
|
|
52
|
-
}
|
|
53
68
|
}
|
|
54
69
|
}
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import { AxiosRequestConfig } from 'axios';
|
|
2
|
+
export type HttpAuthType = 'basic' | 'bearer';
|
|
3
|
+
export type ApiType = 'cloud' | 'semp';
|
|
4
|
+
export interface ScConnectionOptions {
|
|
5
|
+
apiType?: ApiType;
|
|
6
|
+
apiVersion?: string;
|
|
7
|
+
authType?: HttpAuthType;
|
|
8
|
+
timeout?: number;
|
|
9
|
+
}
|
|
2
10
|
export declare class ScConnection {
|
|
3
11
|
private axiosInstance;
|
|
4
12
|
private endpointUrl;
|
|
5
|
-
constructor(baseURL
|
|
13
|
+
constructor(baseURL: string, accessToken: string, options?: ScConnectionOptions);
|
|
6
14
|
delete<T>(url: string, config?: AxiosRequestConfig): Promise<T>;
|
|
7
15
|
get<T>(url: string, config?: AxiosRequestConfig): Promise<T>;
|
|
8
16
|
patch<T>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T>;
|
|
@@ -1,23 +1,33 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
-
import { DefaultBaseUrl, EnvironmentVariable, envVars } from '../config/env-vars.js';
|
|
3
2
|
export class ScConnection {
|
|
4
3
|
axiosInstance;
|
|
5
4
|
endpointUrl = '';
|
|
6
|
-
constructor(baseURL
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
constructor(baseURL, accessToken, options) {
|
|
6
|
+
// Validate required parameters
|
|
7
|
+
if (!baseURL || baseURL.trim() === '') {
|
|
8
|
+
throw new Error('baseURL is required and cannot be empty');
|
|
9
|
+
}
|
|
10
|
+
if (!accessToken || accessToken.trim() === '') {
|
|
11
|
+
throw new Error('accessToken is required and cannot be empty');
|
|
12
|
+
}
|
|
13
|
+
// Extract options with defaults
|
|
14
|
+
const { apiType = 'cloud', apiVersion = 'v2', authType = 'bearer', timeout = 10_000 } = options ?? {};
|
|
15
|
+
// Build endpoint URL based on apiType
|
|
16
|
+
// For SEMP: use baseURL directly (version specified in actual API calls)
|
|
17
|
+
// For cloud: append /api/{version}
|
|
18
|
+
const apiPath = apiType === 'semp' ? '' : `/api/${apiVersion}`;
|
|
19
|
+
this.endpointUrl = apiPath ? this.joinPaths(baseURL, apiPath) : baseURL;
|
|
20
|
+
// Build authorization header based on authType
|
|
21
|
+
const authHeader = authType === 'basic' ? `Basic ${accessToken}` : `Bearer ${accessToken}`;
|
|
12
22
|
this.axiosInstance = axios.create({
|
|
13
23
|
baseURL: this.endpointUrl,
|
|
14
24
|
headers: {
|
|
15
|
-
Authorization:
|
|
25
|
+
Authorization: authHeader,
|
|
16
26
|
'Content-Type': 'application/json',
|
|
17
27
|
},
|
|
18
28
|
timeout,
|
|
19
29
|
});
|
|
20
|
-
// Add interceptors
|
|
30
|
+
// Add interceptors
|
|
21
31
|
this.axiosInstance.interceptors.response.use((response) => response, (error) => {
|
|
22
32
|
console.error('API Error:', error.response?.data || error.message);
|
|
23
33
|
return Promise.reject(error);
|