fix data setting get datatable using api token

This commit is contained in:
arifal
2025-06-17 11:54:02 +07:00
parent a8b02afad9
commit 285ff46c2b
10 changed files with 586 additions and 56 deletions

View File

@@ -0,0 +1,198 @@
/**
* API Token Manager Utility
* Handles API token generation, storage, and usage
*/
class ApiTokenManager {
constructor() {
this.token = null;
this.tokenKey = 'api_token';
this.init();
}
/**
* Initialize token manager
*/
init() {
// Try to get token from meta tag first (session-based)
const metaToken = document.querySelector('meta[name="api-token"]');
if (metaToken && metaToken.getAttribute('content')) {
this.token = metaToken.getAttribute('content');
} else {
// Try to get from localStorage as fallback
this.token = localStorage.getItem(this.tokenKey);
}
}
/**
* Generate new API token
* @returns {Promise<string|null>}
*/
async generateToken() {
try {
const response = await fetch('/api-tokens/generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
},
credentials: 'include'
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (data.token) {
this.setToken(data.token);
return data.token;
}
throw new Error('No token received');
} catch (error) {
console.error('Failed to generate token:', error);
return null;
}
}
/**
* Revoke current API token
* @returns {Promise<boolean>}
*/
async revokeToken() {
try {
const response = await fetch('/api-tokens/revoke', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
'Authorization': `Bearer ${this.token}`
},
credentials: 'include'
});
if (response.ok) {
this.clearToken();
return true;
}
return false;
} catch (error) {
console.error('Failed to revoke token:', error);
return false;
}
}
/**
* Set token and update storage
* @param {string} token
*/
setToken(token) {
this.token = token;
localStorage.setItem(this.tokenKey, token);
// Update meta tag if exists
const metaTag = document.querySelector('meta[name="api-token"]');
if (metaTag) {
metaTag.setAttribute('content', token);
}
}
/**
* Get current token
* @returns {string|null}
*/
getToken() {
return this.token;
}
/**
* Clear token from storage
*/
clearToken() {
this.token = null;
localStorage.removeItem(this.tokenKey);
// Clear meta tag if exists
const metaTag = document.querySelector('meta[name="api-token"]');
if (metaTag) {
metaTag.setAttribute('content', '');
}
}
/**
* Check if token exists
* @returns {boolean}
*/
hasToken() {
return this.token !== null && this.token !== '';
}
/**
* Get authorization header
* @returns {object}
*/
getAuthHeader() {
if (!this.hasToken()) {
return {};
}
return {
'Authorization': `Bearer ${this.token}`
};
}
/**
* Make authenticated API request
* @param {string} url
* @param {object} options
* @returns {Promise<Response>}
*/
async request(url, options = {}) {
const defaultOptions = {
headers: {
'Content-Type': 'application/json',
...this.getAuthHeader(),
...(options.headers || {})
},
credentials: 'include'
};
const mergedOptions = {
...defaultOptions,
...options,
headers: {
...defaultOptions.headers,
...(options.headers || {})
}
};
try {
const response = await fetch(url, mergedOptions);
// If unauthorized, try to generate new token
if (response.status === 401 && this.hasToken()) {
console.log('Token expired, generating new token...');
const newToken = await this.generateToken();
if (newToken) {
// Retry request with new token
mergedOptions.headers.Authorization = `Bearer ${newToken}`;
return fetch(url, mergedOptions);
}
}
return response;
} catch (error) {
console.error('API request failed:', error);
throw error;
}
}
}
// Export singleton instance
const apiTokenManager = new ApiTokenManager();
window.ApiTokenManager = apiTokenManager;
export default apiTokenManager;

View File

@@ -0,0 +1,166 @@
/**
* Modern API Helper with Token Management
*/
import ApiTokenManager from './api-token-manager.js';
class ApiHelper {
constructor() {
this.baseUrl = window.GlobalConfig?.apiHost || '';
this.tokenManager = ApiTokenManager;
}
/**
* Make GET request
* @param {string} endpoint
* @param {object} options
* @returns {Promise<any>}
*/
async get(endpoint, options = {}) {
return this.request(endpoint, {
method: 'GET',
...options
});
}
/**
* Make POST request
* @param {string} endpoint
* @param {object} data
* @param {object} options
* @returns {Promise<any>}
*/
async post(endpoint, data = null, options = {}) {
return this.request(endpoint, {
method: 'POST',
body: data ? JSON.stringify(data) : null,
...options
});
}
/**
* Make PUT request
* @param {string} endpoint
* @param {object} data
* @param {object} options
* @returns {Promise<any>}
*/
async put(endpoint, data = null, options = {}) {
return this.request(endpoint, {
method: 'PUT',
body: data ? JSON.stringify(data) : null,
...options
});
}
/**
* Make DELETE request
* @param {string} endpoint
* @param {object} options
* @returns {Promise<any>}
*/
async delete(endpoint, options = {}) {
return this.request(endpoint, {
method: 'DELETE',
...options
});
}
/**
* Make authenticated request using token manager
* @param {string} endpoint
* @param {object} options
* @returns {Promise<any>}
*/
async request(endpoint, options = {}) {
const url = `${this.baseUrl}${endpoint}`;
try {
const response = await this.tokenManager.request(url, options);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('API request failed:', error);
throw error;
}
}
/**
* Upload file with authentication
* @param {string} endpoint
* @param {FormData} formData
* @param {object} options
* @returns {Promise<any>}
*/
async upload(endpoint, formData, options = {}) {
const url = `${this.baseUrl}${endpoint}`;
const uploadOptions = {
method: 'POST',
body: formData,
headers: {
// Don't set Content-Type for FormData, let browser set it
...this.tokenManager.getAuthHeader(),
...(options.headers || {})
},
credentials: 'include',
...options
};
try {
const response = await fetch(url, uploadOptions);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Upload failed:', error);
throw error;
}
}
/**
* Download file with authentication
* @param {string} endpoint
* @param {string} filename
* @param {object} options
*/
async download(endpoint, filename = null, options = {}) {
const url = `${this.baseUrl}${endpoint}`;
try {
const response = await this.tokenManager.request(url, {
method: 'GET',
...options
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const blob = await response.blob();
const downloadUrl = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = downloadUrl;
link.download = filename || 'download';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(downloadUrl);
} catch (error) {
console.error('Download failed:', error);
throw error;
}
}
}
// Export singleton instance
const apiHelper = new ApiHelper();
window.ApiHelper = apiHelper;
export default apiHelper;