mirror of
https://github.com/portainer/portainer.git
synced 2025-08-07 14:55:27 +02:00
feat(cache): introduce cache option [EE-6293] (#10641)
This commit is contained in:
parent
fffc7b364e
commit
2c032f1739
70 changed files with 418 additions and 45 deletions
|
@ -1,5 +1,5 @@
|
|||
import { notifyError, notifySuccess } from '@/portainer/services/notifications';
|
||||
import { queryKeys } from '@/portainer/users/queries/queryKeys';
|
||||
import { userQueryKeys } from '@/portainer/users/queries/queryKeys';
|
||||
import { queryClient } from '@/react-tools/react-query';
|
||||
import { options } from '@/react/portainer/account/AccountView/theme-options';
|
||||
|
||||
|
@ -32,7 +32,7 @@ export default class ThemeSettingsController {
|
|||
try {
|
||||
if (!this.state.isDemo) {
|
||||
await this.UserService.updateUserTheme(this.state.userId, theme);
|
||||
await queryClient.invalidateQueries(queryKeys.user(this.state.userId));
|
||||
await queryClient.invalidateQueries(userQueryKeys.user(this.state.userId));
|
||||
}
|
||||
|
||||
notifySuccess('Success', 'User theme settings successfully updated');
|
||||
|
|
|
@ -12,6 +12,7 @@ export function UserViewModel(data) {
|
|||
}
|
||||
this.AuthenticationMethod = data.AuthenticationMethod;
|
||||
this.Checked = false;
|
||||
this.UseCache = data.UseCache;
|
||||
}
|
||||
|
||||
export function UserTokenModel(data) {
|
||||
|
|
17
app/portainer/react/components/account.ts
Normal file
17
app/portainer/react/components/account.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import angular from 'angular';
|
||||
|
||||
import { r2a } from '@/react-tools/react2angular';
|
||||
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||
import { ApplicationSettingsWidget } from '@/react/portainer/account/AccountView/ApplicationSettings';
|
||||
|
||||
export const accountModule = angular
|
||||
.module('portainer.app.react.components.account', [])
|
||||
.component(
|
||||
'applicationSettingsWidget',
|
||||
r2a(
|
||||
withUIRouter(withReactQuery(withCurrentUser(ApplicationSettingsWidget))),
|
||||
[]
|
||||
)
|
||||
).name;
|
|
@ -45,6 +45,7 @@ import { accessControlModule } from './access-control';
|
|||
import { environmentsModule } from './environments';
|
||||
import { envListModule } from './environments-list-view-components';
|
||||
import { registriesModule } from './registries';
|
||||
import { accountModule } from './account';
|
||||
|
||||
export const ngModule = angular
|
||||
.module('portainer.app.react.components', [
|
||||
|
@ -55,6 +56,7 @@ export const ngModule = angular
|
|||
gitFormModule,
|
||||
registriesModule,
|
||||
settingsModule,
|
||||
accountModule,
|
||||
])
|
||||
.component(
|
||||
'tagSelector',
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import axiosOrigin, { AxiosError, AxiosRequestConfig } from 'axios';
|
||||
import { setupCache } from 'axios-cache-adapter';
|
||||
import { loadProgressBar } from 'axios-progress-bar';
|
||||
|
||||
import 'axios-progress-bar/dist/nprogress.css';
|
||||
|
@ -6,12 +7,48 @@ import PortainerError from '@/portainer/error';
|
|||
import { get as localStorageGet } from '@/react/hooks/useLocalStorage';
|
||||
|
||||
import {
|
||||
CACHE_DURATION,
|
||||
dispatchCacheRefreshEventIfNeeded,
|
||||
portainerAgentManagerOperation,
|
||||
portainerAgentTargetHeader,
|
||||
} from './http-request.helper';
|
||||
|
||||
export const cache = setupCache({
|
||||
maxAge: CACHE_DURATION,
|
||||
debug: false, // set to true to print cache hits/misses
|
||||
exclude: {
|
||||
query: false, // include urls with query params
|
||||
methods: ['put', 'patch', 'delete'],
|
||||
filter: (req: AxiosRequestConfig) => {
|
||||
// exclude caching get requests unless the path contains 'kubernetes'
|
||||
if (!req.url?.includes('kubernetes') && req.method === 'get') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// exclude caching post requests unless the path contains 'selfsubjectaccessreview'
|
||||
if (
|
||||
!req.url?.includes('selfsubjectaccessreview') &&
|
||||
req.method === 'post'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
// ask to clear cache on mutation
|
||||
invalidate: async (_, req) => {
|
||||
dispatchCacheRefreshEventIfNeeded(req);
|
||||
},
|
||||
});
|
||||
|
||||
// by default don't use the cache adapter
|
||||
const axios = axiosOrigin.create({ baseURL: 'api' });
|
||||
|
||||
// when entering a kubernetes environment, or updating user settings, update the cache adapter
|
||||
export function updateAxiosAdapter(useCache: boolean) {
|
||||
axios.defaults.adapter = useCache ? cache.adapter : undefined;
|
||||
}
|
||||
|
||||
loadProgressBar(undefined, axios);
|
||||
|
||||
export default axios;
|
||||
|
|
|
@ -1,3 +1,31 @@
|
|||
import { AxiosRequestConfig } from 'axios';
|
||||
|
||||
export const CACHE_DURATION = 5 * 60 * 1000; // 5m in ms
|
||||
// event emitted when cache need to be refreshed
|
||||
// used to sync $http + axios cache clear
|
||||
export const CACHE_REFRESH_EVENT = '__cache__refresh__event__';
|
||||
|
||||
// utility function to dispatch catch refresh event
|
||||
export function dispatchCacheRefreshEvent() {
|
||||
dispatchEvent(new CustomEvent(CACHE_REFRESH_EVENT, {}));
|
||||
}
|
||||
|
||||
// perform checks on config.method and config.url
|
||||
// to dispatch event in only specific scenarios
|
||||
export function dispatchCacheRefreshEventIfNeeded(req: AxiosRequestConfig) {
|
||||
if (
|
||||
req.method &&
|
||||
['post', 'patch', 'put', 'delete'].includes(req.method.toLowerCase()) &&
|
||||
// don't clear cache when we try to check for namespaces accesses
|
||||
// otherwise we will clear it on every page
|
||||
req.url &&
|
||||
!req.url.includes('selfsubjectaccessreviews') &&
|
||||
req.url.includes('kubernetes')
|
||||
) {
|
||||
dispatchCacheRefreshEvent();
|
||||
}
|
||||
}
|
||||
|
||||
interface Headers {
|
||||
agentTargetQueue: string[];
|
||||
agentManagerOperation: boolean;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { UserId } from '../types';
|
||||
|
||||
export const queryKeys = {
|
||||
export const userQueryKeys = {
|
||||
base: () => ['users'] as const,
|
||||
user: (id: UserId) => [...queryKeys.base(), id] as const,
|
||||
user: (id: UserId) => [...userQueryKeys.base(), id] as const,
|
||||
};
|
||||
|
|
|
@ -6,13 +6,13 @@ import { withError } from '@/react-tools/react-query';
|
|||
import { buildUrl } from '../user.service';
|
||||
import { User, UserId } from '../types';
|
||||
|
||||
import { queryKeys } from './queryKeys';
|
||||
import { userQueryKeys } from './queryKeys';
|
||||
|
||||
export function useUser(
|
||||
id: UserId,
|
||||
{ staleTime }: { staleTime?: number } = {}
|
||||
) {
|
||||
return useQuery(queryKeys.user(id), () => getUser(id), {
|
||||
return useQuery(userQueryKeys.user(id), () => getUser(id), {
|
||||
...withError('Unable to retrieve user details'),
|
||||
staleTime,
|
||||
});
|
||||
|
|
|
@ -20,6 +20,7 @@ export type User = {
|
|||
EndpointAuthorizations: {
|
||||
[endpointId: EnvironmentId]: AuthorizationMap;
|
||||
};
|
||||
UseCache: boolean;
|
||||
ThemeSettings: {
|
||||
color: 'dark' | 'light' | 'highcontrast' | 'auto';
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<page-header title="'User settings'" breadcrumbs="['User settings']"> </page-header>
|
||||
<page-header title="'User settings'" breadcrumbs="['User settings']" reload="true"> </page-header>
|
||||
|
||||
<demo-feature-indicator ng-if="isDemoUser" content="'You cannot change the password of this account in the demo version of Portainer.'"> </demo-feature-indicator>
|
||||
|
||||
|
@ -16,16 +16,16 @@
|
|||
<form name="form" class="form-horizontal" style="margin-top: 15px">
|
||||
<!-- current-password-input -->
|
||||
<div class="form-group">
|
||||
<label for="current_password" class="col-sm-2 control-label required text-left">Current password</label>
|
||||
<div class="col-sm-8">
|
||||
<label for="current_password" class="col-sm-3 col-lg-2 control-label required text-left">Current password</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input type="password" class="form-control" ng-model="formValues.currentPassword" id="current_password" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !current-password-input -->
|
||||
<!-- new-password-input -->
|
||||
<div class="form-group">
|
||||
<label for="new_password" class="col-sm-2 control-label required text-left">New password</label>
|
||||
<div class="col-sm-8">
|
||||
<label for="new_password" class="col-sm-3 col-lg-2 control-label required text-left">New password</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input type="password" class="form-control" ng-model="formValues.newPassword" ng-minlength="requiredPasswordLength" id="new_password" name="new_password" />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -33,8 +33,8 @@
|
|||
|
||||
<!-- confirm-password-input -->
|
||||
<div class="form-group">
|
||||
<label for="confirm_password" class="col-sm-2 control-label required text-left">Confirm password</label>
|
||||
<div class="col-sm-8">
|
||||
<label for="confirm_password" class="col-sm-3 col-lg-2 control-label required text-left">Confirm password</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<div class="input-group">
|
||||
<input type="password" class="form-control" ng-model="formValues.confirmPassword" id="confirm_password" />
|
||||
<span class="input-group-addon">
|
||||
|
@ -86,6 +86,8 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<application-settings-widget></application-settings-widget>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<access-tokens-datatable
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<page-header title="'Create access token'" breadcrumbs="[{label:'User settings', link:'portainer.account'}, 'Add access token']"> </page-header>
|
||||
<page-header title="'Create access token'" breadcrumbs="[{label:'User settings', link:'portainer.account'}, 'Add access token']" reload="true"> </page-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<page-header title="'Create Custom template'" breadcrumbs="[{label:'Custom Templates', link:'docker.templates.custom'}, 'Create Custom template']"> </page-header>
|
||||
<page-header title="'Create Custom template'" breadcrumbs="[{label:'Custom Templates', link:'docker.templates.custom'}, 'Create Custom template']" reload="true"> </page-header>
|
||||
|
||||
<div class="row" ng-if="!$ctrl.state.loading">
|
||||
<div class="col-sm-12">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<page-header title="'FDO Device Configuration'" breadcrumbs="[{label:'Environments', link:'portainer.endpoints'}, 'Import FDO Device']"> </page-header>
|
||||
<page-header title="'FDO Device Configuration'" breadcrumbs="[{label:'Environments', link:'portainer.endpoints'}, 'Import FDO Device']" reload="true"> </page-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<page-header title="'Create profile'" breadcrumbs="[{label:'Settings', link:'portainer.settings'}, 'Edge Compute']"> </page-header>
|
||||
<page-header title="'Create profile'" breadcrumbs="[{label:'Settings', link:'portainer.settings'}, 'Edge Compute']" reload="true"> </page-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<page-header title="'Edit profile'" breadcrumbs="[{label:'Settings', link:'portainer.settings'}, 'Edge Compute']"> </page-header>
|
||||
<page-header title="'Edit profile'" breadcrumbs="[{label:'Settings', link:'portainer.settings'}, 'Edge Compute']" reload="true"> </page-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
link: 'portainer.endpoints.endpoint',
|
||||
linkParams:{id: ctrl.endpoint.Id}
|
||||
}, 'Access management']"
|
||||
reload="true"
|
||||
>
|
||||
</page-header>
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
},
|
||||
$state.deviceName,
|
||||
'KVM Control']"
|
||||
reload="true"
|
||||
>
|
||||
</page-header>
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
link: 'portainer.groups.group',
|
||||
linkParams:{id: group.Id}
|
||||
}, 'Access management']"
|
||||
reload="true"
|
||||
>
|
||||
</page-header>
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<page-header title="'Create environment group'" breadcrumbs="[{label:'Environment groups', link:'portainer.groups'}, 'Add group']"> </page-header>
|
||||
<page-header title="'Create environment group'" breadcrumbs="[{label:'Environment groups', link:'portainer.groups'}, 'Add group']" reload="true"> </page-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<page-header title="'Environment group details'" breadcrumbs="[{label:'Groups', link:'portainer.groups'}, group.Name]"> </page-header>
|
||||
<page-header title="'Environment group details'" breadcrumbs="[{label:'Groups', link:'portainer.groups'}, group.Name]" reload="true"> </page-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<page-header title="'Create registry'" breadcrumbs="[{label:'Registries', link:'portainer.registries'}, 'Add registry']"> </page-header>
|
||||
<page-header title="'Create registry'" breadcrumbs="[{label:'Registries', link:'portainer.registries'}, 'Add registry']" reload="true"> </page-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<page-header title="'Registry details'" breadcrumbs="[{label:'Registries', link:'portainer.registries'}, $ctrl.registry.Name]"> </page-header>
|
||||
<page-header title="'Registry details'" breadcrumbs="[{label:'Registries', link:'portainer.registries'}, $ctrl.registry.Name]" reload="true"> </page-header>
|
||||
|
||||
<div class="row" ng-if="!$ctrl.state.loading">
|
||||
<div class="col-sm-12">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<page-header title="'Authentication settings'" breadcrumbs="[{label:'Settings', link:'portainer.settings'}, 'Authentication']"> </page-header>
|
||||
<page-header title="'Authentication settings'" breadcrumbs="[{label:'Settings', link:'portainer.settings'}, 'Authentication']" reload="true"> </page-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<page-header title="'Settings'" breadcrumbs="[{label:'Settings', link:'portainer.settings'}, 'Edge Compute']"> </page-header>
|
||||
<page-header title="'Settings'" breadcrumbs="[{label:'Settings', link:'portainer.settings'}, 'Edge Compute']" reload="true"> </page-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12" ng-if="$ctrl.settings">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<page-header title="'Create stack'" breadcrumbs="[{label:'Stacks', link:'docker.stacks'}, 'Add stack']"> </page-header>
|
||||
<page-header title="'Create stack'" breadcrumbs="[{label:'Stacks', link:'docker.stacks'}, 'Add stack']" reload="true"> </page-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<page-header title="'User details'" breadcrumbs="[{label:'Users', link:'portainer.users'}, formValues.username]"> </page-header>
|
||||
<page-header title="'User details'" breadcrumbs="[{label:'Users', link:'portainer.users'}, formValues.username]" reload="true"> </page-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue