fix(edge/templates): fix issues with git templates [EE-6357] (#10679)
parent
974378c9b5
commit
2a18c9f215
|
@ -112,8 +112,7 @@ export default class CreateEdgeStackViewController {
|
||||||
PrePullImage: template.EdgeSettings.PrePullImage || false,
|
PrePullImage: template.EdgeSettings.PrePullImage || false,
|
||||||
RetryDeploy: template.EdgeSettings.RetryDeploy || false,
|
RetryDeploy: template.EdgeSettings.RetryDeploy || false,
|
||||||
PrivateRegistryId: template.EdgeSettings.PrivateRegistryId || null,
|
PrivateRegistryId: template.EdgeSettings.PrivateRegistryId || null,
|
||||||
SupportRelativePath: template.EdgeSettings.RelativePathSettings.SupportRelativePath || false,
|
...template.EdgeSettings.RelativePathSettings,
|
||||||
FilesystemPath: template.EdgeSettings.RelativePathSettings.FilesystemPath || '',
|
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
};
|
};
|
||||||
|
@ -195,11 +194,7 @@ export default class CreateEdgeStackViewController {
|
||||||
createStack() {
|
createStack() {
|
||||||
return this.$async(async () => {
|
return this.$async(async () => {
|
||||||
const name = this.formValues.Name;
|
const name = this.formValues.Name;
|
||||||
let method = this.state.Method;
|
const method = getMethod(this.state.Method, this.state.templateValues.template);
|
||||||
|
|
||||||
if (method === 'template') {
|
|
||||||
method = 'editor';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.validateForm(method)) {
|
if (!this.validateForm(method)) {
|
||||||
return;
|
return;
|
||||||
|
@ -338,3 +333,20 @@ export default class CreateEdgeStackViewController {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {'template'|'repository' | 'editor' | 'upload'} method
|
||||||
|
* @param {import('@/react/portainer/templates/custom-templates/types').CustomTemplate | undefined} template
|
||||||
|
* @returns 'repository' | 'editor' | 'upload'
|
||||||
|
*/
|
||||||
|
function getMethod(method, template) {
|
||||||
|
if (method !== 'template') {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (template && template.GitConfig) {
|
||||||
|
return 'repository';
|
||||||
|
}
|
||||||
|
return 'editor';
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,11 @@ class DockerComposeFormController {
|
||||||
this.onChangeFile = this.onChangeFile.bind(this);
|
this.onChangeFile = this.onChangeFile.bind(this);
|
||||||
this.onChangeMethod = this.onChangeMethod.bind(this);
|
this.onChangeMethod = this.onChangeMethod.bind(this);
|
||||||
this.onChangeFormValues = this.onChangeFormValues.bind(this);
|
this.onChangeFormValues = this.onChangeFormValues.bind(this);
|
||||||
|
this.isGitTemplate = this.isGitTemplate.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
isGitTemplate() {
|
||||||
|
return this.state.Method === 'template' && !!this.templateValues.template && !!this.templateValues.template.GitConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
onChangeFormValues(newValues) {
|
onChangeFormValues(newValues) {
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
ng-required="true"
|
ng-required="true"
|
||||||
yml="true"
|
yml="true"
|
||||||
placeholder="Define or paste the content of your docker compose file here"
|
placeholder="Define or paste the content of your docker compose file here"
|
||||||
read-only="$ctrl.state.Method === 'template' && $ctrl.template.GitConfig"
|
versions="$ctrl.formValues.versions"
|
||||||
|
read-only="$ctrl.isGitTemplate()"
|
||||||
>
|
>
|
||||||
<editor-description>
|
<editor-description>
|
||||||
You can get more information about Compose file format in the
|
You can get more information about Compose file format in the
|
||||||
|
@ -28,7 +29,7 @@
|
||||||
<file-upload-description> You can upload a Compose file from your computer. </file-upload-description>
|
<file-upload-description> You can upload a Compose file from your computer. </file-upload-description>
|
||||||
</file-upload-form>
|
</file-upload-form>
|
||||||
|
|
||||||
<div ng-if="$ctrl.state.Method == 'repository'">
|
<div ng-if="$ctrl.state.Method == 'repository' || $ctrl.isGitTemplate()">
|
||||||
<git-form
|
<git-form
|
||||||
value="$ctrl.formValues"
|
value="$ctrl.formValues"
|
||||||
on-change="($ctrl.onChangeFormValues)"
|
on-change="($ctrl.onChangeFormValues)"
|
||||||
|
|
|
@ -27,14 +27,13 @@ import { EdgeGroup } from '@/react/edge/edge-groups/types';
|
||||||
import { DeploymentType, EdgeStack } from '@/react/edge/edge-stacks/types';
|
import { DeploymentType, EdgeStack } from '@/react/edge/edge-stacks/types';
|
||||||
import { EdgeGroupsSelector } from '@/react/edge/edge-stacks/components/EdgeGroupsSelector';
|
import { EdgeGroupsSelector } from '@/react/edge/edge-stacks/components/EdgeGroupsSelector';
|
||||||
import { EdgeStackDeploymentTypeSelector } from '@/react/edge/edge-stacks/components/EdgeStackDeploymentTypeSelector';
|
import { EdgeStackDeploymentTypeSelector } from '@/react/edge/edge-stacks/components/EdgeStackDeploymentTypeSelector';
|
||||||
import { useCurrentUser } from '@/react/hooks/useUser';
|
import { notifySuccess } from '@/portainer/services/notifications';
|
||||||
import { useCreateGitCredentialMutation } from '@/react/portainer/account/git-credentials/git-credentials.service';
|
|
||||||
import { notifyError, notifySuccess } from '@/portainer/services/notifications';
|
|
||||||
import { EnvironmentType } from '@/react/portainer/environments/types';
|
import { EnvironmentType } from '@/react/portainer/environments/types';
|
||||||
import { Registry } from '@/react/portainer/registries/types';
|
import { Registry } from '@/react/portainer/registries/types';
|
||||||
import { useRegistries } from '@/react/portainer/registries/queries/useRegistries';
|
import { useRegistries } from '@/react/portainer/registries/queries/useRegistries';
|
||||||
import { RelativePathFieldset } from '@/react/portainer/gitops/RelativePathFieldset/RelativePathFieldset';
|
import { RelativePathFieldset } from '@/react/portainer/gitops/RelativePathFieldset/RelativePathFieldset';
|
||||||
import { parseRelativePathResponse } from '@/react/portainer/gitops/RelativePathFieldset/utils';
|
import { parseRelativePathResponse } from '@/react/portainer/gitops/RelativePathFieldset/utils';
|
||||||
|
import { useSaveCredentialsIfRequired } from '@/react/portainer/account/git-credentials/queries/useCreateGitCredentialsMutation';
|
||||||
|
|
||||||
import { LoadingButton } from '@@/buttons';
|
import { LoadingButton } from '@@/buttons';
|
||||||
import { FormSection } from '@@/form-components/FormSection';
|
import { FormSection } from '@@/form-components/FormSection';
|
||||||
|
@ -65,8 +64,8 @@ interface FormValues {
|
||||||
export function GitForm({ stack }: { stack: EdgeStack }) {
|
export function GitForm({ stack }: { stack: EdgeStack }) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const updateStackMutation = useUpdateEdgeStackGitMutation();
|
const updateStackMutation = useUpdateEdgeStackGitMutation();
|
||||||
const saveCredentialsMutation = useCreateGitCredentialMutation();
|
const { saveCredentials, isLoading: isSaveCredentialsLoading } =
|
||||||
const { user } = useCurrentUser();
|
useSaveCredentialsIfRequired();
|
||||||
|
|
||||||
if (!stack.GitConfig) {
|
if (!stack.GitConfig) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -95,7 +94,9 @@ export function GitForm({ stack }: { stack: EdgeStack }) {
|
||||||
onUpdateSettingsClick={handleUpdateSettings}
|
onUpdateSettingsClick={handleUpdateSettings}
|
||||||
gitPath={gitConfig.ConfigFilePath}
|
gitPath={gitConfig.ConfigFilePath}
|
||||||
gitUrl={gitConfig.URL}
|
gitUrl={gitConfig.URL}
|
||||||
isLoading={updateStackMutation.isLoading}
|
isLoading={
|
||||||
|
updateStackMutation.isLoading || isSaveCredentialsLoading
|
||||||
|
}
|
||||||
isUpdateVersion={!!updateStackMutation.variables?.updateVersion}
|
isUpdateVersion={!!updateStackMutation.variables?.updateVersion}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -105,9 +106,7 @@ export function GitForm({ stack }: { stack: EdgeStack }) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const credentialId = await saveCredentialsIfRequired(
|
const credentialId = await saveCredentials(values.authentication);
|
||||||
values.authentication
|
|
||||||
);
|
|
||||||
|
|
||||||
updateStackMutation.mutate(getPayload(values, credentialId, false), {
|
updateStackMutation.mutate(getPayload(values, credentialId, false), {
|
||||||
onSuccess() {
|
onSuccess() {
|
||||||
|
@ -121,7 +120,7 @@ export function GitForm({ stack }: { stack: EdgeStack }) {
|
||||||
);
|
);
|
||||||
|
|
||||||
async function handleSubmit(values: FormValues) {
|
async function handleSubmit(values: FormValues) {
|
||||||
const credentialId = await saveCredentialsIfRequired(values.authentication);
|
const credentialId = await saveCredentials(values.authentication);
|
||||||
|
|
||||||
updateStackMutation.mutate(getPayload(values, credentialId, true), {
|
updateStackMutation.mutate(getPayload(values, credentialId, true), {
|
||||||
onSuccess() {
|
onSuccess() {
|
||||||
|
@ -151,29 +150,6 @@ export function GitForm({ stack }: { stack: EdgeStack }) {
|
||||||
...values,
|
...values,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveCredentialsIfRequired(authentication: GitAuthModel) {
|
|
||||||
if (
|
|
||||||
!authentication.SaveCredential ||
|
|
||||||
!authentication.RepositoryPassword ||
|
|
||||||
!authentication.NewCredentialName
|
|
||||||
) {
|
|
||||||
return authentication.RepositoryGitCredentialID;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const credential = await saveCredentialsMutation.mutateAsync({
|
|
||||||
userId: user.Id,
|
|
||||||
username: authentication.RepositoryUsername,
|
|
||||||
password: authentication.RepositoryPassword,
|
|
||||||
name: authentication.NewCredentialName,
|
|
||||||
});
|
|
||||||
return credential.id;
|
|
||||||
} catch (err) {
|
|
||||||
notifyError('Error', err as Error, 'Unable to save credentials');
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function InnerForm({
|
function InnerForm({
|
||||||
|
|
|
@ -29,7 +29,7 @@ export function PrivateRegistryFieldsetWrapper({
|
||||||
}) {
|
}) {
|
||||||
const dryRunMutation = useParseRegistries();
|
const dryRunMutation = useParseRegistries();
|
||||||
|
|
||||||
const registriesQuery = useRegistries();
|
const registriesQuery = useRegistries({ hideDefault: true });
|
||||||
|
|
||||||
if (!registriesQuery.data) {
|
if (!registriesQuery.data) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -7,9 +7,12 @@ import { useCreateTemplateMutation } from '@/react/portainer/templates/custom-te
|
||||||
import { Platform } from '@/react/portainer/templates/types';
|
import { Platform } from '@/react/portainer/templates/types';
|
||||||
import { useFetchTemplateFile } from '@/react/portainer/templates/app-templates/queries/useFetchTemplateFile';
|
import { useFetchTemplateFile } from '@/react/portainer/templates/app-templates/queries/useFetchTemplateFile';
|
||||||
import { getDefaultEdgeTemplateSettings } from '@/react/portainer/templates/custom-templates/types';
|
import { getDefaultEdgeTemplateSettings } from '@/react/portainer/templates/custom-templates/types';
|
||||||
|
import { useSaveCredentialsIfRequired } from '@/react/portainer/account/git-credentials/queries/useCreateGitCredentialsMutation';
|
||||||
|
|
||||||
import { editor } from '@@/BoxSelector/common-options/build-methods';
|
import { editor } from '@@/BoxSelector/common-options/build-methods';
|
||||||
|
|
||||||
|
import { toGitRequest } from '../common/git';
|
||||||
|
|
||||||
import { InnerForm } from './InnerForm';
|
import { InnerForm } from './InnerForm';
|
||||||
import { FormValues } from './types';
|
import { FormValues } from './types';
|
||||||
import { useValidation } from './useValidation';
|
import { useValidation } from './useValidation';
|
||||||
|
@ -19,6 +22,8 @@ export function CreateTemplateForm() {
|
||||||
const mutation = useCreateTemplateMutation();
|
const mutation = useCreateTemplateMutation();
|
||||||
const validation = useValidation();
|
const validation = useValidation();
|
||||||
const { appTemplateId, type } = useParams();
|
const { appTemplateId, type } = useParams();
|
||||||
|
const { saveCredentials, isLoading: isSaveCredentialsLoading } =
|
||||||
|
useSaveCredentialsIfRequired();
|
||||||
|
|
||||||
const fileContentQuery = useFetchTemplateFile(appTemplateId);
|
const fileContentQuery = useFetchTemplateFile(appTemplateId);
|
||||||
|
|
||||||
|
@ -58,13 +63,19 @@ export function CreateTemplateForm() {
|
||||||
validationSchema={validation}
|
validationSchema={validation}
|
||||||
validateOnMount
|
validateOnMount
|
||||||
>
|
>
|
||||||
<InnerForm isLoading={mutation.isLoading} />
|
<InnerForm isLoading={mutation.isLoading || isSaveCredentialsLoading} />
|
||||||
</Formik>
|
</Formik>
|
||||||
);
|
);
|
||||||
|
|
||||||
function handleSubmit(values: FormValues) {
|
async function handleSubmit(values: FormValues) {
|
||||||
|
const credentialId = await saveCredentials(values.Git);
|
||||||
|
|
||||||
mutation.mutate(
|
mutation.mutate(
|
||||||
{ ...values, EdgeTemplate: true },
|
{
|
||||||
|
...values,
|
||||||
|
EdgeTemplate: true,
|
||||||
|
Git: toGitRequest(values.Git, credentialId),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
onSuccess() {
|
onSuccess() {
|
||||||
notifySuccess('Success', 'Template created');
|
notifySuccess('Success', 'Template created');
|
||||||
|
|
|
@ -42,7 +42,7 @@ export function InnerForm({ isLoading }: { isLoading: boolean }) {
|
||||||
usePreventExit(
|
usePreventExit(
|
||||||
initialValues.FileContent,
|
initialValues.FileContent,
|
||||||
values.FileContent,
|
values.FileContent,
|
||||||
values.Method === editor.value && !isSubmitting
|
values.Method === editor.value && !isSubmitting && !isLoading
|
||||||
);
|
);
|
||||||
|
|
||||||
const isGit = values.Method === git.value;
|
const isGit = values.Method === git.value;
|
||||||
|
@ -108,16 +108,7 @@ export function InnerForm({ isLoading }: { isLoading: boolean }) {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isTemplateVariablesEnabled && (
|
{isGit && (
|
||||||
<CustomTemplatesVariablesDefinitionField
|
|
||||||
value={values.Variables}
|
|
||||||
onChange={(values) => setFieldValue('Variables', values)}
|
|
||||||
isVariablesNamesFromParent={values.Method === editor.value}
|
|
||||||
errors={errors.Variables}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{values.Method === git.value && (
|
|
||||||
<GitForm
|
<GitForm
|
||||||
value={values.Git}
|
value={values.Git}
|
||||||
onChange={(newValues) =>
|
onChange={(newValues) =>
|
||||||
|
@ -130,6 +121,15 @@ export function InnerForm({ isLoading }: { isLoading: boolean }) {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{isTemplateVariablesEnabled && (
|
||||||
|
<CustomTemplatesVariablesDefinitionField
|
||||||
|
value={values.Variables}
|
||||||
|
onChange={(values) => setFieldValue('Variables', values)}
|
||||||
|
isVariablesNamesFromParent={values.Method === editor.value}
|
||||||
|
errors={errors.Variables}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{values.EdgeSettings && (
|
{values.EdgeSettings && (
|
||||||
<EdgeSettingsFieldset
|
<EdgeSettingsFieldset
|
||||||
setValues={(edgeSetValues) =>
|
setValues={(edgeSetValues) =>
|
||||||
|
|
|
@ -11,6 +11,9 @@ import {
|
||||||
isTemplateVariablesEnabled,
|
isTemplateVariablesEnabled,
|
||||||
} from '@/react/portainer/custom-templates/components/utils';
|
} from '@/react/portainer/custom-templates/components/utils';
|
||||||
import { toGitFormModel } from '@/react/portainer/gitops/types';
|
import { toGitFormModel } from '@/react/portainer/gitops/types';
|
||||||
|
import { useSaveCredentialsIfRequired } from '@/react/portainer/account/git-credentials/queries/useCreateGitCredentialsMutation';
|
||||||
|
|
||||||
|
import { toGitRequest } from '../common/git';
|
||||||
|
|
||||||
import { InnerForm } from './InnerForm';
|
import { InnerForm } from './InnerForm';
|
||||||
import { FormValues } from './types';
|
import { FormValues } from './types';
|
||||||
|
@ -22,6 +25,8 @@ export function EditTemplateForm({ template }: { template: CustomTemplate }) {
|
||||||
const isGit = !!template.GitConfig;
|
const isGit = !!template.GitConfig;
|
||||||
const validation = useValidation(template.Id, isGit);
|
const validation = useValidation(template.Id, isGit);
|
||||||
const fileQuery = useCustomTemplateFile(template.Id, isGit);
|
const fileQuery = useCustomTemplateFile(template.Id, isGit);
|
||||||
|
const { saveCredentials, isLoading: isSaveCredentialsLoading } =
|
||||||
|
useSaveCredentialsIfRequired();
|
||||||
|
|
||||||
if (fileQuery.isLoading) {
|
if (fileQuery.isLoading) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -49,7 +54,7 @@ export function EditTemplateForm({ template }: { template: CustomTemplate }) {
|
||||||
validateOnMount
|
validateOnMount
|
||||||
>
|
>
|
||||||
<InnerForm
|
<InnerForm
|
||||||
isLoading={mutation.isLoading}
|
isLoading={mutation.isLoading || isSaveCredentialsLoading}
|
||||||
isEditorReadonly={isGit}
|
isEditorReadonly={isGit}
|
||||||
gitFileContent={isGit ? fileQuery.data : ''}
|
gitFileContent={isGit ? fileQuery.data : ''}
|
||||||
refreshGitFile={fileQuery.refetch}
|
refreshGitFile={fileQuery.refetch}
|
||||||
|
@ -60,7 +65,9 @@ export function EditTemplateForm({ template }: { template: CustomTemplate }) {
|
||||||
</Formik>
|
</Formik>
|
||||||
);
|
);
|
||||||
|
|
||||||
function handleSubmit(values: FormValues) {
|
async function handleSubmit(values: FormValues) {
|
||||||
|
const credentialId = await saveCredentials(values.Git);
|
||||||
|
|
||||||
mutation.mutate(
|
mutation.mutate(
|
||||||
{
|
{
|
||||||
id: template.Id,
|
id: template.Id,
|
||||||
|
@ -74,7 +81,7 @@ export function EditTemplateForm({ template }: { template: CustomTemplate }) {
|
||||||
Platform: values.Platform,
|
Platform: values.Platform,
|
||||||
Variables: values.Variables,
|
Variables: values.Variables,
|
||||||
EdgeSettings: values.EdgeSettings,
|
EdgeSettings: values.EdgeSettings,
|
||||||
...values.Git,
|
...(values.Git ? toGitRequest(values.Git, credentialId) : {}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess() {
|
onSuccess() {
|
||||||
|
|
|
@ -51,7 +51,7 @@ export function InnerForm({
|
||||||
usePreventExit(
|
usePreventExit(
|
||||||
initialValues.FileContent,
|
initialValues.FileContent,
|
||||||
values.FileContent,
|
values.FileContent,
|
||||||
!isEditorReadonly && !isSubmitting
|
!isEditorReadonly && !isSubmitting && !isLoading
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<Form className="form-horizontal">
|
<Form className="form-horizontal">
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { transformGitAuthenticationViewModel } from '@/react/portainer/gitops/AuthFieldset/utils';
|
||||||
|
import { GitFormModel } from '@/react/portainer/gitops/types';
|
||||||
|
|
||||||
|
export function toGitRequest(
|
||||||
|
gitConfig: GitFormModel,
|
||||||
|
credentialId: number | undefined
|
||||||
|
): GitFormModel {
|
||||||
|
return {
|
||||||
|
...gitConfig,
|
||||||
|
...getGitAuthValues(gitConfig, credentialId),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGitAuthValues(
|
||||||
|
gitConfig: GitFormModel | undefined,
|
||||||
|
credentialId: number | undefined
|
||||||
|
) {
|
||||||
|
if (!credentialId) {
|
||||||
|
return gitConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
const authModel = transformGitAuthenticationViewModel({
|
||||||
|
...gitConfig,
|
||||||
|
RepositoryGitCredentialID: credentialId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return authModel
|
||||||
|
? {
|
||||||
|
RepositoryAuthentication: true,
|
||||||
|
RepositoryGitCredentialID: authModel.GitCredentialID,
|
||||||
|
RepositoryPassword: authModel.Password,
|
||||||
|
RepositoryUsername: authModel.Username,
|
||||||
|
}
|
||||||
|
: {};
|
||||||
|
}
|
|
@ -6,25 +6,7 @@ import { UserId } from '@/portainer/users/types';
|
||||||
|
|
||||||
import { isBE } from '../../feature-flags/feature-flags.service';
|
import { isBE } from '../../feature-flags/feature-flags.service';
|
||||||
|
|
||||||
import {
|
import { GitCredential, UpdateGitCredentialPayload } from './types';
|
||||||
CreateGitCredentialPayload,
|
|
||||||
GitCredential,
|
|
||||||
UpdateGitCredentialPayload,
|
|
||||||
} from './types';
|
|
||||||
|
|
||||||
export async function createGitCredential(
|
|
||||||
gitCredential: CreateGitCredentialPayload
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
const { data } = await axios.post<{ gitCredential: GitCredential }>(
|
|
||||||
buildGitUrl(gitCredential.userId),
|
|
||||||
gitCredential
|
|
||||||
);
|
|
||||||
return data.gitCredential;
|
|
||||||
} catch (e) {
|
|
||||||
throw parseAxiosError(e as Error, 'Unable to create git credential');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getGitCredentials(userId: number) {
|
export async function getGitCredentials(userId: number) {
|
||||||
try {
|
try {
|
||||||
|
@ -141,24 +123,7 @@ export function useGitCredential(userId: number, id: number) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useCreateGitCredentialMutation() {
|
export function buildGitUrl(userId: number, credentialId?: number) {
|
||||||
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
|
return credentialId
|
||||||
? `/users/${userId}/gitcredentials/${credentialId}`
|
? `/users/${userId}/gitcredentials/${credentialId}`
|
||||||
: `/users/${userId}/gitcredentials`;
|
: `/users/${userId}/gitcredentials`;
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
import { useQueryClient, useMutation } from 'react-query';
|
||||||
|
|
||||||
|
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||||
|
import { notifyError, notifySuccess } from '@/portainer/services/notifications';
|
||||||
|
import { GitAuthModel } from '@/react/portainer/gitops/types';
|
||||||
|
import { useCurrentUser } from '@/react/hooks/useUser';
|
||||||
|
|
||||||
|
import { GitCredential } from '../types';
|
||||||
|
import { buildGitUrl } from '../git-credentials.service';
|
||||||
|
|
||||||
|
export interface CreateGitCredentialPayload {
|
||||||
|
userId: number;
|
||||||
|
name: string;
|
||||||
|
username?: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
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',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createGitCredential(gitCredential: CreateGitCredentialPayload) {
|
||||||
|
try {
|
||||||
|
const { data } = await axios.post<{ gitCredential: GitCredential }>(
|
||||||
|
buildGitUrl(gitCredential.userId),
|
||||||
|
gitCredential
|
||||||
|
);
|
||||||
|
return data.gitCredential;
|
||||||
|
} catch (e) {
|
||||||
|
throw parseAxiosError(e as Error, 'Unable to create git credential');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useSaveCredentialsIfRequired() {
|
||||||
|
const saveCredentialsMutation = useCreateGitCredentialMutation();
|
||||||
|
const { user } = useCurrentUser();
|
||||||
|
|
||||||
|
return {
|
||||||
|
saveCredentials: saveCredentialsIfRequired,
|
||||||
|
isLoading: saveCredentialsMutation.isLoading,
|
||||||
|
};
|
||||||
|
|
||||||
|
async function saveCredentialsIfRequired(authentication?: GitAuthModel) {
|
||||||
|
if (!authentication) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!authentication.SaveCredential ||
|
||||||
|
!authentication.RepositoryPassword ||
|
||||||
|
!authentication.NewCredentialName
|
||||||
|
) {
|
||||||
|
return authentication.RepositoryGitCredentialID;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const credential = await saveCredentialsMutation.mutateAsync({
|
||||||
|
userId: user.Id,
|
||||||
|
username: authentication.RepositoryUsername,
|
||||||
|
password: authentication.RepositoryPassword,
|
||||||
|
name: authentication.NewCredentialName,
|
||||||
|
});
|
||||||
|
return credential.id;
|
||||||
|
} catch (err) {
|
||||||
|
notifyError('Error', err as Error, 'Unable to save credentials');
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,13 +13,6 @@ export interface GitCredentialFormValues {
|
||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateGitCredentialPayload {
|
|
||||||
userId: number;
|
|
||||||
name: string;
|
|
||||||
username?: string;
|
|
||||||
password: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UpdateGitCredentialPayload {
|
export interface UpdateGitCredentialPayload {
|
||||||
name: string;
|
name: string;
|
||||||
username?: string;
|
username?: string;
|
||||||
|
|
|
@ -35,7 +35,7 @@ export function RelativePathFieldset({
|
||||||
|
|
||||||
const { errors } = useValidation(value);
|
const { errors } = useValidation(value);
|
||||||
|
|
||||||
const { enableFsPath0, enableFsPath1, toggleFsPath } = useEnableFsPath();
|
const { enableFsPath0, enableFsPath1, toggleFsPath } = useEnableFsPath(value);
|
||||||
|
|
||||||
const pathTip0 =
|
const pathTip0 =
|
||||||
'For relative path volumes use with Docker Swarm, you must have a network filesystem which all of your nodes can access.';
|
'For relative path volumes use with Docker Swarm, you must have a network filesystem which all of your nodes can access.';
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
export function useEnableFsPath() {
|
import { RelativePathModel } from './types';
|
||||||
const [state, setState] = useState<number[]>([]);
|
|
||||||
|
export function useEnableFsPath(initialValue: RelativePathModel) {
|
||||||
|
const [state, setState] = useState<number[]>(() =>
|
||||||
|
initialValue.SupportPerDeviceConfigs ? [1] : []
|
||||||
|
);
|
||||||
|
|
||||||
const enableFsPath0 = state.length && state[0] === 0;
|
const enableFsPath0 = state.length && state[0] === 0;
|
||||||
const enableFsPath1 = state.length && state[0] === 1;
|
const enableFsPath1 = state.length && state[0] === 1;
|
||||||
|
|
|
@ -9,11 +9,7 @@ import { usePublicSettings } from '../../settings/queries';
|
||||||
import { queryKeys } from './query-keys';
|
import { queryKeys } from './query-keys';
|
||||||
|
|
||||||
export function useRegistries<T = Registry[]>(
|
export function useRegistries<T = Registry[]>(
|
||||||
queryOptions: {
|
queryOptions: GenericRegistriesQueryOptions<T> = {}
|
||||||
enabled?: boolean;
|
|
||||||
select?: (registries: Registry[]) => T;
|
|
||||||
onSuccess?: (data: T) => void;
|
|
||||||
} = {}
|
|
||||||
) {
|
) {
|
||||||
return useGenericRegistriesQuery(
|
return useGenericRegistriesQuery(
|
||||||
queryKeys.base(),
|
queryKeys.base(),
|
||||||
|
@ -22,13 +18,11 @@ export function useRegistries<T = Registry[]>(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @field hideDefault - is used to hide the default registry from the list of registries, regardless of the user's settings. Kubernetes views use this.
|
|
||||||
*/
|
|
||||||
export type GenericRegistriesQueryOptions<T> = {
|
export type GenericRegistriesQueryOptions<T> = {
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
select?: (registries: Registry[]) => T;
|
select?: (registries: Registry[]) => T;
|
||||||
onSuccess?: (data: T) => void;
|
onSuccess?: (data: T) => void;
|
||||||
|
/** is used to hide the default registry from the list of registries, regardless of the user's settings. Kubernetes views use this. */
|
||||||
hideDefault?: boolean;
|
hideDefault?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue