1
0
Fork 0
mirror of https://github.com/mealie-recipes/mealie.git synced 2025-07-22 22:59:41 +02:00
mealie/frontend/pages/admin/manage/users/[id].vue

239 lines
6.4 KiB
Vue
Raw Permalink Normal View History

<template>
<v-container
v-if="user"
class="narrow-container"
>
<BasePageTitle>
<template #header>
<v-img
width="100%"
max-height="125"
max-width="125"
:src="require('~/static/svgs/manage-profile.svg')"
/>
</template>
<template #title>
{{ $t("user.admin-user-management") }}
</template>
2023-10-26 15:26:14 +02:00
{{ $t("user.changes-reflected-immediately") }}
</BasePageTitle>
<AppToolbar back />
<v-form
v-if="!userError"
ref="refNewUserForm"
@submit.prevent="handleSubmit"
>
<v-card
variant="outlined"
style="border-color: lightgrey;"
>
<v-card-text>
<div class="d-flex">
<p> {{ $t("user.user-id-with-value", { id: user.id }) }}</p>
</div>
2024-08-22 10:14:32 -05:00
<!-- This is disabled since we can't properly handle changing the user's group in most scenarios -->
<v-select
v-if="groups"
v-model="user.group"
2024-08-22 10:14:32 -05:00
disabled
:items="groups"
variant="solo-filled"
flat
item-title="name"
item-value="name"
:return-object="false"
:label="$t('group.user-group')"
:rules="[validators.required]"
2024-08-22 10:14:32 -05:00
/>
<v-select
v-if="households"
v-model="user.household"
:items="households"
variant="solo-filled"
flat
item-title="name"
2024-08-22 10:14:32 -05:00
item-value="name"
:return-object="false"
:label="$t('household.user-household')"
2024-08-22 10:14:32 -05:00
:rules="[validators.required]"
/>
<div class="d-flex py-2 pr-2">
<BaseButton
type="button"
:loading="generatingToken"
create
@click.prevent="handlePasswordReset"
>
{{ $t("user.generate-password-reset-link") }}
</BaseButton>
</div>
<div
v-if="resetUrl"
class="mb-2"
>
<v-card-text>
<p class="text-center pb-0">
{{ resetUrl }}
</p>
</v-card-text>
<v-card-actions
class="align-center pt-0"
style="gap: 4px"
>
<BaseButton
cancel
@click="resetUrl = ''"
>
{{ $t("general.close") }}
</BaseButton>
<v-spacer />
<BaseButton
v-if="user.email"
color="info"
class="mr-1"
@click="sendResetEmail"
>
2024-02-10 12:51:38 +00:00
<template #icon>
{{ $globals.icons.email }}
</template>
{{ $t("user.email") }}
</BaseButton>
<AppButtonCopy
:icon="false"
color="info"
:copy-text="resetUrl"
/>
</v-card-actions>
</div>
<AutoForm
v-model="user"
:items="userForm"
update-mode
:disabled-fields="disabledFields"
/>
</v-card-text>
</v-card>
<div class="d-flex pa-2">
<BaseButton
type="submit"
edit
class="ml-auto"
>
{{ $t("general.update") }}
</BaseButton>
</div>
</v-form>
</v-container>
</template>
<script lang="ts">
2024-02-10 12:51:38 +00:00
import { useAdminApi, useUserApi } from "~/composables/api";
import { useGroups } from "~/composables/use-groups";
import { useAdminHouseholds } from "~/composables/use-households";
import { alert } from "~/composables/use-toast";
import { useUserForm } from "~/composables/use-users";
import { validators } from "~/composables/use-validators";
import type { UserOut } from "~/lib/api/types/user";
export default defineNuxtComponent({
setup() {
definePageMeta({
layout: "admin",
});
const { userForm } = useUserForm();
const { groups } = useGroups();
const { useHouseholdsInGroup } = useAdminHouseholds();
const i18n = useI18n();
const route = useRoute();
const userId = route.params.id as string;
// ==============================================
// New User Form
const refNewUserForm = ref<VForm | null>(null);
const adminApi = useAdminApi();
const user = ref<UserOut | null>(null);
2024-08-22 10:14:32 -05:00
const households = useHouseholdsInGroup(computed(() => user.value?.groupId || ""));
const disabledFields = computed(() => {
feat: Login with OAuth via OpenID Connect (OIDC) (#3280) * initial oidc implementation * add dynamic scheme * e2e test setup * add caching * fix * try this * add libldap-2.5 to runtime dependencies (#2849) * New translations en-us.json (Norwegian) (#2851) * New Crowdin updates (#2855) * New translations en-us.json (Italian) * New translations en-us.json (Norwegian) * New translations en-us.json (Portuguese) * fix * remove cache * cache yarn deps * cache docker image * cleanup action * lint * fix tests * remove not needed variables * run code gen * fix tests * add docs * move code into custom scheme * remove unneeded type * fix oidc admin * add more tests * add better spacing on login page * create auth providers * clean up testing stuff * type fixes * add OIDC auth method to postgres enum * add option to bypass login screen and go directly to iDP * remove check so we can fallback to another auth method oauth fails * Add provider name to be shown at the login screen * add new properties to admin about api * fix spec * add a prompt to change auth method when changing password * Create new auth section. Add more info on auth methods * update docs * run ruff * update docs * format * docs gen * formatting * initialize logger in class * mypy type fixes * docs gen * add models to get proper fields in docs and fix serialization * validate id token before using it * only request a mealie token on initial callback * remove unused method * fix unit tests * docs gen * check for valid idToken before getting token * add iss to mealie token * check to see if we already have a mealie token before getting one * fix lock file * update authlib * update lock file * add remember me environment variable * add user group setting to allow only certain groups to log in --------- Co-authored-by: Carter Mintey <cmintey8@gmail.com> Co-authored-by: Carter <35710697+cmintey@users.noreply.github.com>
2024-03-10 13:51:36 -05:00
return user.value?.authMethod !== "Mealie" ? ["admin"] : [];
});
const userError = ref(false);
const resetUrl = ref<string | null>(null);
const generatingToken = ref(false);
onMounted(async () => {
const { data, error } = await adminApi.users.getOne(userId);
if (error?.response?.status === 404) {
alert.error(i18n.t("user.user-not-found"));
userError.value = true;
}
if (data) {
user.value = data;
}
});
async function handleSubmit() {
if (!refNewUserForm.value?.validate() || user.value === null) return;
const { response, data } = await adminApi.users.updateOne(user.value.id, user.value);
if (response?.status === 200 && data) {
user.value = data;
}
}
async function handlePasswordReset() {
if (user.value === null) return;
generatingToken.value = true;
const { response, data } = await adminApi.users.generatePasswordResetToken({ email: user.value.email });
if (response?.status === 201 && data) {
const token: string = data.token;
resetUrl.value = `${window.location.origin}/reset-password/?token=${token}`;
}
generatingToken.value = false;
}
2024-02-10 12:51:38 +00:00
const userApi = useUserApi();
async function sendResetEmail() {
if (!user.value?.email) return;
const { response } = await userApi.email.sendForgotPassword({ email: user.value.email });
if (response && response.status === 200) {
alert.success(i18n.t("profile.email-sent"));
}
else {
alert.error(i18n.t("profile.error-sending-email"));
2024-02-10 12:51:38 +00:00
}
}
return {
user,
disabledFields,
userError,
userForm,
refNewUserForm,
handleSubmit,
groups,
2024-08-22 10:14:32 -05:00
households,
validators,
handlePasswordReset,
resetUrl,
generatingToken,
2024-02-10 12:51:38 +00:00
sendResetEmail,
};
},
});
</script>