dd05ee3a65
Co-authored-by: Eric Nemchik <eric@nemchik.com> Co-authored-by: Pavel Djundik <xPaw@users.noreply.github.com>
196 lines
5.0 KiB
Vue
196 lines
5.0 KiB
Vue
<template>
|
|
<div>
|
|
<div
|
|
v-if="
|
|
!store.state.serverConfiguration?.public &&
|
|
!store.state.serverConfiguration?.ldapEnabled
|
|
"
|
|
id="change-password"
|
|
role="group"
|
|
aria-labelledby="label-change-password"
|
|
>
|
|
<h2 id="label-change-password">Change password</h2>
|
|
<div class="password-container">
|
|
<label for="current-password" class="sr-only"> Enter current password </label>
|
|
<RevealPassword v-slot:default="slotProps">
|
|
<input
|
|
id="current-password"
|
|
autocomplete="current-password"
|
|
:type="slotProps.isVisible ? 'text' : 'password'"
|
|
name="old_password"
|
|
class="input"
|
|
placeholder="Enter current password"
|
|
/>
|
|
</RevealPassword>
|
|
</div>
|
|
<div class="password-container">
|
|
<label for="new-password" class="sr-only"> Enter desired new password </label>
|
|
<RevealPassword v-slot:default="slotProps">
|
|
<input
|
|
id="new-password"
|
|
:type="slotProps.isVisible ? 'text' : 'password'"
|
|
name="new_password"
|
|
autocomplete="new-password"
|
|
class="input"
|
|
placeholder="Enter desired new password"
|
|
/>
|
|
</RevealPassword>
|
|
</div>
|
|
<div class="password-container">
|
|
<label for="new-password-verify" class="sr-only"> Repeat new password </label>
|
|
<RevealPassword v-slot:default="slotProps">
|
|
<input
|
|
id="new-password-verify"
|
|
:type="slotProps.isVisible ? 'text' : 'password'"
|
|
name="verify_password"
|
|
autocomplete="new-password"
|
|
class="input"
|
|
placeholder="Repeat new password"
|
|
/>
|
|
</RevealPassword>
|
|
</div>
|
|
<div
|
|
v-if="passwordChangeStatus && passwordChangeStatus.success"
|
|
class="feedback success"
|
|
>
|
|
Successfully updated your password
|
|
</div>
|
|
<div
|
|
v-else-if="passwordChangeStatus && passwordChangeStatus.error"
|
|
class="feedback error"
|
|
>
|
|
{{ passwordErrors[passwordChangeStatus.error] }}
|
|
</div>
|
|
<div>
|
|
<button type="submit" class="btn" @click.prevent="changePassword">
|
|
Change password
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="!store.state.serverConfiguration?.public" class="session-list" role="group">
|
|
<h2>Sessions</h2>
|
|
|
|
<h3>Current session</h3>
|
|
<Session v-if="currentSession" :session="currentSession" />
|
|
|
|
<template v-if="activeSessions.length > 0">
|
|
<h3>Active sessions</h3>
|
|
<Session
|
|
v-for="session in activeSessions"
|
|
:key="session.token"
|
|
:session="session"
|
|
/>
|
|
</template>
|
|
|
|
<h3>Other sessions</h3>
|
|
<p v-if="store.state.sessions.length === 0">Loading…</p>
|
|
<p v-else-if="otherSessions.length === 0">
|
|
<em>You are not currently logged in to any other device.</em>
|
|
</p>
|
|
<Session
|
|
v-for="session in otherSessions"
|
|
v-else
|
|
:key="session.token"
|
|
:session="session"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import socket from "../../js/socket";
|
|
import RevealPassword from "../RevealPassword.vue";
|
|
import Session from "../Session.vue";
|
|
import {computed, defineComponent, onMounted, PropType, ref} from "vue";
|
|
import {useStore} from "../../js/store";
|
|
|
|
export default defineComponent({
|
|
name: "UserSettings",
|
|
components: {
|
|
RevealPassword,
|
|
Session,
|
|
},
|
|
props: {
|
|
settingsForm: {
|
|
type: Object as PropType<HTMLFormElement>,
|
|
required: true,
|
|
},
|
|
},
|
|
setup(props) {
|
|
const store = useStore();
|
|
|
|
const passwordErrors = {
|
|
missing_fields: "Please enter a new password",
|
|
password_mismatch: "Both new password fields must match",
|
|
password_incorrect: "The current password field does not match your account password",
|
|
update_failed: "Failed to update your password",
|
|
};
|
|
|
|
const passwordChangeStatus = ref<{
|
|
success: boolean;
|
|
error: keyof typeof passwordErrors;
|
|
}>();
|
|
|
|
const currentSession = computed(() => {
|
|
return store.state.sessions.find((item) => item.current);
|
|
});
|
|
|
|
const activeSessions = computed(() => {
|
|
return store.state.sessions.filter((item) => !item.current && item.active > 0);
|
|
});
|
|
|
|
const otherSessions = computed(() => {
|
|
return store.state.sessions.filter((item) => !item.current && !item.active);
|
|
});
|
|
|
|
onMounted(() => {
|
|
socket.emit("sessions:get");
|
|
});
|
|
|
|
const changePassword = () => {
|
|
const allFields = new FormData(props.settingsForm);
|
|
|
|
const data = {
|
|
old_password: allFields.get("old_password"),
|
|
new_password: allFields.get("new_password"),
|
|
verify_password: allFields.get("verify_password"),
|
|
};
|
|
|
|
if (!data.old_password || !data.new_password || !data.verify_password) {
|
|
passwordChangeStatus.value = {
|
|
success: false,
|
|
error: "missing_fields",
|
|
};
|
|
return;
|
|
}
|
|
|
|
if (data.new_password !== data.verify_password) {
|
|
passwordChangeStatus.value = {
|
|
success: false,
|
|
error: "password_mismatch",
|
|
};
|
|
return;
|
|
}
|
|
|
|
socket.once("change-password", (response) => {
|
|
// TODO type
|
|
passwordChangeStatus.value = response as any;
|
|
});
|
|
|
|
socket.emit("change-password", data);
|
|
};
|
|
|
|
return {
|
|
store,
|
|
passwordChangeStatus,
|
|
passwordErrors,
|
|
currentSession,
|
|
activeSessions,
|
|
otherSessions,
|
|
changePassword,
|
|
};
|
|
},
|
|
});
|
|
</script>
|