fix data setting get datatable using api token
This commit is contained in:
198
resources/js/utils/api-token-manager.js
Normal file
198
resources/js/utils/api-token-manager.js
Normal 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;
|
||||
166
resources/js/utils/modern-api-helper.js
Normal file
166
resources/js/utils/modern-api-helper.js
Normal 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;
|
||||
Reference in New Issue
Block a user