Skip to content
Snippets Groups Projects

Resolve "Support private Git Repositories"

Merged Daniel Göbel requested to merge feature/61-support-private-git-repositories into development
Files
4
 
<script setup lang="ts">
 
import BootstrapModal from "@/components/modals/BootstrapModal.vue";
 
import type { WorkflowCredentialsIn, WorkflowOut } from "@/client/workflow";
 
import { WorkflowCredentialsService } from "@/client/workflow";
 
import { onMounted, ref, reactive } from "vue";
 
import { Modal, Toast } from "bootstrap";
 
import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 
import DeleteModal from "@/components/modals/DeleteModal.vue";
 
import { GitRepository } from "@/utils/GitRepository";
 
 
// Constants
 
// =============================================================================
 
const randomIDSuffix = Math.random().toString(16).substring(2, 8);
 
 
// Bootstrap Elements
 
// =============================================================================
 
let updateCredentialsModal: Modal | null = null;
 
let successToast: Toast | null = null;
 
 
// Form Elements
 
// =============================================================================
 
const credentialsUpdateForm = ref<HTMLFormElement | undefined>(undefined);
 
const credentialsInputElement = ref<HTMLInputElement | undefined>(undefined);
 
 
// Props
 
// =============================================================================
 
const props = defineProps<{
 
modalID: string;
 
workflow: WorkflowOut;
 
}>();
 
 
// Reactive State
 
// =============================================================================
 
const credentials = reactive<WorkflowCredentialsIn>({
 
token: "",
 
username: undefined,
 
});
 
 
const formState = reactive<{
 
loading: boolean;
 
validated: boolean;
 
updateCredentials: boolean;
 
error: boolean;
 
}>({
 
loading: false,
 
validated: false,
 
updateCredentials: true,
 
error: false,
 
});
 
 
// Events
 
// =============================================================================
 
const emit = defineEmits<{
 
(
 
e: "credentials-updated",
 
workflow: WorkflowOut,
 
credentialsDeleted: boolean,
 
): void;
 
}>();
 
 
// Functions
 
// =============================================================================
 
function resetForm() {
 
credentials.token = "";
 
}
 
 
function modalClosed() {
 
formState.validated = false;
 
credentialsInputElement.value?.setCustomValidity("");
 
formState.error = false;
 
resetForm();
 
}
 
 
function updateCredentials() {
 
formState.validated = true;
 
formState.error = false;
 
credentialsInputElement.value?.setCustomValidity("");
 
if (credentialsUpdateForm.value?.checkValidity()) {
 
const repo = GitRepository.buildRepository(
 
props.workflow.repository_url,
 
props.workflow.versions[0].git_commit_hash,
 
credentials.token,
 
);
 
repo.checkFileExist("main.nf").then((result: boolean) => {
 
if (result) {
 
WorkflowCredentialsService.workflowCredentialsUpdateWorkflowCredentials(
 
props.workflow.workflow_id,
 
credentials,
 
)
 
.then(() => {
 
formState.updateCredentials = true;
 
emit("credentials-updated", props.workflow, false);
 
successToast?.show();
 
updateCredentialsModal?.hide();
 
})
 
.catch((error) => {
 
console.error(error);
 
})
 
.finally(() => {
 
formState.loading = false;
 
});
 
} else {
 
formState.error = true;
 
credentialsInputElement.value?.setCustomValidity(
 
"Can't access repository.",
 
);
 
formState.loading = false;
 
}
 
});
 
formState.loading = true;
 
}
 
}
 
 
function deleteCredentials() {
 
formState.loading = true;
 
WorkflowCredentialsService.workflowCredentialsDeleteWorkflowCredentials(
 
props.workflow.workflow_id,
 
)
 
.then(() => {
 
formState.updateCredentials = false;
 
emit("credentials-updated", props.workflow, true);
 
successToast?.show();
 
updateCredentialsModal?.hide();
 
})
 
.catch((error) => {
 
console.error(error);
 
})
 
.finally(() => {
 
formState.loading = false;
 
});
 
}
 
 
// Lifecycle Events
 
// =============================================================================
 
onMounted(() => {
 
updateCredentialsModal = new Modal("#" + props.modalID);
 
successToast = new Toast("#successToast-" + randomIDSuffix);
 
});
 
</script>
 
 
<template>
 
<DeleteModal
 
v-if="props.workflow.private"
 
:modalID="'delete-credentials-modal' + randomIDSuffix"
 
:object-name-delete="'credentials for workflow ' + props.workflow.name"
 
:back-modal-id="modalID"
 
@confirm-delete="deleteCredentials()"
 
/>
 
<div class="toast-container position-fixed top-toast end-0 p-3">
 
<div
 
role="alert"
 
aria-live="assertive"
 
aria-atomic="true"
 
class="toast text-bg-success align-items-center border-0"
 
data-bs-autohide="true"
 
:id="'successToast-' + randomIDSuffix"
 
>
 
<div class="d-flex">
 
<div v-if="formState.updateCredentials" class="toast-body">
 
Successfully updated credentials
 
</div>
 
<div v-else class="toast-body">Successfully deleted credentials</div>
 
<button
 
type="button"
 
class="btn-close btn-close-white me-2 m-auto"
 
data-bs-dismiss="toast"
 
aria-label="Close"
 
></button>
 
</div>
 
</div>
 
</div>
 
<bootstrap-modal
 
:modalID="modalID"
 
:static-backdrop="true"
 
modal-label="Update Workflow Version Icon Modal"
 
v-on="{ 'hidden.bs.modal': modalClosed }"
 
>
 
<template v-slot:header>
 
Update git repository credentials for Workflow
 
<span class="fw-bold">{{ props.workflow.name }}</span>
 
</template>
 
<template v-slot:extra-button v-if="props.workflow.private">
 
<button
 
class="btn delete-icon"
 
data-bs-toggle="modal"
 
:data-bs-target="'#delete-credentials-modal' + randomIDSuffix"
 
>
 
<font-awesome-icon icon="fa-solid fa-trash" />
 
</button>
 
</template>
 
<template v-slot:body>
 
<form
 
ref="credentialsUpdateForm"
 
id="credentialsUpdateForm"
 
:class="{ 'was-validated': formState.validated }"
 
>
 
<label for="workflowCredentialsInput" class="form-label"
 
>New Token</label
 
>
 
<div class="input-group">
 
<div class="input-group-text">
 
<font-awesome-icon icon="fa-solid fa-key" />
 
</div>
 
<input
 
type="password"
 
ref="credentialsInputElement"
 
class="form-control"
 
id="workflowCredentialsInput"
 
aria-describedby="iconHelp"
 
required
 
v-model="credentials.token"
 
/>
 
</div>
 
<div v-if="formState.error" class="text-danger">
 
Can't access the repository.
 
</div>
 
<div class="card card-body mt-3">
 
<h5>GitHub</h5>
 
<p>
 
For private GitHub repositories, CloWM needs a Personal Access Token
 
(classic) with the scope <code>repo</code>.<br />
 
Read this
 
<a
 
target="_blank"
 
href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic"
 
>Tutorial</a
 
>
 
on how to create such a token.
 
</p>
 
<h5>GitLab</h5>
 
<p>
 
For private GitLab repositories, CloWM needs a Project Access Token
 
with the <code>read_api</code> scope and at least
 
<code>Reporter</code> role.<br />
 
Read this
 
<a
 
target="_blank"
 
href="https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html#create-a-project-access-token"
 
>Tutorial</a
 
>
 
on how to create such a token.
 
</p>
 
<p>
 
Select a distant expiration date for both providers to ensure that
 
there won't be any problems in the short future.
 
</p>
 
</div>
 
</form>
 
</template>
 
<template v-slot:footer>
 
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
 
Close
 
</button>
 
<button
 
type="submit"
 
form="credentialsUpdateForm"
 
class="btn btn-primary"
 
:disabled="formState.loading"
 
@click.prevent="updateCredentials"
 
>
 
<span
 
v-if="formState.loading"
 
class="spinner-border spinner-border-sm"
 
role="status"
 
aria-hidden="true"
 
></span>
 
Save
 
</button>
 
</template>
 
</bootstrap-modal>
 
</template>
 
 
<style scoped>
 
img {
 
max-height: 64px;
 
max-width: 64px;
 
}
 
 
.delete-icon {
 
color: var(--bs-secondary) !important;
 
}
 
 
.delete-icon:hover {
 
color: var(--bs-danger) !important;
 
}
 
</style>
Loading