import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'; import qs from 'qs'; export interface ActionParams { filterByTk?: any; [key: string]: any; } type ResourceActionOptions

= { resource?: string; resourceOf?: any; action?: string; params?: P; }; export interface IResource { list?: (params?: ActionParams) => Promise; get?: (params?: ActionParams) => Promise; create?: (params?: ActionParams) => Promise; update?: (params?: ActionParams) => Promise; destroy?: (params?: ActionParams) => Promise; [key: string]: (params?: ActionParams) => Promise; } export class Auth { protected api: APIClient; protected NOCOBASE_LOCALE_KEY = 'NOCOBASE_LOCALE'; protected NOCOBASE_ROLE_KEY = 'NOCOBASE_ROLE'; protected options = { token: null, locale: null, role: null, }; constructor(api: APIClient) { this.api = api; this.initKeys(); this.locale = this.getLocale(); this.role = this.getRole(); this.token = this.getToken(); this.api.axios.interceptors.request.use(this.middleware.bind(this)); } initKeys() { if (!window) { return; } const match = window.location.pathname.match(/^\/apps\/([^/]*)\//); if (!match) { return; } const appName = match[1]; this.NOCOBASE_LOCALE_KEY = `${appName.toUpperCase()}_NOCOBASE_LOCALE`; this.NOCOBASE_ROLE_KEY = `${appName.toUpperCase()}_NOCOBASE_ROLE`; } get locale() { return this.getLocale(); } get role() { return this.getRole(); } get token() { return this.getToken(); } set locale(value) { this.setLocale(value); } set role(value) { this.setRole(value); } set token(value) { this.setToken(value); } middleware(config: AxiosRequestConfig) { if (this.locale) { config.headers['X-Locale'] = this.locale; } if (this.role) { config.headers['X-Role'] = this.role; } if (this.token) { config.headers['Authorization'] = `Bearer ${this.token}`; } return config; } getLocale() { return this.api.storage.getItem(this.NOCOBASE_LOCALE_KEY); } setLocale(locale: string) { this.options.locale = locale; this.api.storage.setItem(this.NOCOBASE_LOCALE_KEY, locale || ''); } getToken() { return this.api.storage.getItem('NOCOBASE_TOKEN'); } setToken(token: string) { this.options.token = token; this.api.storage.setItem('NOCOBASE_TOKEN', token || ''); if (!token) { this.setRole(null); // this.setLocale(null); } } getRole() { return this.api.storage.getItem(this.NOCOBASE_ROLE_KEY); } setRole(role: string) { this.options.role = role; this.api.storage.setItem(this.NOCOBASE_ROLE_KEY, role || ''); } async signIn(values, authenticator = 'password'): Promise> { const response = await this.api.request({ method: 'post', url: 'users:signin', data: values, params: { authenticator, }, }); const data = response?.data?.data; this.setToken(data?.token); return response; } async signOut() { await this.api.request({ method: 'post', url: 'users:signout', }); this.setToken(null); } } export abstract class Storage { abstract clear(): void; abstract getItem(key: string): string | null; abstract removeItem(key: string): void; abstract setItem(key: string, value: string): void; } export class MemoryStorage extends Storage { items = new Map(); clear() { this.items.clear(); } getItem(key: string) { return this.items.get(key); } setItem(key: string, value: string) { return this.items.set(key, value); } removeItem(key: string) { return this.items.delete(key); } } interface ExtendedOptions { authClass?: any; storageClass?: any; } export class APIClient { axios: AxiosInstance; auth: Auth; storage: Storage; constructor(instance?: AxiosInstance | (AxiosRequestConfig & ExtendedOptions)) { if (typeof instance === 'function') { this.axios = instance; } else { const { authClass, storageClass, ...others } = instance || {}; this.axios = axios.create(others); this.initStorage(storageClass); if (authClass) { this.auth = new authClass(this); } } if (!this.storage) { this.initStorage(); } if (!this.auth) { this.auth = new Auth(this); } this.interceptors(); } private initStorage(storage?: any) { if (storage) { this.storage = new storage(this); } else if (typeof localStorage !== 'undefined') { this.storage = localStorage; } else { this.storage = new MemoryStorage(); } } interceptors() { this.axios.interceptors.request.use((config) => { config.paramsSerializer = (params) => { return qs.stringify(params, { strictNullHandling: true, arrayFormat: 'brackets', }); }; return config; }); } request, D = any>(config: AxiosRequestConfig | ResourceActionOptions): Promise { const { resource, resourceOf, action, params } = config as any; if (resource) { return this.resource(resource, resourceOf)[action](params); } return this.axios.request(config); } resource(name: string, of?: any): IResource { const target = {}; const handler = { get: (_: any, actionName: string) => { let url = name.split('.').join(`/${of || '_'}/`); url += `:${actionName}`; const config: AxiosRequestConfig = { url }; if (['get', 'list'].includes(actionName)) { config['method'] = 'get'; } else { config['method'] = 'post'; } return async (params?: ActionParams, opts?: any) => { const { values, filter, ...others } = params || {}; config['params'] = others; if (filter) { if (typeof filter === 'string') { config['params']['filter'] = filter; } else { config['params']['filter'] = JSON.stringify(filter); } } if (config.method !== 'get') { config['data'] = values || {}; } return await this.request({ ...config, ...opts, }); }; }, }; return new Proxy(target, handler); } }