Skip to content

Commit

Permalink
Merge pull request #158 from ditrit/feature/secrets
Browse files Browse the repository at this point in the history
Feature: secrets managment
  • Loading branch information
Zorin95670 authored Nov 4, 2024
2 parents 0708e65 + b418f65 commit d1e1d60
Show file tree
Hide file tree
Showing 28 changed files with 5,641 additions and 15,592 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ Administration application for leto-modelizer.

### Requirements

- node - v18.14
- npm - v8.19.3
* node - [v20.16.0](https://nodejs.org/en/blog/release/v20.16.0)
* npm - [v10.8.1](https://www.npmjs.com/package/npm/v/10.8.1)

This server is based on [Quasar](https://quasar.dev/).

Expand Down
10 changes: 10 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)

## [Unreleased]

### Added

* Setup AI settings page with:
* Table to display all secrets.
* Add action to create a secret.
* Add action to edit a secret.
* Add action to remove a secret.

## [1.0.0] - 2024/10/15

### Added
Expand Down
19,480 changes: 3,890 additions & 15,590 deletions package-lock.json

Large diffs are not rendered by default.

37 changes: 37 additions & 0 deletions src/components/card/SecretFiltersCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<template>
<q-card>
<q-card-section class="row justify-center items-center">
<span class="text-weight-medium q-mr-md">
{{ $t('SecretFiltersCard.text.title') }}
</span>
<q-input
outlined
dense
clearable
:model-value="secretKey"
:label="$t('SecretFiltersCard.text.byKey')"
:debounce="inputDebounceTime"
class="q-mr-sm"
data-cy="secret_filter_key"
@update:model-value="(value) => $emit('update:secretKey', value)"
>
<template #prepend>
<q-icon :name="$t('SecretFiltersCard.icon.byKey')" />
</template>
</q-input>
</q-card-section>
</q-card>
</template>

<script setup>
import { ref } from 'vue';
defineEmits(['update:secretKey']);
defineProps({
secretKey: {
type: String,
default: '',
},
});
const inputDebounceTime = ref(process.env.INPUT_DEBOUNCE_TIME);
</script>
106 changes: 106 additions & 0 deletions src/components/dialog/AddSecretDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<template>
<q-dialog
v-model="show"
data-cy="add-secret-dialog"
>
<q-card>
<q-card-section class="flex row justify-center">
<span class="text-h6">
{{ $t('AddSecretDialog.text.title') }}
</span>
</q-card-section>
<q-form @submit="onSubmit">
<q-card-section class="column flex-center">
<q-input
v-model="secretKey"
outlined
class="input-secret-key"
data-cy="secret_field_key"
:label="$t('AddSecretDialog.text.key')"
/>
<q-input
v-model="secretValue"
outlined
type="textarea"
class="q-mt-md input-secret-value"
data-cy="secret_field_value"
:label="$t('AddSecretDialog.text.value')"
/>
</q-card-section>
<q-card-actions align="center">
<q-btn
v-close-popup
:label="$t('AddSecretDialog.text.cancel')"
color="negative"
/>
<q-btn
:label="$t('AddSecretDialog.text.confirm')"
:loading="submitting"
type="submit"
color="positive"
data-cy="button_confirm"
>
<template #loading>
<q-spinner-hourglass />
</template>
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>

<script setup>
import { useDialog } from 'src/composables/Dialog';
import { ref } from 'vue';
import ReloadSecretsEvent from 'src/composables/events/ReloadSecretsEvent';
import * as SecretService from 'src/services/SecretService';
import { Notify } from 'quasar';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const submitting = ref(false);
const secretKey = ref('');
const secretValue = ref('');
const { show } = useDialog('add-secret', () => {
submitting.value = false;
secretKey.value = '';
secretValue.value = '';
});
/**
* Add secret, send event to reload all secrets and close dialog.
* @returns {Promise<void>} Promise with nothing on success.
*/
async function onSubmit() {
submitting.value = true;
return SecretService.add(secretKey.value, secretValue.value)
.then(() => {
Notify.create({
type: 'positive',
message: t('AddSecretDialog.text.notifySuccess'),
html: true,
});
ReloadSecretsEvent.next();
show.value = false;
})
.catch(() => {
Notify.create({
type: 'negative',
message: t('AddSecretDialog.text.notifyError'),
html: true,
});
}).finally(() => {
submitting.value = false;
});
}
</script>

<style lang="scss" scoped>
.input-secret-value, .input-secret-key {
min-width: 400px;
}
</style>
80 changes: 80 additions & 0 deletions src/components/dialog/RemoveSecretDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<template>
<q-dialog
v-model="show"
data-cy="remove-secret-dialog"
>
<q-card>
<q-card-section class="flex row justify-center">
<span class="text-h6">
{{ $t('RemoveSecretDialog.text.title', { key: secret.key }) }}
</span>
</q-card-section>
<q-form @submit="onSubmit">
<q-card-section class="column flex-center">
{{ $t('RemoveSecretDialog.text.content') }}
</q-card-section>
<q-card-actions align="center">
<q-btn
v-close-popup
:label="$t('RemoveSecretDialog.text.cancel')"
color="negative"
/>
<q-btn
:label="$t('RemoveSecretDialog.text.confirm')"
:loading="submitting"
type="submit"
color="positive"
data-cy="button_confirm"
>
<template #loading>
<q-spinner-hourglass />
</template>
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>

<script setup>
import { useDialog } from 'src/composables/Dialog';
import { ref } from 'vue';
import ReloadSecretsEvent from 'src/composables/events/ReloadSecretsEvent';
import * as SecretService from 'src/services/SecretService';
import { Notify } from 'quasar';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const submitting = ref(false);
const secret = ref(null);
const { show } = useDialog('remove-secret', (event) => {
submitting.value = false;
secret.value = event.secret;
});
/**
* Remove secret, send event to reload all secrets and close dialog.
* @returns {Promise<void>} Promise with nothing on success.
*/
async function onSubmit() {
submitting.value = true;
return SecretService.remove(secret.value.id)
.then(() => Notify.create({
type: 'positive',
message: t('RemoveSecretDialog.text.notifySuccess'),
html: true,
}))
.catch(() => Notify.create({
type: 'negative',
message: t('RemoveSecretDialog.text.notifyError'),
html: true,
}))
.finally(() => {
ReloadSecretsEvent.next();
submitting.value = false;
show.value = false;
});
}
</script>
94 changes: 94 additions & 0 deletions src/components/dialog/UpdateSecretDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<template>
<q-dialog
v-model="show"
data-cy="edit-secret-dialog"
>
<q-card>
<q-card-section class="flex row justify-center">
<span class="text-h6">
{{ $t('UpdateSecretDialog.text.title', { key: secret.key }) }}
</span>
</q-card-section>
<q-form @submit="onSubmit">
<q-card-section class="column flex-center">
<q-input
v-model="secretValue"
outlined
type="textarea"
class="q-mt-md input-secret-value"
data-cy="secret_field_key"
:label="$t('UpdateSecretDialog.text.value')"
/>
</q-card-section>
<q-card-actions align="center">
<q-btn
v-close-popup
:label="$t('UpdateSecretDialog.text.cancel')"
color="negative"
/>
<q-btn
:label="$t('UpdateSecretDialog.text.confirm')"
:loading="submitting"
type="submit"
color="positive"
data-cy="button_confirm"
>
<template #loading>
<q-spinner-hourglass />
</template>
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>

<script setup>
import { useDialog } from 'src/composables/Dialog';
import { ref } from 'vue';
import ReloadSecretsEvent from 'src/composables/events/ReloadSecretsEvent';
import * as SecretService from 'src/services/SecretService';
import { Notify } from 'quasar';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const submitting = ref(false);
const secret = ref(null);
const secretValue = ref('');
const { show } = useDialog('update-secret', (event) => {
submitting.value = false;
secretValue.value = '';
secret.value = event.secret;
});
/**
* Update secret, send event to reload all secrets and close dialog.
* @returns {Promise<void>} Promise with nothing on success.
*/
async function onSubmit() {
submitting.value = true;
return SecretService.update(secret.value.id, secret.value.key, secretValue.value)
.then(() => Notify.create({
type: 'positive',
message: t('UpdateSecretDialog.text.notifySuccess'),
html: true,
}))
.catch(() => Notify.create({
type: 'negative',
message: t('UpdateSecretDialog.text.notifyError'),
html: true,
})).finally(() => {
ReloadSecretsEvent.next();
show.value = false;
submitting.value = false;
});
}
</script>

<style lang="scss" scoped>
.input-secret-value, .input-secret-key {
min-width: 400px;
}
</style>
6 changes: 6 additions & 0 deletions src/components/drawer/ApplicationDrawer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ const menuList = ref([
url: '/libraries',
name: 'libraries',
},
{
icon: t('ApplicationDrawer.icon.ai'),
label: t('ApplicationDrawer.text.ai'),
url: '/ai',
name: 'ai',
},
]);
</script>

Expand Down
13 changes: 13 additions & 0 deletions src/components/tab-panel/ConfigurationsTabPanel.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<template>
<q-tab-panel
name="configurations"
data-cy="configurations_tab_panel"
>
<h6
class="q-ma-none q-mb-sm"
data-cy="configurations_title"
>
{{ $t('ConfigurationsTabPanel.text.title') }}
</h6>
</q-tab-panel>
</template>
Loading

0 comments on commit d1e1d60

Please sign in to comment.