2022-09-07 04:25:00 +00:00
import { ResourceControlViewModel } from '@/react/portainer/access-control/models/ResourceControlViewModel' ;
2021-09-02 05:28:51 +00:00
import { AccessControlFormData } from '@/portainer/components/accessControlForm/porAccessControlFormModel' ;
2022-11-13 08:10:18 +00:00
import { isBE } from '@/react/portainer/feature-flags/feature-flags.service' ;
2022-05-31 10:00:47 +00:00
import { getTemplateVariables , intersectVariables } from '@/react/portainer/custom-templates/components/utils' ;
2023-02-14 08:19:41 +00:00
import { confirmWebEditorDiscard } from '@@/modals/confirm' ;
2023-04-04 00:44:42 +00:00
import { getFilePreview } from '@/react/portainer/gitops/gitops.service' ;
2023-04-20 21:39:55 +00:00
import { KUBE _TEMPLATE _NAME _VALIDATION _REGEX } from '@/constants' ;
2021-09-02 05:28:51 +00:00
class KubeEditCustomTemplateViewController {
/* @ngInject */
2023-02-14 08:19:41 +00:00
constructor ( $async , $state , Authentication , CustomTemplateService , FormValidator , Notifications , ResourceControlService ) {
Object . assign ( this , { $async , $state , Authentication , CustomTemplateService , FormValidator , Notifications , ResourceControlService } ) ;
2021-09-02 05:28:51 +00:00
2022-06-16 05:32:41 +00:00
this . isTemplateVariablesEnabled = isBE ;
2023-04-04 00:44:42 +00:00
this . formValues = {
Variables : [ ] ,
TLSSkipVerify : false ,
2023-10-22 09:19:19 +00:00
Title : '' ,
Description : '' ,
Note : '' ,
Logo : '' ,
2023-04-04 00:44:42 +00:00
} ;
2021-09-02 05:28:51 +00:00
this . state = {
formValidationError : '' ,
isEditorDirty : false ,
2022-05-31 10:00:47 +00:00
isTemplateValid : true ,
2023-04-04 00:44:42 +00:00
isEditorReadOnly : false ,
templateLoadFailed : false ,
templatePreviewFailed : false ,
templatePreviewError : '' ,
2021-09-02 05:28:51 +00:00
} ;
this . templates = [ ] ;
2023-10-22 09:19:19 +00:00
this . validationData = {
title : {
pattern : KUBE _TEMPLATE _NAME _VALIDATION _REGEX ,
error :
"This field must consist of lower-case alphanumeric characters, '.', '_' or '-', must start and end with an alphanumeric character and must be 63 characters or less (e.g. 'my-name', or 'abc-123')." ,
} ,
} ;
2021-09-02 05:28:51 +00:00
this . getTemplate = this . getTemplate . bind ( this ) ;
this . submitAction = this . submitAction . bind ( this ) ;
this . onChangeFileContent = this . onChangeFileContent . bind ( this ) ;
this . onBeforeUnload = this . onBeforeUnload . bind ( this ) ;
2022-05-31 10:00:47 +00:00
this . handleChange = this . handleChange . bind ( this ) ;
this . onVariablesChange = this . onVariablesChange . bind ( this ) ;
2023-04-04 00:44:42 +00:00
this . previewFileFromGitRepository = this . previewFileFromGitRepository . bind ( this ) ;
2023-10-22 09:19:19 +00:00
this . onChangePlatform = this . onChangePlatform . bind ( this ) ;
this . onChangeType = this . onChangeType . bind ( this ) ;
}
onChangePlatform ( value ) {
this . handleChange ( { Platform : value } ) ;
}
onChangeType ( value ) {
this . handleChange ( { Type : value } ) ;
2021-09-02 05:28:51 +00:00
}
getTemplate ( ) {
return this . $async ( async ( ) => {
try {
const { id } = this . $state . params ;
2023-04-04 00:44:42 +00:00
const template = await this . CustomTemplateService . customTemplate ( id ) ;
if ( template . GitConfig !== null ) {
this . state . isEditorReadOnly = true ;
}
try {
template . FileContent = await this . CustomTemplateService . customTemplateFile ( id , template . GitConfig !== null ) ;
} catch ( err ) {
this . state . templateLoadFailed = true ;
throw err ;
}
2022-05-31 10:00:47 +00:00
template . Variables = template . Variables || [ ] ;
2023-04-04 00:44:42 +00:00
this . formValues = { ... this . formValues , ... template } ;
2022-05-31 10:00:47 +00:00
2023-04-04 00:44:42 +00:00
this . parseTemplate ( template . FileContent ) ;
this . parseGitConfig ( template . GitConfig ) ;
2022-05-31 10:00:47 +00:00
2021-09-02 05:28:51 +00:00
this . oldFileContent = this . formValues . FileContent ;
this . formValues . ResourceControl = new ResourceControlViewModel ( template . ResourceControl ) ;
this . formValues . AccessControlData = new AccessControlFormData ( ) ;
} catch ( err ) {
this . Notifications . error ( 'Failure' , err , 'Unable to retrieve custom template data' ) ;
}
} ) ;
}
2022-05-31 10:00:47 +00:00
onVariablesChange ( values ) {
this . handleChange ( { Variables : values } ) ;
}
handleChange ( values ) {
return this . $async ( async ( ) => {
this . formValues = {
... this . formValues ,
... values ,
} ;
} ) ;
}
parseTemplate ( templateStr ) {
2022-06-16 05:32:41 +00:00
if ( ! this . isTemplateVariablesEnabled ) {
return ;
}
2022-05-31 10:00:47 +00:00
const variables = getTemplateVariables ( templateStr ) ;
const isValid = ! ! variables ;
this . state . isTemplateValid = isValid ;
if ( isValid ) {
this . onVariablesChange ( intersectVariables ( this . formValues . Variables , variables ) ) ;
}
}
2023-04-04 00:44:42 +00:00
parseGitConfig ( config ) {
if ( config === null ) {
return ;
}
let flatConfig = {
RepositoryURL : config . URL ,
RepositoryReferenceName : config . ReferenceName ,
ComposeFilePathInRepository : config . ConfigFilePath ,
RepositoryAuthentication : config . Authentication !== null ,
TLSSkipVerify : config . TLSSkipVerify ,
} ;
if ( config . Authentication ) {
flatConfig = {
... flatConfig ,
RepositoryUsername : config . Authentication . Username ,
RepositoryPassword : config . Authentication . Password ,
} ;
}
this . formValues = { ... this . formValues , ... flatConfig } ;
}
previewFileFromGitRepository ( ) {
this . state . templatePreviewFailed = false ;
this . state . templatePreviewError = '' ;
let creds = { } ;
if ( this . formValues . RepositoryAuthentication ) {
creds = {
username : this . formValues . RepositoryUsername ,
password : this . formValues . RepositoryPassword ,
} ;
}
const payload = {
repository : this . formValues . RepositoryURL ,
targetFile : this . formValues . ComposeFilePathInRepository ,
tlsSkipVerify : this . formValues . TLSSkipVerify ,
... creds ,
} ;
this . $async ( async ( ) => {
try {
this . formValues . FileContent = await getFilePreview ( payload ) ;
this . state . isEditorDirty = true ;
// check if the template contains mustache template symbol
this . parseTemplate ( this . formValues . FileContent ) ;
} catch ( err ) {
this . state . templatePreviewError = err . message ;
this . state . templatePreviewFailed = true ;
}
} ) ;
}
2021-09-02 05:28:51 +00:00
validateForm ( ) {
this . state . formValidationError = '' ;
if ( ! this . formValues . FileContent ) {
this . state . formValidationError = 'Template file content must not be empty' ;
return false ;
}
const title = this . formValues . Title ;
const id = this . $state . params . id ;
const isNotUnique = this . templates . some ( ( template ) => template . Title === title && template . Id != id ) ;
if ( isNotUnique ) {
this . state . formValidationError = ` A template with the name ${ title } already exists ` ;
return false ;
}
const isAdmin = this . Authentication . isAdmin ( ) ;
const accessControlData = this . formValues . AccessControlData ;
const error = this . FormValidator . validateAccessControl ( accessControlData , isAdmin ) ;
if ( error ) {
this . state . formValidationError = error ;
return false ;
}
return true ;
}
submitAction ( ) {
return this . $async ( async ( ) => {
if ( ! this . validateForm ( ) ) {
return ;
}
this . actionInProgress = true ;
try {
await this . CustomTemplateService . updateCustomTemplate ( this . formValues . Id , this . formValues ) ;
const userDetails = this . Authentication . getUserDetails ( ) ;
const userId = userDetails . ID ;
await this . ResourceControlService . applyResourceControl ( userId , this . formValues . AccessControlData , this . formValues . ResourceControl ) ;
2022-08-10 05:07:35 +00:00
this . Notifications . success ( 'Success' , 'Custom template successfully updated' ) ;
2021-09-02 05:28:51 +00:00
this . state . isEditorDirty = false ;
this . $state . go ( 'kubernetes.templates.custom' ) ;
} catch ( err ) {
this . Notifications . error ( 'Failure' , err , 'Unable to update custom template' ) ;
} finally {
this . actionInProgress = false ;
}
} ) ;
}
onChangeFileContent ( value ) {
if ( stripSpaces ( this . formValues . FileContent ) !== stripSpaces ( value ) ) {
this . formValues . FileContent = value ;
2022-05-31 10:00:47 +00:00
this . parseTemplate ( value ) ;
2021-09-02 05:28:51 +00:00
this . state . isEditorDirty = true ;
}
}
async $onInit ( ) {
this . $async ( async ( ) => {
this . getTemplate ( ) ;
try {
this . templates = await this . CustomTemplateService . customTemplates ( ) ;
} catch ( err ) {
this . Notifications . error ( 'Failure loading' , err , 'Failed loading custom templates' ) ;
}
window . addEventListener ( 'beforeunload' , this . onBeforeUnload ) ;
} ) ;
}
isEditorDirty ( ) {
return this . formValues . FileContent !== this . oldFileContent && this . state . isEditorDirty ;
}
uiCanExit ( ) {
if ( this . isEditorDirty ( ) ) {
2023-02-14 08:19:41 +00:00
return confirmWebEditorDiscard ( ) ;
2021-09-02 05:28:51 +00:00
}
}
onBeforeUnload ( event ) {
if ( this . formValues . FileContent !== this . oldFileContent && this . state . isEditorDirty ) {
event . preventDefault ( ) ;
event . returnValue = '' ;
return '' ;
}
}
$onDestroy ( ) {
window . removeEventListener ( 'beforeunload' , this . onBeforeUnload ) ;
}
}
export default KubeEditCustomTemplateViewController ;
function stripSpaces ( str = '' ) {
return str . replace ( /(\r\n|\n|\r)/gm , '' ) ;
}