mirror of
https://github.com/portainer/portainer.git
synced 2025-08-08 15:25:22 +02:00
refactor(ui): move react components to react codebase [EE-3354] (#8258)
* refactor(ui): move react components to react codebase [EE-3354] * refactor(app): move bocx selector options * refactor(react): spearate portainer components * fix(app): fix imports
This commit is contained in:
parent
f9a09301a8
commit
b98c71f1ab
66 changed files with 312 additions and 294 deletions
|
@ -1,4 +1,4 @@
|
|||
import { tlsOptions } from './tls-options';
|
||||
import { tlsOptions } from '@/react/portainer/environments/ItemView/tls-options';
|
||||
|
||||
angular.module('portainer.app').controller('porEndpointSecurityController', [
|
||||
'$scope',
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
import { Shield } from 'lucide-react';
|
||||
|
||||
import { BoxSelectorOption } from '@@/BoxSelector';
|
||||
|
||||
export const tlsOptions: ReadonlyArray<BoxSelectorOption<string>> = [
|
||||
{
|
||||
id: 'tls_client_ca',
|
||||
value: 'tls_client_ca',
|
||||
icon: Shield,
|
||||
iconType: 'badge',
|
||||
label: 'TLS with server and client verification',
|
||||
description: 'Use client certificates and server verification',
|
||||
},
|
||||
{
|
||||
id: 'tls_client_noca',
|
||||
value: 'tls_client_noca',
|
||||
icon: Shield,
|
||||
iconType: 'badge',
|
||||
label: 'TLS with client verification only',
|
||||
description: 'Use client certificates without server verification',
|
||||
},
|
||||
{
|
||||
id: 'tls_ca',
|
||||
value: 'tls_ca',
|
||||
icon: Shield,
|
||||
iconType: 'badge',
|
||||
label: 'TLS with server verification only',
|
||||
description: 'Only verify the server certificate',
|
||||
},
|
||||
{
|
||||
id: 'tls_only',
|
||||
value: 'tls_only',
|
||||
icon: Shield,
|
||||
iconType: 'badge',
|
||||
label: 'TLS only',
|
||||
description: 'No server/client verification',
|
||||
},
|
||||
] as const;
|
|
@ -5,8 +5,8 @@ import { notifyError } from '@/portainer/services/notifications';
|
|||
import { IAuthenticationService } from '@/portainer/services/types';
|
||||
import { GitAuthModel } from '@/react/portainer/gitops/types';
|
||||
import { gitAuthValidation } from '@/react/portainer/gitops/AuthFieldset';
|
||||
import { getGitCredentials } from '@/portainer/views/account/git-credential/gitCredential.service';
|
||||
import { GitCredential } from '@/portainer/views/account/git-credential/types';
|
||||
import { GitCredential } from '@/react/portainer/account/git-credentials/types';
|
||||
import { getGitCredentials } from '@/react/portainer/account/git-credentials/git-credentials.service';
|
||||
|
||||
import { validateForm } from '@@/form-components/validate-form';
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@ import { GitFormModel } from '@/react/portainer/gitops/types';
|
|||
import { validateGitForm } from '@/react/portainer/gitops/GitForm';
|
||||
import { notifyError } from '@/portainer/services/notifications';
|
||||
import { IAuthenticationService } from '@/portainer/services/types';
|
||||
import { getGitCredentials } from '@/portainer/views/account/git-credential/gitCredential.service';
|
||||
import { GitCredential } from '@/portainer/views/account/git-credential/types';
|
||||
import { getGitCredentials } from '@/react/portainer/account/git-credentials/git-credentials.service';
|
||||
import { GitCredential } from '@/react/portainer/account/git-credentials/types';
|
||||
import { isBE } from '@/react/portainer/feature-flags/feature-flags.service';
|
||||
|
||||
export default class GitFormController {
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
import Lightmode from '@/assets/ico/theme/lightmode.svg?c';
|
||||
import Darkmode from '@/assets/ico/theme/darkmode.svg?c';
|
||||
import Highcontrastmode from '@/assets/ico/theme/highcontrastmode.svg?c';
|
||||
import Automode from '@/assets/ico/theme/auto.svg?c';
|
||||
|
||||
export const options = [
|
||||
{
|
||||
id: 'light',
|
||||
icon: Lightmode,
|
||||
label: 'Light Theme',
|
||||
description: 'Default color mode',
|
||||
value: 'light',
|
||||
},
|
||||
{
|
||||
id: 'dark',
|
||||
icon: Darkmode,
|
||||
label: 'Dark Theme',
|
||||
description: 'Dark color mode',
|
||||
value: 'dark',
|
||||
},
|
||||
{
|
||||
id: 'highcontrast',
|
||||
icon: Highcontrastmode,
|
||||
label: 'High Contrast',
|
||||
description: 'High contrast color mode',
|
||||
value: 'highcontrast',
|
||||
},
|
||||
{
|
||||
id: 'auto',
|
||||
icon: Automode,
|
||||
label: 'Auto',
|
||||
description: 'Sync with system theme',
|
||||
value: 'auto',
|
||||
},
|
||||
];
|
|
@ -1,7 +1,7 @@
|
|||
import { notifyError, notifySuccess } from '@/portainer/services/notifications';
|
||||
import { queryKeys } from '@/portainer/users/queries/queryKeys';
|
||||
import { queryClient } from '@/react-tools/react-query';
|
||||
import { options } from './options';
|
||||
import { options } from '@/react/portainer/account/AccountView/theme-options';
|
||||
|
||||
export default class ThemeSettingsController {
|
||||
/* @ngInject */
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
import { server, rest } from '@/setup-tests/server';
|
||||
|
||||
import { getLicenses } from './license.service';
|
||||
import type { License } from './types';
|
||||
|
||||
describe('getLicenses', () => {
|
||||
it('on success should return the server body', async () => {
|
||||
const catchFn = jest.fn();
|
||||
const thenFn = jest.fn();
|
||||
|
||||
const data: License[] = [];
|
||||
server.use(
|
||||
rest.get('/api/licenses', (req, res, ctx) => res(ctx.json(data)))
|
||||
);
|
||||
|
||||
const promise = getLicenses();
|
||||
|
||||
await promise.then(thenFn).catch(catchFn);
|
||||
|
||||
expect(catchFn).not.toHaveBeenCalled();
|
||||
expect(thenFn).toHaveBeenCalledWith(data);
|
||||
});
|
||||
|
||||
it('on failure should return the server message', async () => {
|
||||
const catchFn = jest.fn();
|
||||
const thenFn = jest.fn();
|
||||
|
||||
const message = 'message';
|
||||
const details = 'details';
|
||||
|
||||
server.use(
|
||||
rest.get('/api/licenses', (req, res, ctx) =>
|
||||
res(ctx.status(400), ctx.json({ message, details }))
|
||||
)
|
||||
);
|
||||
|
||||
const promise = getLicenses();
|
||||
await promise.then(thenFn, catchFn);
|
||||
|
||||
expect(catchFn).toHaveBeenCalledWith(new Error(message));
|
||||
expect(thenFn).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
|
@ -1,112 +1,11 @@
|
|||
import _ from 'lodash';
|
||||
import { AxiosError } from 'axios';
|
||||
|
||||
import axios from '@/portainer/services/axios';
|
||||
|
||||
import { License, LicenseInfo } from './types';
|
||||
|
||||
type Listener = (info: LicenseInfo) => void;
|
||||
|
||||
interface Store {
|
||||
data?: LicenseInfo;
|
||||
lastLoaded?: number;
|
||||
invalidated: boolean;
|
||||
listeners: Listener[];
|
||||
}
|
||||
|
||||
const store: Store = {
|
||||
listeners: [],
|
||||
invalidated: true,
|
||||
};
|
||||
|
||||
export async function getLicenses() {
|
||||
try {
|
||||
const { data } = await axios.get<License[]>(buildUrl());
|
||||
|
||||
return data;
|
||||
} catch (e) {
|
||||
const axiosError = e as AxiosError;
|
||||
throw new Error(axiosError.response?.data.message);
|
||||
}
|
||||
}
|
||||
|
||||
interface AttachResponse {
|
||||
licenses: License[];
|
||||
failedKeys: Record<string, string>;
|
||||
}
|
||||
|
||||
export async function attachLicense(licenseKeys: string[]) {
|
||||
try {
|
||||
const { data } = await axios.post<AttachResponse>(buildUrl(), {
|
||||
licenseKeys,
|
||||
});
|
||||
|
||||
if (Object.keys(data.failedKeys).length === licenseKeys.length) {
|
||||
return data;
|
||||
}
|
||||
|
||||
store.invalidated = true;
|
||||
getLicenseInfo();
|
||||
return data;
|
||||
} catch (e) {
|
||||
const axiosError = e as AxiosError;
|
||||
throw new Error(axiosError.response?.data.message);
|
||||
}
|
||||
}
|
||||
|
||||
interface RemoveResponse {
|
||||
failedKeys: Record<string, string>;
|
||||
}
|
||||
|
||||
export async function removeLicense(licenseKeys: string[]) {
|
||||
try {
|
||||
const { data } = await axios.post<RemoveResponse>(buildUrl('remove'), {
|
||||
licenseKeys,
|
||||
});
|
||||
if (Object.keys(data.failedKeys).length === licenseKeys.length) {
|
||||
return data;
|
||||
}
|
||||
|
||||
store.invalidated = true;
|
||||
getLicenseInfo();
|
||||
return data;
|
||||
} catch (e) {
|
||||
const axiosError = e as AxiosError;
|
||||
throw new Error(axiosError.response?.data.message);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getLicenseInfo() {
|
||||
try {
|
||||
if (
|
||||
store.data &&
|
||||
!store.invalidated &&
|
||||
store.lastLoaded &&
|
||||
Math.abs(store.lastLoaded - Date.now()) < 1000 * 30
|
||||
) {
|
||||
return store.data;
|
||||
}
|
||||
|
||||
const { data: info } = await axios.get<LicenseInfo>(buildUrl('info'));
|
||||
store.data = info;
|
||||
store.lastLoaded = Date.now();
|
||||
store.invalidated = false;
|
||||
store.listeners.forEach((listener) => listener(info));
|
||||
|
||||
return info;
|
||||
} catch (e) {
|
||||
const axiosError = e as AxiosError;
|
||||
throw new Error(axiosError.response?.data.message);
|
||||
}
|
||||
}
|
||||
|
||||
export function subscribe(listener: Listener) {
|
||||
store.listeners.push(listener);
|
||||
}
|
||||
|
||||
export function unsubscribe(listener: Listener) {
|
||||
_.remove<Listener>(store.listeners, listener);
|
||||
}
|
||||
import {
|
||||
getLicenses,
|
||||
attachLicense,
|
||||
removeLicense,
|
||||
getLicenseInfo,
|
||||
unsubscribe,
|
||||
subscribe,
|
||||
} from '@/react/portainer/licenses/license.service';
|
||||
|
||||
/* @ngInject */
|
||||
export function LicenseService() {
|
||||
|
@ -119,12 +18,3 @@ export function LicenseService() {
|
|||
unsubscribe,
|
||||
};
|
||||
}
|
||||
|
||||
function buildUrl(action = '') {
|
||||
let url = 'licenses';
|
||||
|
||||
if (action) {
|
||||
url += `/${action}`;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
// matches https://github.com/portainer/liblicense/blob/master/liblicense.go#L66-L74
|
||||
export enum Edition {
|
||||
CE = 1,
|
||||
BE,
|
||||
EE,
|
||||
}
|
||||
|
||||
// matches https://github.com/portainer/liblicense/blob/master/liblicense.go#L60-L64
|
||||
|
||||
export enum LicenseType {
|
||||
Trial = 1,
|
||||
Subscription,
|
||||
}
|
||||
|
||||
// matches https://github.com/portainer/liblicense/blob/master/liblicense.go#L35-L50
|
||||
export interface License {
|
||||
id: string;
|
||||
company: string;
|
||||
created: number;
|
||||
email: string;
|
||||
expiresAfter: number;
|
||||
licenseKey: string;
|
||||
nodes: number;
|
||||
productEdition: Edition;
|
||||
revoked: boolean;
|
||||
revokedAt: number;
|
||||
type: LicenseType;
|
||||
version: number;
|
||||
reference: string;
|
||||
expiresAt: number;
|
||||
}
|
||||
|
||||
// matches https://github.com/portainer/portainer-ee/blob/c4575bf528583fe1682267db4ee40a11a905f611/api/portainer.go#L588-L597
|
||||
export interface LicenseInfo {
|
||||
productEdition: Edition;
|
||||
company: string;
|
||||
email: string;
|
||||
createdAt: number;
|
||||
expiresAt: number;
|
||||
nodes: number;
|
||||
type: LicenseType;
|
||||
valid: boolean;
|
||||
enforcedAt: number;
|
||||
enforced: boolean;
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
import { useQuery } from 'react-query';
|
||||
|
||||
import { error as notifyError } from '@/portainer/services/notifications';
|
||||
import { useNodesCount } from '@/react/portainer/system/useNodesCount';
|
||||
|
||||
import { getLicenseInfo } from './license.service';
|
||||
import { LicenseInfo, LicenseType } from './types';
|
||||
|
||||
export function useLicenseInfo() {
|
||||
const { isLoading, data: info } = useQuery<LicenseInfo, Error>(
|
||||
'licenseInfo',
|
||||
() => getLicenseInfo(),
|
||||
{
|
||||
onError(error) {
|
||||
notifyError('Failure', error as Error, 'Failed to get license info');
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return { isLoading, info };
|
||||
}
|
||||
|
||||
export function useIntegratedLicenseInfo() {
|
||||
const { isLoading: isLoadingNodes, data: nodesCount = 0 } = useNodesCount();
|
||||
|
||||
const { isLoading: isLoadingLicense, info } = useLicenseInfo();
|
||||
if (
|
||||
isLoadingLicense ||
|
||||
isLoadingNodes ||
|
||||
!info ||
|
||||
info.type === LicenseType.Trial
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { licenseInfo: info as LicenseInfo, usedNodes: nodesCount };
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
import { Edit } from 'lucide-react';
|
||||
|
||||
import { FeatureId } from '@/react/portainer/feature-flags/enums';
|
||||
import Microsoft from '@/assets/ico/vendor/microsoft.svg?c';
|
||||
import Google from '@/assets/ico/vendor/google.svg?c';
|
||||
import Github from '@/assets/ico/vendor/github.svg?c';
|
||||
|
||||
import { BadgeIcon } from '@@/BadgeIcon';
|
||||
|
||||
export const options = [
|
||||
{
|
||||
id: 'microsoft',
|
||||
icon: Microsoft,
|
||||
label: 'Microsoft',
|
||||
description: 'Microsoft OAuth provider',
|
||||
value: 'microsoft',
|
||||
feature: FeatureId.HIDE_INTERNAL_AUTH,
|
||||
},
|
||||
{
|
||||
id: 'google',
|
||||
icon: Google,
|
||||
label: 'Google',
|
||||
description: 'Google OAuth provider',
|
||||
value: 'google',
|
||||
feature: FeatureId.HIDE_INTERNAL_AUTH,
|
||||
},
|
||||
{
|
||||
id: 'github',
|
||||
icon: Github,
|
||||
label: 'Github',
|
||||
description: 'Github OAuth provider',
|
||||
value: 'github',
|
||||
feature: FeatureId.HIDE_INTERNAL_AUTH,
|
||||
},
|
||||
{
|
||||
id: 'custom',
|
||||
icon: <BadgeIcon icon={Edit} />,
|
||||
label: 'Custom',
|
||||
description: 'Custom OAuth provider',
|
||||
value: 'custom',
|
||||
},
|
||||
];
|
|
@ -1,4 +1,4 @@
|
|||
import { options } from './oauth-options';
|
||||
import { options } from '@/react/portainer/settings/AuthenticationView/oauth-options';
|
||||
|
||||
export default class OAuthProviderSelectorController {
|
||||
constructor() {
|
||||
|
|
58
app/portainer/react/components/access-control.ts
Normal file
58
app/portainer/react/components/access-control.ts
Normal file
|
@ -0,0 +1,58 @@
|
|||
import angular from 'angular';
|
||||
|
||||
import { r2a } from '@/react-tools/react2angular';
|
||||
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||
import { PorAccessControlFormTeamSelector } from '@/react/portainer/access-control/PorAccessControlForm/TeamsSelector';
|
||||
import { PorAccessControlFormUserSelector } from '@/react/portainer/access-control/PorAccessControlForm/UsersSelector';
|
||||
import { PorAccessManagementUsersSelector } from '@/react/portainer/access-control/AccessManagement/PorAccessManagementUsersSelector';
|
||||
import { AccessTypeSelector } from '@/react/portainer/access-control/EditDetails/AccessTypeSelector';
|
||||
import { AccessControlPanel } from '@/react/portainer/access-control';
|
||||
|
||||
export const accessControlModule = angular
|
||||
.module('portainer.app.react.components.access-control', [])
|
||||
.component(
|
||||
'accessControlPanel',
|
||||
r2a(withUIRouter(withReactQuery(withCurrentUser(AccessControlPanel))), [
|
||||
'disableOwnershipChange',
|
||||
'onUpdateSuccess',
|
||||
'resourceControl',
|
||||
'resourceId',
|
||||
'resourceType',
|
||||
'environmentId',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'accessTypeSelector',
|
||||
r2a(AccessTypeSelector, [
|
||||
'isAdmin',
|
||||
'isPublicVisible',
|
||||
'name',
|
||||
'onChange',
|
||||
'value',
|
||||
'teams',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'porAccessControlFormTeamSelector',
|
||||
r2a(PorAccessControlFormTeamSelector, [
|
||||
'inputId',
|
||||
'onChange',
|
||||
'options',
|
||||
'value',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'porAccessControlFormUserSelector',
|
||||
r2a(PorAccessControlFormUserSelector, [
|
||||
'inputId',
|
||||
'onChange',
|
||||
'options',
|
||||
'value',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'porAccessManagementUsersSelector',
|
||||
r2a(PorAccessManagementUsersSelector, ['onChange', 'options', 'value'])
|
||||
).name;
|
16
app/portainer/react/components/envronments.ts
Normal file
16
app/portainer/react/components/envronments.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import angular from 'angular';
|
||||
|
||||
import { r2a } from '@/react-tools/react2angular';
|
||||
import { withControlledInput } from '@/react-tools/withControlledInput';
|
||||
import { EdgeKeyDisplay } from '@/react/portainer/environments/ItemView/EdgeKeyDisplay';
|
||||
import { KVMControl } from '@/react/portainer/environments/KvmView/KVMControl';
|
||||
import { GpusList } from '@/react/portainer/environments/wizard/EnvironmentsCreationView/shared/Hardware/GpusList';
|
||||
|
||||
export const environmentsModule = angular
|
||||
.module('portainer.app.react.components.environments', [])
|
||||
.component('edgeKeyDisplay', r2a(EdgeKeyDisplay, ['edgeKey']))
|
||||
.component('kvmControl', r2a(KVMControl, ['deviceId', 'server', 'token']))
|
||||
.component(
|
||||
'gpusList',
|
||||
r2a(withControlledInput(GpusList), ['value', 'onChange'])
|
||||
).name;
|
|
@ -9,6 +9,7 @@ import { GitForm } from '@/react/portainer/gitops/GitForm';
|
|||
import { AuthFieldset } from '@/react/portainer/gitops/AuthFieldset';
|
||||
import { InfoPanel } from '@/react/portainer/gitops/InfoPanel';
|
||||
import { RefField } from '@/react/portainer/gitops/RefField';
|
||||
import { TimeWindowDisplay } from '@/react/portainer/gitops/TimeWindowDisplay';
|
||||
|
||||
export const gitFormModule = angular
|
||||
.module('portainer.app.components.forms.git', [])
|
||||
|
@ -66,4 +67,8 @@ export const gitFormModule = angular
|
|||
'value',
|
||||
'isUrlValid',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'timeWindowDisplay',
|
||||
r2a(withReactQuery(withUIRouter(TimeWindowDisplay)), [])
|
||||
).name;
|
||||
|
|
|
@ -1,26 +1,12 @@
|
|||
import angular from 'angular';
|
||||
|
||||
import { r2a } from '@/react-tools/react2angular';
|
||||
import {
|
||||
DefaultRegistryAction,
|
||||
DefaultRegistryDomain,
|
||||
DefaultRegistryName,
|
||||
} from '@/react/portainer/registries/ListView/DefaultRegistry';
|
||||
import { Icon } from '@/react/components/Icon';
|
||||
import { ReactQueryDevtoolsWrapper } from '@/react/components/ReactQueryDevtoolsWrapper';
|
||||
import { AccessControlPanel } from '@/react/portainer/access-control';
|
||||
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||
import { SettingsFDO } from '@/react/portainer/settings/EdgeComputeView/SettingsFDO';
|
||||
import { SettingsOpenAMT } from '@/react/portainer/settings/EdgeComputeView/SettingsOpenAMT';
|
||||
import { InternalAuth } from '@/react/portainer/settings/AuthenticationView/InternalAuth';
|
||||
import { PorAccessControlFormTeamSelector } from '@/react/portainer/access-control/PorAccessControlForm/TeamsSelector';
|
||||
import { PorAccessControlFormUserSelector } from '@/react/portainer/access-control/PorAccessControlForm/UsersSelector';
|
||||
import { PorAccessManagementUsersSelector } from '@/react/portainer/access-control/AccessManagement/PorAccessManagementUsersSelector';
|
||||
import { AccessTypeSelector } from '@/react/portainer/access-control/EditDetails/AccessTypeSelector';
|
||||
import { EdgeKeyDisplay } from '@/react/portainer/environments/ItemView/EdgeKeyDisplay';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
import { ReactQueryDevtoolsWrapper } from '@@/ReactQueryDevtoolsWrapper';
|
||||
import { PageHeader } from '@@/PageHeader';
|
||||
import { TagSelector } from '@@/TagSelector';
|
||||
import { Loading } from '@@/Widget/Loading';
|
||||
|
@ -38,18 +24,21 @@ import { PortainerSelect } from '@@/form-components/PortainerSelect';
|
|||
import { Slider } from '@@/form-components/Slider';
|
||||
import { TagButton } from '@@/TagButton';
|
||||
import { BETeaserButton } from '@@/BETeaserButton';
|
||||
import { TimeWindowDisplay } from '@@/TimeWindowDisplay';
|
||||
import { CodeEditor } from '@@/CodeEditor';
|
||||
|
||||
import { fileUploadField } from './file-upload-field';
|
||||
import { switchField } from './switch-field';
|
||||
import { customTemplatesModule } from './custom-templates';
|
||||
import { gitFormModule } from './git-form';
|
||||
import { settingsModule } from './settings';
|
||||
import { accessControlModule } from './access-control';
|
||||
|
||||
export const componentsModule = angular
|
||||
.module('portainer.app.react.components', [
|
||||
customTemplatesModule,
|
||||
gitFormModule,
|
||||
settingsModule,
|
||||
accessControlModule,
|
||||
])
|
||||
.component(
|
||||
'tagSelector',
|
||||
|
@ -74,17 +63,7 @@ export const componentsModule = angular
|
|||
'tagButton',
|
||||
r2a(TagButton, ['value', 'label', 'title', 'onRemove'])
|
||||
)
|
||||
.component(
|
||||
'accessTypeSelector',
|
||||
r2a(AccessTypeSelector, [
|
||||
'isAdmin',
|
||||
'isPublicVisible',
|
||||
'name',
|
||||
'onChange',
|
||||
'value',
|
||||
'teams',
|
||||
])
|
||||
)
|
||||
|
||||
.component(
|
||||
'portainerTooltip',
|
||||
r2a(Tooltip, ['message', 'position', 'className', 'setHtmlMessage'])
|
||||
|
@ -143,38 +122,6 @@ export const componentsModule = angular
|
|||
])
|
||||
)
|
||||
.component('badgeIcon', r2a(BadgeIcon, ['icon', 'size']))
|
||||
.component(
|
||||
'accessControlPanel',
|
||||
r2a(withUIRouter(withReactQuery(withCurrentUser(AccessControlPanel))), [
|
||||
'disableOwnershipChange',
|
||||
'onUpdateSuccess',
|
||||
'resourceControl',
|
||||
'resourceId',
|
||||
'resourceType',
|
||||
'environmentId',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'defaultRegistryName',
|
||||
r2a(withReactQuery(DefaultRegistryName), [])
|
||||
)
|
||||
.component(
|
||||
'defaultRegistryAction',
|
||||
r2a(withReactQuery(DefaultRegistryAction), [])
|
||||
)
|
||||
.component(
|
||||
'defaultRegistryDomain',
|
||||
r2a(withReactQuery(DefaultRegistryDomain), [])
|
||||
)
|
||||
.component(
|
||||
'settingsFdo',
|
||||
r2a(withUIRouter(withReactQuery(SettingsFDO)), ['onSubmit', 'settings'])
|
||||
)
|
||||
.component('settingsOpenAmt', r2a(SettingsOpenAMT, ['onSubmit', 'settings']))
|
||||
.component(
|
||||
'internalAuth',
|
||||
r2a(InternalAuth, ['onSaveSettings', 'isLoading', 'value', 'onChange'])
|
||||
)
|
||||
.component(
|
||||
'teamsSelector',
|
||||
r2a(TeamsSelector, [
|
||||
|
@ -188,24 +135,6 @@ export const componentsModule = angular
|
|||
'disabled',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'porAccessControlFormTeamSelector',
|
||||
r2a(PorAccessControlFormTeamSelector, [
|
||||
'inputId',
|
||||
'onChange',
|
||||
'options',
|
||||
'value',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'porAccessControlFormUserSelector',
|
||||
r2a(PorAccessControlFormUserSelector, [
|
||||
'inputId',
|
||||
'onChange',
|
||||
'options',
|
||||
'value',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'porSelect',
|
||||
r2a(PortainerSelect, [
|
||||
|
@ -234,15 +163,7 @@ export const componentsModule = angular
|
|||
'dataCy',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'porAccessManagementUsersSelector',
|
||||
r2a(PorAccessManagementUsersSelector, ['onChange', 'options', 'value'])
|
||||
)
|
||||
.component('edgeKeyDisplay', r2a(EdgeKeyDisplay, ['edgeKey']))
|
||||
.component(
|
||||
'timeWindowDisplay',
|
||||
r2a(withReactQuery(withUIRouter(TimeWindowDisplay)), [])
|
||||
)
|
||||
|
||||
.component(
|
||||
'reactCodeEditor',
|
||||
r2a(CodeEditor, [
|
||||
|
|
24
app/portainer/react/components/registries.ts
Normal file
24
app/portainer/react/components/registries.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import angular from 'angular';
|
||||
|
||||
import { r2a } from '@/react-tools/react2angular';
|
||||
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||
import {
|
||||
DefaultRegistryAction,
|
||||
DefaultRegistryDomain,
|
||||
DefaultRegistryName,
|
||||
} from '@/react/portainer/registries/ListView/DefaultRegistry';
|
||||
|
||||
export const registriesModule = angular
|
||||
.module('portainer.app.react.components.registries', [])
|
||||
.component(
|
||||
'defaultRegistryName',
|
||||
r2a(withReactQuery(DefaultRegistryName), [])
|
||||
)
|
||||
.component(
|
||||
'defaultRegistryAction',
|
||||
r2a(withReactQuery(DefaultRegistryAction), [])
|
||||
)
|
||||
.component(
|
||||
'defaultRegistryDomain',
|
||||
r2a(withReactQuery(DefaultRegistryDomain), [])
|
||||
).name;
|
20
app/portainer/react/components/settings.ts
Normal file
20
app/portainer/react/components/settings.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import angular from 'angular';
|
||||
|
||||
import { SettingsFDO } from '@/react/portainer/settings/EdgeComputeView/SettingsFDO';
|
||||
import { SettingsOpenAMT } from '@/react/portainer/settings/EdgeComputeView/SettingsOpenAMT';
|
||||
import { InternalAuth } from '@/react/portainer/settings/AuthenticationView/InternalAuth';
|
||||
import { r2a } from '@/react-tools/react2angular';
|
||||
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||
|
||||
export const settingsModule = angular
|
||||
.module('portainer.app.react.components.settings', [])
|
||||
.component(
|
||||
'settingsFdo',
|
||||
r2a(withUIRouter(withReactQuery(SettingsFDO)), ['onSubmit', 'settings'])
|
||||
)
|
||||
.component('settingsOpenAmt', r2a(SettingsOpenAMT, ['onSubmit', 'settings']))
|
||||
.component(
|
||||
'internalAuth',
|
||||
r2a(InternalAuth, ['onSaveSettings', 'isLoading', 'value', 'onChange'])
|
||||
).name;
|
|
@ -1,28 +0,0 @@
|
|||
import { Edit } from 'lucide-react';
|
||||
|
||||
import { FeatureId } from '@/react/portainer/feature-flags/enums';
|
||||
import Openldap from '@/assets/ico/vendor/openldap.svg?c';
|
||||
|
||||
import { BadgeIcon } from '@@/BadgeIcon';
|
||||
|
||||
const SERVER_TYPES = {
|
||||
CUSTOM: 0,
|
||||
OPEN_LDAP: 1,
|
||||
AD: 2,
|
||||
};
|
||||
|
||||
export const options = [
|
||||
{
|
||||
id: 'ldap_custom',
|
||||
icon: <BadgeIcon icon={Edit} />,
|
||||
label: 'Custom',
|
||||
value: SERVER_TYPES.CUSTOM,
|
||||
},
|
||||
{
|
||||
id: 'ldap_openldap',
|
||||
icon: Openldap,
|
||||
label: 'OpenLDAP',
|
||||
value: SERVER_TYPES.OPEN_LDAP,
|
||||
feature: FeatureId.EXTERNAL_AUTH_LDAP,
|
||||
},
|
||||
];
|
|
@ -1,5 +1,5 @@
|
|||
import { buildLdapSettingsModel, buildOpenLDAPSettingsModel } from '@/portainer/settings/authentication/ldap/ldap-settings.model';
|
||||
import { options } from './ldap-options';
|
||||
import { options } from '@/react/portainer/settings/AuthenticationView/ldap-options';
|
||||
|
||||
const SERVER_TYPES = {
|
||||
CUSTOM: 0,
|
||||
|
|
|
@ -1,155 +0,0 @@
|
|||
import { useMutation, useQuery, useQueryClient } from 'react-query';
|
||||
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { success as notifySuccess } from '@/portainer/services/notifications';
|
||||
|
||||
import {
|
||||
CreateGitCredentialPayload,
|
||||
GitCredential,
|
||||
UpdateGitCredentialPayload,
|
||||
} from './types';
|
||||
|
||||
export async function createGitCredential(
|
||||
gitCredential: CreateGitCredentialPayload
|
||||
) {
|
||||
try {
|
||||
await axios.post(buildGitUrl(gitCredential.userId), gitCredential);
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error, 'Unable to create git credential');
|
||||
}
|
||||
}
|
||||
|
||||
export async function getGitCredentials(userId: number) {
|
||||
try {
|
||||
const { data } = await axios.get<GitCredential[]>(buildGitUrl(userId));
|
||||
return data;
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error, 'Unable to get git credentials');
|
||||
}
|
||||
}
|
||||
|
||||
export async function getGitCredential(userId: number, id: number) {
|
||||
try {
|
||||
const { data } = await axios.get<GitCredential>(buildGitUrl(userId, id));
|
||||
return data;
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error, 'Unable to get git credential');
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteGitCredential(credential: GitCredential) {
|
||||
try {
|
||||
await axios.delete<GitCredential[]>(
|
||||
buildGitUrl(credential.userId, credential.id)
|
||||
);
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error, 'Unable to delete git credential');
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateGitCredential(
|
||||
credential: Partial<UpdateGitCredentialPayload>,
|
||||
userId: number,
|
||||
id: number
|
||||
) {
|
||||
try {
|
||||
const { data } = await axios.put(buildGitUrl(userId, id), credential);
|
||||
return data;
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error, 'Unable to update credential');
|
||||
}
|
||||
}
|
||||
|
||||
export function useUpdateGitCredentialMutation() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation(
|
||||
({
|
||||
credential,
|
||||
userId,
|
||||
id,
|
||||
}: {
|
||||
credential: UpdateGitCredentialPayload;
|
||||
userId: number;
|
||||
id: number;
|
||||
}) => updateGitCredential(credential, userId, id),
|
||||
{
|
||||
onSuccess: (_, data) => {
|
||||
notifySuccess(
|
||||
'Git credential updated successfully',
|
||||
data.credential.name
|
||||
);
|
||||
return queryClient.invalidateQueries(['gitcredentials']);
|
||||
},
|
||||
meta: {
|
||||
error: {
|
||||
title: 'Failure',
|
||||
message: 'Unable to update credential',
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function useDeleteGitCredentialMutation() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation(deleteGitCredential, {
|
||||
onSuccess: (_, credential) => {
|
||||
notifySuccess('Git Credential deleted successfully', credential.name);
|
||||
return queryClient.invalidateQueries(['gitcredentials']);
|
||||
},
|
||||
meta: {
|
||||
error: {
|
||||
title: 'Failure',
|
||||
message: 'Unable to delete git credential',
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useGitCredentials(userId: number) {
|
||||
return useQuery('gitcredentials', () => getGitCredentials(userId), {
|
||||
staleTime: 20,
|
||||
meta: {
|
||||
error: {
|
||||
title: 'Failure',
|
||||
message: 'Unable to retrieve git credentials',
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useGitCredential(userId: number, id: number) {
|
||||
return useQuery(['gitcredentials', id], () => getGitCredential(userId, id), {
|
||||
meta: {
|
||||
error: {
|
||||
title: 'Failure',
|
||||
message: 'Unable to retrieve git credential',
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateGitCredentialMutation() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation(createGitCredential, {
|
||||
onSuccess: (_, payload) => {
|
||||
notifySuccess('Credentials created successfully', payload.name);
|
||||
return queryClient.invalidateQueries(['gitcredentials']);
|
||||
},
|
||||
meta: {
|
||||
error: {
|
||||
title: 'Failure',
|
||||
message: 'Unable to create credential',
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function buildGitUrl(userId: number, credentialId?: number) {
|
||||
return credentialId
|
||||
? `/users/${userId}/gitcredentials/${credentialId}`
|
||||
: `/users/${userId}/gitcredentials`;
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
import {
|
||||
PaginationTableSettings,
|
||||
SortableTableSettings,
|
||||
} from '@@/datatables/types';
|
||||
|
||||
export interface GitCredentialTableSettings
|
||||
extends SortableTableSettings,
|
||||
PaginationTableSettings {}
|
||||
|
||||
export interface GitCredentialFormValues {
|
||||
name: string;
|
||||
username?: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface CreateGitCredentialPayload {
|
||||
userId: number;
|
||||
name: string;
|
||||
username?: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface UpdateGitCredentialPayload {
|
||||
name: string;
|
||||
username?: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export type GitCredential = {
|
||||
id: number;
|
||||
userId: number;
|
||||
name: string;
|
||||
username: string;
|
||||
creationDate: number;
|
||||
};
|
|
@ -1,15 +0,0 @@
|
|||
.canvas-container {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.kvm-maximized {
|
||||
position: fixed;
|
||||
background: white;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
z-index: 1000;
|
||||
max-height: 100% !important;
|
||||
overflow-y: scroll;
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
import { KVM } from '@open-amt-cloud-toolkit/ui-toolkit-react/reactjs/src/kvm.bundle';
|
||||
|
||||
import { react2angular } from '@/react-tools/react2angular';
|
||||
|
||||
import './KVMControl.css';
|
||||
|
||||
export interface KVMControlProps {
|
||||
deviceId: string;
|
||||
server: string;
|
||||
token: string;
|
||||
}
|
||||
|
||||
export function KVMControl({ deviceId, server, token }: KVMControlProps) {
|
||||
if (!deviceId || !server || !token) return <div>Loading...</div>;
|
||||
|
||||
return (
|
||||
<KVM
|
||||
deviceId={deviceId}
|
||||
mpsServer={`https://${server}/mps/ws/relay`}
|
||||
authToken={token}
|
||||
mouseDebounceTime="200"
|
||||
canvasHeight="100%"
|
||||
canvasWidth="100%"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const KVMControlAngular = react2angular(KVMControl, [
|
||||
'deviceId',
|
||||
'server',
|
||||
'token',
|
||||
]);
|
|
@ -1,5 +0,0 @@
|
|||
import angular from 'angular';
|
||||
|
||||
import { KVMControlAngular } from '@/portainer/views/endpoints/kvm/KVMControl';
|
||||
|
||||
angular.module('portainer.app').component('kvmControl', KVMControlAngular).name;
|
|
@ -1,5 +1,5 @@
|
|||
import { getEnvironments } from '@/react/portainer/environments/environment.service';
|
||||
import { restoreOptions } from './restore-options';
|
||||
import { restoreOptions } from '@/react/portainer/init/InitAdminView/restore-options';
|
||||
|
||||
angular.module('portainer.app').controller('InitAdminController', [
|
||||
'$scope',
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
import { Download, Upload } from 'lucide-react';
|
||||
|
||||
import { FeatureId } from '@/react/portainer/feature-flags/enums';
|
||||
|
||||
import { BoxSelectorOption } from '@@/BoxSelector';
|
||||
|
||||
export const restoreOptions: ReadonlyArray<BoxSelectorOption<string>> = [
|
||||
{
|
||||
id: 'restore_file',
|
||||
value: 'file',
|
||||
icon: Upload,
|
||||
iconType: 'badge',
|
||||
label: 'Upload backup file',
|
||||
},
|
||||
{
|
||||
id: 'restore_s3',
|
||||
value: 's3',
|
||||
icon: Download,
|
||||
iconType: 'badge',
|
||||
label: 'Retrieve from S3',
|
||||
feature: FeatureId.S3_RESTORE,
|
||||
},
|
||||
] as const;
|
|
@ -1,7 +1,7 @@
|
|||
import _ from 'lodash';
|
||||
import { RegistryTypes } from 'Portainer/models/registryTypes';
|
||||
import { RegistryCreateFormValues } from 'Portainer/models/registry';
|
||||
import { options } from './options';
|
||||
import { options } from '@/react/portainer/registries/CreateView/options';
|
||||
|
||||
class CreateRegistryController {
|
||||
/* @ngInject */
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
import { Edit } from 'lucide-react';
|
||||
|
||||
import Docker from '@/assets/ico/vendor/docker.svg?c';
|
||||
import Ecr from '@/assets/ico/vendor/ecr.svg?c';
|
||||
import Quay from '@/assets/ico/vendor/quay.svg?c';
|
||||
import Proget from '@/assets/ico/vendor/proget.svg?c';
|
||||
import Azure from '@/assets/ico/vendor/azure.svg?c';
|
||||
import Gitlab from '@/assets/ico/vendor/gitlab.svg?c';
|
||||
|
||||
import { BadgeIcon } from '@@/BadgeIcon';
|
||||
|
||||
export const options = [
|
||||
{
|
||||
id: 'registry_dockerhub',
|
||||
icon: Docker,
|
||||
label: 'DockerHub',
|
||||
description: 'DockerHub authenticated account',
|
||||
value: '6',
|
||||
},
|
||||
{
|
||||
id: 'registry_aws_ecr',
|
||||
icon: Ecr,
|
||||
label: 'AWS ECR',
|
||||
description: 'Amazon elastic container registry',
|
||||
value: '7',
|
||||
},
|
||||
{
|
||||
id: 'registry_quay',
|
||||
icon: Quay,
|
||||
label: 'Quay.io',
|
||||
description: 'Quay container registry',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
id: 'registry_proget',
|
||||
icon: Proget,
|
||||
label: 'ProGet',
|
||||
description: 'ProGet container registry',
|
||||
value: '5',
|
||||
},
|
||||
{
|
||||
id: 'registry_azure',
|
||||
icon: Azure,
|
||||
label: 'Azure',
|
||||
description: 'Azure container registry',
|
||||
value: '2',
|
||||
},
|
||||
{
|
||||
id: 'registry_gitlab',
|
||||
icon: Gitlab,
|
||||
label: 'GitLab',
|
||||
description: 'GitLab container registry',
|
||||
value: '4',
|
||||
},
|
||||
{
|
||||
id: 'registry_custom',
|
||||
icon: <BadgeIcon icon={Edit} />,
|
||||
label: 'Custom registry',
|
||||
description: 'Define your own registry',
|
||||
value: '3',
|
||||
},
|
||||
];
|
|
@ -1,40 +0,0 @@
|
|||
import { ArrowDownCircle } from 'lucide-react';
|
||||
|
||||
import { FeatureId } from '@/react/portainer/feature-flags/enums';
|
||||
import Microsoft from '@/assets/ico/vendor/microsoft.svg?c';
|
||||
import Ldap from '@/assets/ico/ldap.svg?c';
|
||||
import OAuth from '@/assets/ico/oauth.svg?c';
|
||||
|
||||
import { BadgeIcon } from '@@/BadgeIcon';
|
||||
|
||||
export const options = [
|
||||
{
|
||||
id: 'auth_internal',
|
||||
icon: <BadgeIcon icon={ArrowDownCircle} />,
|
||||
label: 'Internal',
|
||||
description: 'Internal authentication mechanism',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
id: 'auth_ldap',
|
||||
icon: Ldap,
|
||||
label: 'LDAP',
|
||||
description: 'LDAP authentication',
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
id: 'auth_ad',
|
||||
icon: Microsoft,
|
||||
label: 'Microsoft Active Directory',
|
||||
description: 'AD authentication',
|
||||
value: 4,
|
||||
feature: FeatureId.HIDE_INTERNAL_AUTH,
|
||||
},
|
||||
{
|
||||
id: 'auth_oauth',
|
||||
icon: OAuth,
|
||||
label: 'OAuth',
|
||||
description: 'OAuth authentication',
|
||||
value: 3,
|
||||
},
|
||||
];
|
|
@ -2,7 +2,7 @@ import angular from 'angular';
|
|||
import _ from 'lodash-es';
|
||||
|
||||
import { buildLdapSettingsModel, buildAdSettingsModel } from '@/portainer/settings/authentication/ldap/ldap-settings.model';
|
||||
import { options } from './options';
|
||||
import { options } from '@/react/portainer/settings/AuthenticationView/InternalAuth/options';
|
||||
|
||||
angular.module('portainer.app').controller('SettingsAuthenticationController', SettingsAuthenticationController);
|
||||
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
import { DownloadCloud, UploadCloud } from 'lucide-react';
|
||||
|
||||
import { FeatureId } from '@/react/portainer/feature-flags/enums';
|
||||
|
||||
import { BadgeIcon } from '@@/BadgeIcon';
|
||||
|
||||
export const options = [
|
||||
{
|
||||
id: 'backup_file',
|
||||
icon: <BadgeIcon icon={DownloadCloud} />,
|
||||
label: 'Download backup file',
|
||||
value: 'file',
|
||||
},
|
||||
{
|
||||
id: 'backup_s3',
|
||||
icon: <BadgeIcon icon={UploadCloud} />,
|
||||
label: 'Store in S3',
|
||||
description: 'Define a cron schedule',
|
||||
value: 's3',
|
||||
feature: FeatureId.S3_BACKUP_SETTING,
|
||||
},
|
||||
];
|
|
@ -1,7 +1,7 @@
|
|||
import angular from 'angular';
|
||||
|
||||
import { FeatureId } from '@/react/portainer/feature-flags/enums';
|
||||
import { options } from './options';
|
||||
import { options } from '@/react/portainer/settings/SettingsView/backup-options';
|
||||
|
||||
angular.module('portainer.app').controller('SettingsController', [
|
||||
'$scope',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue