@mindmakr/gs-websdk
Version:
Web SDK for Guru SaaS System - Complete JavaScript/TypeScript SDK for building applications with dynamic schema management
445 lines • 15.3 kB
JavaScript
"use strict";
/**
* Core SDK Client - Main entry point for the Guru SaaS Web SDK
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GuruSaaSClient = void 0;
const axios_1 = __importDefault(require("axios"));
const types_1 = require("../types");
class GuruSaaSClient {
constructor(config = {}) {
this.tokens = null;
this.refreshPromise = null;
this.user = null;
// Set default configuration
this.config = {
baseUrl: config.baseUrl || 'http://localhost',
authUrl: config.authUrl || `${config.baseUrl || 'http://localhost'}:4000`,
globalDataUrl: config.globalDataUrl || `${config.baseUrl || 'http://localhost'}:5010`,
aiServiceUrl: config.aiServiceUrl || `${config.baseUrl || 'http://localhost'}:4001`,
notificationUrl: config.notificationUrl || `${config.baseUrl || 'http://localhost'}:5020`,
timeout: config.timeout || 30000,
retryAttempts: config.retryAttempts || 3,
debug: config.debug || false,
defaultTenant: config.defaultTenant || '',
autoRefreshToken: config.autoRefreshToken !== false
};
// Create axios instance
this.axiosInstance = axios_1.default.create({
timeout: this.config.timeout,
headers: {
'Content-Type': 'application/json'
}
});
// Set up request interceptor
this.axiosInstance.interceptors.request.use((config) => this.handleRequest(config), (error) => Promise.reject(error));
// Set up response interceptor
this.axiosInstance.interceptors.response.use((response) => this.handleResponse(response), (error) => this.handleResponseError(error));
// Try to restore tokens from storage
this.restoreTokensFromStorage();
}
// ============================================================================
// PUBLIC METHODS
// ============================================================================
/**
* Get current configuration
*/
getConfig() {
return { ...this.config };
}
/**
* Update configuration
*/
updateConfig(newConfig) {
this.config = { ...this.config, ...newConfig };
}
/**
* Check if user is authenticated
*/
isAuthenticated() {
return this.tokens !== null && this.tokens.token !== '';
}
/**
* Get current user
*/
getCurrentUser() {
return this.user;
}
/**
* Get current auth tokens
*/
getTokens() {
return this.tokens;
}
/**
* Set auth tokens manually
*/
setTokens(tokens) {
this.tokens = tokens;
this.storeTokensInStorage();
}
/**
* Clear authentication data
*/
clearAuth() {
this.tokens = null;
this.user = null;
this.clearTokensFromStorage();
}
/**
* Make an authenticated request to any service
*/
async request(method, url, data, config) {
const requestConfig = {
method,
url: this.resolveUrl(url),
data,
timeout: config?.timeout || this.config.timeout,
headers: {
...config?.headers
}
};
// Add tenant header if specified
if (config?.tenantId) {
requestConfig.headers['X-Tenant-ID'] = config.tenantId;
}
else if (this.config.defaultTenant) {
requestConfig.headers['X-Tenant-ID'] = this.config.defaultTenant;
}
const response = await this.axiosInstance.request(requestConfig);
return response.data;
}
/**
* GET request helper
*/
async get(url, config) {
return this.request('GET', url, undefined, config);
}
/**
* POST request helper
*/
async post(url, data, config) {
return this.request('POST', url, data, config);
}
/**
* PUT request helper
*/
async put(url, data, config) {
return this.request('PUT', url, data, config);
}
/**
* DELETE request helper
*/
async delete(url, config) {
return this.request('DELETE', url, undefined, config);
}
/**
* PATCH request helper
*/
async patch(url, data, config) {
return this.request('PATCH', url, data, config);
}
// ============================================================================
// AUTHENTICATION METHODS
// ============================================================================
/**
* Login with email and password
*/
async login(email, password) {
try {
const response = await this.axiosInstance.post(`${this.config.authUrl}/api/auth/login`, {
email,
password
});
const { user, token, access_token, refresh_token, expires_in, token_type } = response.data;
const tokens = {
token: token || access_token,
refreshToken: refresh_token,
expiresIn: expires_in,
tokenType: token_type || 'Bearer'
};
this.tokens = tokens;
this.user = user;
this.storeTokensInStorage();
return { user, tokens };
}
catch (error) {
const authError = new types_1.AuthenticationError('Login failed', error);
throw authError;
}
}
/**
* Request OTP for email/password
*/
async requestOTP(email, password) {
try {
const response = await this.axiosInstance.post(`${this.config.authUrl}/api/auth/request-otp`, {
email,
password
});
return response.data;
}
catch (error) {
const authError = new types_1.AuthenticationError('OTP request failed', error);
throw authError;
}
}
/**
* Verify OTP and complete login
*/
async verifyOTP(otpToken, otpCode, options) {
try {
const response = await this.axiosInstance.post(`${this.config.authUrl}/api/auth/verify-otp`, {
otp_token: otpToken,
otp_code: otpCode,
remember_device: options?.rememberDevice,
device_name: options?.deviceName
});
const { user, token, access_token, refresh_token, expires_in, token_type } = response.data;
const tokens = {
token: token || access_token,
refreshToken: refresh_token,
expiresIn: expires_in,
tokenType: token_type || 'Bearer'
};
this.tokens = tokens;
this.user = user;
this.storeTokensInStorage();
return { user, tokens };
}
catch (error) {
const authError = new types_1.AuthenticationError('OTP verification failed', error);
throw authError;
}
}
/**
* Register new user
*/
async register(userData) {
try {
const response = await this.axiosInstance.post(`${this.config.authUrl}/api/auth/register`, userData);
return response.data;
}
catch (error) {
const authError = new types_1.AuthenticationError('Registration failed', error);
throw authError;
}
}
/**
* Refresh access token
*/
async refreshToken() {
if (this.refreshPromise) {
return this.refreshPromise;
}
if (!this.tokens?.refreshToken) {
throw new types_1.AuthenticationError('No refresh token available');
}
this.refreshPromise = this.performTokenRefresh();
try {
const tokens = await this.refreshPromise;
return tokens;
}
finally {
this.refreshPromise = null;
}
}
/**
* Logout current user
*/
async logout() {
try {
if (this.tokens?.refreshToken) {
await this.axiosInstance.post(`${this.config.authUrl}/api/auth/logout`, { refresh_token: this.tokens.refreshToken }, {
headers: this.getAuthHeaders()
});
}
}
catch (error) {
// Continue with logout even if API call fails
this.log('Logout API call failed:', error);
}
finally {
this.clearAuth();
}
}
/**
* Get current user info
*/
async me() {
try {
const response = await this.axiosInstance.get(`${this.config.authUrl}/api/auth/me`, {
headers: this.getAuthHeaders()
});
this.user = response.data;
return response.data;
}
catch (error) {
throw new types_1.AuthenticationError('Failed to get user info', error);
}
}
// ============================================================================
// PRIVATE METHODS
// ============================================================================
async performTokenRefresh() {
try {
const response = await this.axiosInstance.post(`${this.config.authUrl}/api/auth/refresh`, {
refresh_token: this.tokens.refreshToken
});
const { token, access_token, refresh_token, expires_in, token_type } = response.data;
const tokens = {
token: token || access_token,
refreshToken: refresh_token || this.tokens.refreshToken,
expiresIn: expires_in,
tokenType: token_type || 'Bearer'
};
this.tokens = tokens;
this.storeTokensInStorage();
return tokens;
}
catch (error) {
this.clearAuth();
const authError = new types_1.AuthenticationError('Token refresh failed', error);
throw authError;
}
}
handleRequest(config) {
// Add auth headers if authenticated
if (this.isAuthenticated() && this.tokens) {
config.headers = {
...config.headers,
...this.getAuthHeaders()
};
}
// Log request in debug mode
if (this.config.debug) {
this.log('Request:', config.method?.toUpperCase(), config.url);
}
return config;
}
handleResponse(response) {
if (this.config.debug) {
this.log('Response:', response.status, response.config.url);
}
return response;
}
async handleResponseError(error) {
if (this.config.debug) {
this.log('Response error:', error?.response?.status, error?.config?.url, error?.message);
}
// Handle authentication errors
if (error?.response?.status === 401 && this.config.autoRefreshToken && this.tokens?.refreshToken) {
try {
await this.refreshToken();
// Retry the original request
return this.axiosInstance.request(error.config);
}
catch (refreshError) {
// Refresh failed, clear auth and propagate error
this.clearAuth();
throw new types_1.AuthenticationError('Authentication failed', refreshError);
}
}
// Transform axios errors to SDK errors
if (error?.response) {
const { status, data } = error.response;
const message = data?.message || data?.error || `Request failed with status ${status}`;
throw new types_1.SDKError(message, 'API_ERROR', status, data);
}
else if (error?.request) {
throw new types_1.NetworkError('Network request failed', error);
}
else {
throw new types_1.SDKError(error?.message || 'Unknown error', 'UNKNOWN_ERROR');
}
}
resolveUrl(url) {
// If URL is already absolute, return as-is
if (url.startsWith('http://') || url.startsWith('https://')) {
return url;
}
// Determine the base URL based on the endpoint
if (url.startsWith('/api/auth/') ||
url.startsWith('/api/user') ||
url.startsWith('/api/role') ||
url.startsWith('/api/permission') ||
url.startsWith('/api/admin/') ||
url.startsWith('/api/tenant/') ||
url.startsWith('/api/settings/')) {
return `${this.config.authUrl}${url}`;
}
else if (url.startsWith('/api/schema-') || url.startsWith('/api/performance/')) {
return `${this.config.globalDataUrl}${url}`;
}
else if (url.startsWith('/api/v1/ai/') || url.startsWith('/api/ai/') || url === '/health') {
return `${this.config.aiServiceUrl}${url}`;
}
else if (url.startsWith('/api/notification/')) {
return `${this.config.notificationUrl}${url}`;
}
else {
// Default to global data service
return `${this.config.globalDataUrl}${url}`;
}
}
getAuthHeaders() {
if (!this.tokens) {
return {};
}
return {
Authorization: `${this.tokens.tokenType} ${this.tokens.token}`
};
}
storeTokensInStorage() {
if (typeof window !== 'undefined' && window.localStorage && this.tokens) {
try {
localStorage.setItem('gs_auth_tokens', JSON.stringify(this.tokens));
if (this.user) {
localStorage.setItem('gs_user', JSON.stringify(this.user));
}
}
catch (error) {
this.log('Failed to store tokens in localStorage:', error);
}
}
}
restoreTokensFromStorage() {
if (typeof window !== 'undefined' && window.localStorage) {
try {
const tokensJson = localStorage.getItem('gs_auth_tokens');
const userJson = localStorage.getItem('gs_user');
if (tokensJson) {
this.tokens = JSON.parse(tokensJson);
}
if (userJson) {
this.user = JSON.parse(userJson);
}
}
catch (error) {
this.log('Failed to restore tokens from localStorage:', error);
this.clearTokensFromStorage();
}
}
}
clearTokensFromStorage() {
if (typeof window !== 'undefined' && window.localStorage) {
try {
localStorage.removeItem('gs_auth_tokens');
localStorage.removeItem('gs_user');
}
catch (error) {
this.log('Failed to clear tokens from localStorage:', error);
}
}
}
log(...args) {
if (this.config.debug) {
console.log('[GuruSaaS SDK]', ...args);
}
}
}
exports.GuruSaaSClient = GuruSaaSClient;
//# sourceMappingURL=client.js.map