* feat: create custom-theme plugin
* feat: add custom-theme
* chore: add plugin name and description
* chore: add deps
* chore: optimize deps
* refactor: rename
* chore: add antd-token-previewer
* chore: add deps in plugin
* feat: add theme-editor
* feat: add theme-editor
* feat: add settings
* feat: add theme collection
* refactor: migration to the components folder
* feat: add ThemeList
* refactor: be better
* feat: export createStyles
* feat: implement ThemeCard (T-723)
* style: optimize style
* feat: add ThemeEditorProvider
* feat: add ToEditTheme
* chore: add isBuiltIn field
* feat: implement WYSIWYG
* refactor: migrate i18n
* feat: support to save config
* feat: add validation rule
* refactor: remove useless code
* refactor: optimize GlobalThemeProvider
* feat: add CurrentUserSettingsMenuProvider
* feat: support switching themes
* refactor: migrate CurrentUserProvider to app root
* feat: add InitializeTheme
* fix: fix preview demo
* fix: filter themes
* fix: not valid when editing theme
* fix: should restore the previous theme when closing theme editor
* fix: fix algorithm
* fix: the theme that was just saved should be applied
* refactor: loacl antd-token-previewer
* fix: should be based on the current theme when editing themes
* feat: support to edit theme
* perf: reduce executions
* feat: add animation
* fix: fix the type error of useRequest
* feat: remove built-in themes
* chore: reinstall deps
* fix: fix version of deps
* fix: delete client.d.ts to fix build error
* chore: reinstall deps
* fix: fix build
* fix: fix build
* fix: avoid build errors
* fix: fix crashing
* fix: use value instead of defaultValue
* fix: avoid error
* fix: avoid closure
* fix: fix build
* fix: fix style of login page
* refactor(page): fix style
* fix: fix style of PageHeader
* refactor: fix style of Drawer
* refactor: add FormDialog to loacl
* refactor: fix style of SchemaSettings.ModalItem
* refactor: fix style of pm/Card
* fix: fix text color of pm/Marketplace
* fix: fix table error
* refactor: fix style of collection-manager/summary
* refactor: fix style of fields drawer
* chore: reinstall deps
* fix: fix build
* fix: fix build of custom-theme
* fix: should export Plugin
* refactor: fix style of GraphDrawPage
* chore: upgrade plugin version
* refactor: fix style of Modal by using antd App
* refactor: fix style of FormDialog by using local version
* refactor(workflow): refactor style using antd-style in workflow
* fix(workflow): fix style of workflow
* fix: fix size
* refactor: add --nb-header-height
* feat: remove theme configuration from system settings
* refactor: migrate useUpdateThemeSettings to a new file
* refactor: rename theme to themeId
* feat: add updateSystemThemeSettings
* refactor: migrate utils function
* feat: use localStorage to avoid theme invalid in login page
* fix: fix style of login page
* fix: fix style of Drawer
* feat: optimize style of theme card
* fix: should use a empty object to reset theme
* fix: fix test of Page
* fix: fix test of Application
* fix: change backgroundColor of login page
* fix: fix all style of modal
* fix: fix gap between blocks (T-896)
* fix: fix color of font (T-905)
* fix: fix build
* fix: fix can not scroll in Drawer (T-897)
* fix: fix width of built-in plugins page (T-900)
* fix: fix style of import Modal (T-907)
* fix: fix style of calendar (T-908)
* fix: fix style of kanban (T-909)
* fix: fix style of Gantt (T-910)
* fix: fix style of Collapse (T-915)
* fix: fix style of mobile (T-916)
* fix: fix style of PageHeader (T-958)
* fix: optimize background color of Drawer
* fix: fix style of notification
* fix: fix T-1000
* fix: fix style of LinkageHeader (T-1003)
* fix: fix T-1004
* fix: fix style of scroll bar of chart (T-911)
* fix: fix style of workflow drawer (T-997)
* fix: fix T-922
* fix: fix T-924
* refactor: rename custom-theme to theme-editor
* fix: fix T-999
* fix: fix T-1011
* fix: fix error
* fix: optimize dark mode (T-921)
* fix: fix style of markdown (T-1020)
* fix: fix style of data template (T-1025)
* fix: fix style of rich text (T-1026)
* fix: fix style of a
* fix: fix style of XButton (T-1028)
* fix: fix T-1027
* fix: fix color of variable tag (T-1030)
* chore: translation
* feat: add a modal before create new theme (T-1024)
* feat: highlight card when editing theme (T-1031)
* feat: support to change theme name (T-1023)
* fix: api.auth.getOption('theme')
* fix: fix T-1032
* fix: fix feedback in feishu group
* refactor: code review
* fix: fix test
* chore: rename
* fix: useNotificationMiddleware
* refactor: revert
* fix: fix build
* fix: notification
* refactor: migrate CurrentUserProvider from Application to NocoBaseBuildInPlugin
* fix: fix test
* refactor: fix code review
* chore: avoid test error
---------
Co-authored-by: chenos <chenlinxh@gmail.com>
322 lines
7.3 KiB
TypeScript
322 lines
7.3 KiB
TypeScript
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
|
|
import qs from 'qs';
|
|
import getSubAppName from './getSubAppName';
|
|
|
|
export interface ActionParams {
|
|
filterByTk?: any;
|
|
[key: string]: any;
|
|
}
|
|
|
|
type ResourceActionOptions<P = any> = {
|
|
resource?: string;
|
|
resourceOf?: any;
|
|
action?: string;
|
|
params?: P;
|
|
};
|
|
|
|
export interface IResource {
|
|
list?: (params?: ActionParams) => Promise<any>;
|
|
get?: (params?: ActionParams) => Promise<any>;
|
|
create?: (params?: ActionParams) => Promise<any>;
|
|
update?: (params?: ActionParams) => Promise<any>;
|
|
destroy?: (params?: ActionParams) => Promise<any>;
|
|
[key: string]: (params?: ActionParams) => Promise<any>;
|
|
}
|
|
|
|
export class Auth {
|
|
protected api: APIClient;
|
|
|
|
protected KEYS = {
|
|
locale: 'NOCOBASE_LOCALE',
|
|
role: 'NOCOBASE_ROLE',
|
|
token: 'NOCOBASE_TOKEN',
|
|
authenticator: 'NOCOBASE_AUTH',
|
|
theme: 'NOCOBASE_THEME',
|
|
};
|
|
|
|
protected options = {
|
|
locale: null,
|
|
role: null,
|
|
authenticator: null,
|
|
token: null,
|
|
};
|
|
|
|
constructor(api: APIClient) {
|
|
this.api = api;
|
|
this.initKeys();
|
|
this.api.axios.interceptors.request.use(this.middleware.bind(this));
|
|
}
|
|
|
|
initKeys() {
|
|
if (!window) {
|
|
return;
|
|
}
|
|
const appName = getSubAppName();
|
|
if (!appName) {
|
|
return;
|
|
}
|
|
this.KEYS['role'] = `${appName.toUpperCase()}_` + this.KEYS['role'];
|
|
this.KEYS['locale'] = `${appName.toUpperCase()}_` + this.KEYS['locale'];
|
|
}
|
|
|
|
get locale() {
|
|
return this.getLocale();
|
|
}
|
|
|
|
set locale(value: string) {
|
|
this.setLocale(value);
|
|
}
|
|
|
|
get role() {
|
|
return this.getRole();
|
|
}
|
|
|
|
set role(value: string) {
|
|
this.setRole(value);
|
|
}
|
|
|
|
get token() {
|
|
return this.getToken();
|
|
}
|
|
|
|
set token(value: string) {
|
|
this.setToken(value);
|
|
}
|
|
|
|
get authenticator() {
|
|
return this.getAuthenticator();
|
|
}
|
|
|
|
set authenticator(value: string) {
|
|
this.setAuthenticator(value);
|
|
}
|
|
|
|
getOption(key: string) {
|
|
if (!this.KEYS[key]) {
|
|
return;
|
|
}
|
|
return this.api.storage.getItem(this.KEYS[key]);
|
|
}
|
|
|
|
setOption(key: string, value?: string) {
|
|
if (!this.KEYS[key]) {
|
|
return;
|
|
}
|
|
this.options[key] = value;
|
|
return this.api.storage.setItem(this.KEYS[key], value || '');
|
|
}
|
|
|
|
getLocale() {
|
|
return this.getOption('locale');
|
|
}
|
|
|
|
setLocale(locale: string) {
|
|
this.setOption('locale', locale);
|
|
}
|
|
|
|
getRole() {
|
|
return this.getOption('role');
|
|
}
|
|
|
|
setRole(role: string) {
|
|
this.setOption('role', role);
|
|
}
|
|
|
|
getToken() {
|
|
return this.getOption('token');
|
|
}
|
|
|
|
setToken(token: string) {
|
|
this.setOption('token', token);
|
|
}
|
|
|
|
getAuthenticator() {
|
|
return this.getOption('authenticator');
|
|
}
|
|
|
|
setAuthenticator(authenticator: string) {
|
|
this.setOption('authenticator', authenticator);
|
|
}
|
|
|
|
middleware(config: AxiosRequestConfig) {
|
|
if (this.locale) {
|
|
config.headers['X-Locale'] = this.locale;
|
|
}
|
|
if (this.role) {
|
|
config.headers['X-Role'] = this.role;
|
|
}
|
|
if (this.authenticator && !config.headers['X-Authenticator']) {
|
|
config.headers['X-Authenticator'] = this.authenticator;
|
|
}
|
|
if (this.token) {
|
|
config.headers['Authorization'] = `Bearer ${this.token}`;
|
|
}
|
|
return config;
|
|
}
|
|
|
|
async signIn(values: any, authenticator?: string): Promise<AxiosResponse<any>> {
|
|
const response = await this.api.request({
|
|
method: 'post',
|
|
url: 'auth:signIn',
|
|
data: values,
|
|
headers: {
|
|
'X-Authenticator': authenticator,
|
|
},
|
|
});
|
|
const data = response?.data?.data;
|
|
this.setToken(data?.token);
|
|
this.setAuthenticator(authenticator);
|
|
return response;
|
|
}
|
|
|
|
async signUp(values: any, authenticator?: string): Promise<AxiosResponse<any>> {
|
|
return await this.api.request({
|
|
method: 'post',
|
|
url: 'auth:signUp',
|
|
data: values,
|
|
headers: {
|
|
'X-Authenticator': authenticator,
|
|
},
|
|
});
|
|
}
|
|
|
|
async signOut() {
|
|
const response = await this.api.request({
|
|
method: 'post',
|
|
url: 'auth:signOut',
|
|
});
|
|
this.setToken(null);
|
|
this.setRole(null);
|
|
this.setAuthenticator(null);
|
|
return response;
|
|
}
|
|
}
|
|
|
|
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 type APIClientOptions = AxiosInstance | (AxiosRequestConfig & ExtendedOptions);
|
|
|
|
export class APIClient {
|
|
axios: AxiosInstance;
|
|
auth: Auth;
|
|
storage: Storage;
|
|
|
|
constructor(instance?: APIClientOptions) {
|
|
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<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D> | ResourceActionOptions): Promise<R> {
|
|
const { resource, resourceOf, action, params } = config as any;
|
|
if (resource) {
|
|
return this.resource(resource, resourceOf)[action](params);
|
|
}
|
|
return this.axios.request<T, R, D>(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 {
|
|
if (filter['*']) {
|
|
delete filter['*'];
|
|
}
|
|
config['params']['filter'] = JSON.stringify(filter);
|
|
}
|
|
}
|
|
if (config.method !== 'get') {
|
|
config['data'] = values || {};
|
|
}
|
|
return await this.request({
|
|
...config,
|
|
...opts,
|
|
});
|
|
};
|
|
},
|
|
};
|
|
return new Proxy(target, handler);
|
|
}
|
|
}
|