diff options
Diffstat (limited to 'pse-dashboard/src/views/SettingsView.vue')
-rw-r--r-- | pse-dashboard/src/views/SettingsView.vue | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/pse-dashboard/src/views/SettingsView.vue b/pse-dashboard/src/views/SettingsView.vue new file mode 100644 index 0000000..4e5e486 --- /dev/null +++ b/pse-dashboard/src/views/SettingsView.vue @@ -0,0 +1,347 @@ +<script setup> +import { + DashboardLayout, + FloatingLabelInput, + PasswordInput, + PasswordValidator, +} from '@/components' +import { ref } from 'vue'; +import { store } from '@/store.js' +import useGPodder from '@/api/gpodder.js' +import { + changePassword, + deleteAccount, + getEpisodeActions, + getTitles, + postEpisodeActions, + putSubscriptions, +} from '@/api/pse-squared.js' +import JSZip from 'jszip'; +import router from '@/router.js' +import { useLogger } from '@/logger.js' +import { saveAs } from 'file-saver'; + +/******************************************************************************/ +/* change password */ +/******************************************************************************/ + +const changePasswordOld = ref(null); +const changePasswordNew = ref(null); + +const logger = useLogger(); + +async function formChangePassword() { + if(!changePasswordNew.value.valid) { + logger.passwordRequirements(); + return; + } + + try { + await changePassword({ + password: changePasswordOld.value, + new_password: changePasswordNew.value.password + }); + + await store.login({ + username: store.username, + password: changePasswordNew.value.password + }, false); + + changePasswordOld.value = ""; + changePasswordNew.value = {password: "", valid: false}; + + logger.passwordChanged(); + } catch (err) {} +} + +/******************************************************************************/ +/* delete account */ +/******************************************************************************/ + +const deletePassword = ref(""); + +async function formDelete() { + + // FIXME: backend + if (store.password != deletePassword.value) { + logger.badRequestError(); + return; + } + + try { + await deleteAccount({ + password: deletePassword.value + }); + + logger.accountDeleted(); + store.logout(); + router.push("/login"); + } catch (err) {} +} + +/******************************************************************************/ +/* import from another GPodder instance */ +/******************************************************************************/ + +const gPodderInstance = ref(""); +const gPodderUsername = ref(""); +const gPodderPassword = ref(""); + +async function formImportGPodder() { + const instance = useGPodder({ + baseURL: gPodderInstance.value + }); + + await instance.login({ + username: gPodderUsername.value, + password: gPodderPassword.value + }); + + // download titles from instance and upload to pse-squared + try { + const response = await instance.getTitles(); + const subscriptions = response.data.map(sub => sub.url); + + await putSubscriptions(subscriptions); + } catch (err) { + console.error(err); + return; + } + + // download episodes from instance and upload to pse-squared + try { + const response = await instance.getEpisodeActions(); + const episodes = response.data.actions; + + await postEpisodeActions(episodes); + } catch (err) { + console.error(err); + return; + } + + logger.gpodderImport(); +} + + +/******************************************************************************/ +/* import and export data */ +/******************************************************************************/ + +async function importData(e) { + const file = e.target.files[0]; + + const zip = await JSZip.loadAsync(file); + + // read and upload subscriptions + try { + const subscriptionsText = await zip.files["subscriptions.json"].async("string"); + const subscriptions = JSON.parse(subscriptionsText).map(sub => sub.url); + console.log({subscriptions}); + + await putSubscriptions(subscriptions); + } catch(err) { + console.error(err); + } + + // read and upload episodeActions + try { + const episodeActionsText = await zip.files["episodeActions.json"].async("string"); + const episodeActions = JSON.parse(episodeActionsText).actions; + + await postEpisodeActions(episodeActions); + } catch(err) { + console.error(err); + } +} + +async function exportData() { + const zip = new JSZip(); + + // load subscriptions + try { + const subscriptionResponse = await getTitles(); + zip.file("subscriptions.json", JSON.stringify(subscriptionResponse.data)); + } catch(err) { + console.error(err); + } + + // load episodeActions + try { + const episodeActionsResponse = await getEpisodeActions(); + zip.file("episode-actions.json", JSON.stringify(episodeActionsResponse.data)); + } catch(err) { + console.error(err); + } + + // generate and save zip + zip.generateAsync({type:"blob"}).then(function(content) { + saveAs(content, "pse-export.zip"); + }); +} + +</script> +<template> + <DashboardLayout> + <!-- Überschrift --> + <h1 class="h1 mb-4"> + {{ $t("message.settings") }} + </h1> + + <!-- Passwort ändern Sektion --> + <form + class="mb-4" + @submit.prevent="formChangePassword" + > + <h2>{{ $t("message.changePassword") }}</h2> + <!-- Altes Passwort --> + <PasswordInput + v-model="changePasswordOld" + :label="$t('message.oldPassword')" + /> + + <!-- Neues Passwort --> + <PasswordValidator v-model="changePasswordNew" /> + + <button + type="submit" + class="btn btn-primary" + > + {{ $t("message.changePassword") }} + </button> + </form> + + <!-- Gpodder-Import Sektion --> + <form + class="mb-4" + @submit.prevent="formImportGPodder" + > + <h2>Gpodder-{{ $t("message.import") }}</h2> + + <!-- Gpodder Instanz --> + <FloatingLabelInput + v-model="gPodderInstance" + :label="$t('message.instance')" + /> + + <!-- Nutzername --> + <FloatingLabelInput + v-model="gPodderUsername" + :label="$t('form.username')" + /> + + <!-- Passwort --> + <PasswordInput + v-model="gPodderPassword" + :label="$t('form.password')" + /> + + <button + type="submit" + class="btn btn-primary" + > + {{ $t("message.importData") }} + </button> + </form> + + <!-- Personenbezogene Daten im-/exportieren --> + <div class="mb-4"> + <h2>{{ $t("message.personalData") }}</h2> + <label + for="file" + class="btn btn-success" + > + {{ $t("message.importData") }} + </label> + <input + id="file" + type="file" + accept=".zip,application/zip" + hidden + @change="importData" + > + <button + class="btn btn-warning mx-2" + @click="exportData" + > + {{ $t("message.exportData") }} + </button> + </div> + + <!-- Account löschen --> + <div class="mb-4"> + <h2>{{ $t("message.deleteAccount") }}</h2> + + <button + class="btn btn-danger" + data-bs-toggle="modal" + data-bs-target="#delete-user" + > + {{ $t("message.deleteAccount") }} + </button> + </div> + </DashboardLayout> + + <!-- Modal for confirming deletion of user --> + <div + id="delete-user" + class="modal" + tabindex="-1" + > + <div class="modal-dialog modal-dialog-centered"> + <div class="modal-content"> + <!-- Title of Modal --> + <div class="modal-header"> + <h5 class="modal-title"> + {{ $t('message.deleteAccount') }} + </h5> + <button + type="button" + class="btn-close" + data-bs-dismiss="modal" + aria-label="Close" + /> + </div> + + <!-- list podcasts to unsubscribe from --> + <div class="modal-body"> + <div + class="alert alert-warning d-flex align-items-center" + role="alert" + > + <i class="fs-2 me-3 fa fa-warning" /> + <div> + {{ $t('message.deleteAccountWarning', {username: store.username}) }} + </div> + </div> + + <PasswordInput + v-model="deletePassword" + :label="$t('form.password')" + /> + </div> + + <!-- buttons to dismiss or finaly unsubscribe --> + <div class="modal-footer"> + <button + type="button" + class="btn btn-secondary" + data-bs-dismiss="modal" + > + {{ $t('message.close') }} + </button> + <button + type="button" + data-bs-dismiss="modal" + class="btn btn-danger" + @click="formDelete" + > + {{ $t('message.deleteAccount') }} + </button> + </div> + </div> + </div> + </div> +</template> +<style scoped> +</style> + |