mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 23:28:25 +00:00
added the ability to change user name
This commit is contained in:
@@ -3,10 +3,10 @@ import { Types } from "mongoose";
|
|||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
import bcrypt from "bcrypt";
|
import bcrypt from "bcrypt";
|
||||||
import {
|
import {
|
||||||
MembershipOrg,
|
|
||||||
User,
|
|
||||||
APIKeyData,
|
APIKeyData,
|
||||||
TokenVersion
|
MembershipOrg,
|
||||||
|
TokenVersion,
|
||||||
|
User
|
||||||
} from "../../models";
|
} from "../../models";
|
||||||
import { getSaltRounds } from "../../config";
|
import { getSaltRounds } from "../../config";
|
||||||
|
|
||||||
@@ -80,6 +80,26 @@ export const updateMyMfaEnabled = async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the current user's name [firstName, lastName].
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const updateName = async (req: Request, res: Response) => {
|
||||||
|
const { firstName, lastName }: { firstName: string; lastName: string; } = req.body;
|
||||||
|
req.user.firstName = firstName;
|
||||||
|
req.user.lastName = lastName || "";
|
||||||
|
|
||||||
|
await req.user.save();
|
||||||
|
|
||||||
|
const user = req.user;
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
user,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return organizations that the current user is part of.
|
* Return organizations that the current user is part of.
|
||||||
* @param req
|
* @param req
|
||||||
|
|||||||
@@ -29,6 +29,16 @@ router.patch(
|
|||||||
usersController.updateMyMfaEnabled
|
usersController.updateMyMfaEnabled
|
||||||
);
|
);
|
||||||
|
|
||||||
|
router.patch(
|
||||||
|
"/me/name",
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY],
|
||||||
|
}),
|
||||||
|
body("firstName").exists(),
|
||||||
|
validateRequest,
|
||||||
|
usersController.updateName
|
||||||
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/me/organizations",
|
"/me/organizations",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
CreateAPIKeyRes,
|
CreateAPIKeyRes,
|
||||||
DeletOrgMembershipDTO,
|
DeletOrgMembershipDTO,
|
||||||
OrgUser,
|
OrgUser,
|
||||||
|
RenameUserDTO,
|
||||||
TokenVersion,
|
TokenVersion,
|
||||||
UpdateOrgUserRoleDTO,
|
UpdateOrgUserRoleDTO,
|
||||||
User} from "./types";
|
User} from "./types";
|
||||||
@@ -45,6 +46,18 @@ const fetchUserAction = async (action: string) => {
|
|||||||
return data.userAction;
|
return data.userAction;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useRenameUser = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation<{}, {}, RenameUserDTO>({
|
||||||
|
mutationFn: ({ newName }) =>
|
||||||
|
apiRequest.patch("/api/v2/users/me/name", { firstName: newName?.split(" ")[0], lastName: newName?.split(" ").slice(1).join(" ") }),
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries(userKeys.getUser);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const useGetUserAction = (action: string) =>
|
export const useGetUserAction = (action: string) =>
|
||||||
useQuery({
|
useQuery({
|
||||||
queryKey: userKeys.userAction,
|
queryKey: userKeys.userAction,
|
||||||
|
|||||||
@@ -67,6 +67,10 @@ export type CreateAPIKeyRes = {
|
|||||||
apiKeyData: APIKeyData;
|
apiKeyData: APIKeyData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type RenameUserDTO = {
|
||||||
|
newName: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type APIKeyData = {
|
export type APIKeyData = {
|
||||||
_id: string;
|
_id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ export const ChangeLanguageSection = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-4 bg-mineshaft-900 mb-6 max-w-screen-lg rounded-lg border border-mineshaft-600">
|
<div className="p-4 bg-mineshaft-900 mb-6 rounded-lg border border-mineshaft-600">
|
||||||
<p className="text-xl font-semibold text-mineshaft-100 mb-8">
|
<p className="text-xl font-semibold text-mineshaft-100 mb-8">
|
||||||
{t("settings.personal.change-language")}
|
{t("settings.personal.change-language")}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
+2
@@ -3,10 +3,12 @@ import { ChangePasswordSection } from "../ChangePasswordSection";
|
|||||||
import { EmergencyKitSection } from "../EmergencyKitSection";
|
import { EmergencyKitSection } from "../EmergencyKitSection";
|
||||||
import { SecuritySection } from "../SecuritySection";
|
import { SecuritySection } from "../SecuritySection";
|
||||||
import { SessionsSection } from "../SessionsSection";
|
import { SessionsSection } from "../SessionsSection";
|
||||||
|
import { UserNameSection } from "../UserNameSection";
|
||||||
|
|
||||||
export const PersonalSecurityTab = () => {
|
export const PersonalSecurityTab = () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<UserNameSection />
|
||||||
<ChangeLanguageSection />
|
<ChangeLanguageSection />
|
||||||
<SecuritySection />
|
<SecuritySection />
|
||||||
<SessionsSection />
|
<SessionsSection />
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ const tabs = [
|
|||||||
export const PersonalTabGroup = () => {
|
export const PersonalTabGroup = () => {
|
||||||
return (
|
return (
|
||||||
<Tab.Group>
|
<Tab.Group>
|
||||||
<Tab.List className="mb-6 border-b-2 border-mineshaft-800 w-full">
|
<Tab.List className="mb-4 border-b-2 border-mineshaft-800 w-full">
|
||||||
{tabs.map((tab) => (
|
{tabs.map((tab) => (
|
||||||
<Tab as={Fragment} key={tab.key}>
|
<Tab as={Fragment} key={tab.key}>
|
||||||
{({ selected }) => (
|
{({ selected }) => (
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import { useEffect } from "react";
|
||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import { yupResolver } from "@hookform/resolvers/yup";
|
||||||
|
import * as yup from "yup";
|
||||||
|
|
||||||
|
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||||
|
import { Button, FormControl, Input } from "@app/components/v2";
|
||||||
|
import { useUser } from "@app/context";
|
||||||
|
import { useRenameUser } from "@app/hooks/api/users/queries";
|
||||||
|
|
||||||
|
const formSchema = yup.object({
|
||||||
|
name: yup.string().required().label("User Name"),
|
||||||
|
});
|
||||||
|
|
||||||
|
type FormData = yup.InferType<typeof formSchema>;
|
||||||
|
|
||||||
|
export const UserNameSection = (): JSX.Element => {
|
||||||
|
const { user } = useUser();
|
||||||
|
const { createNotification } = useNotificationContext();
|
||||||
|
const {
|
||||||
|
handleSubmit,
|
||||||
|
control,
|
||||||
|
reset
|
||||||
|
} = useForm<FormData>({ resolver: yupResolver(formSchema) });
|
||||||
|
const { mutateAsync, isLoading } = useRenameUser();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (user) {
|
||||||
|
reset({ name: `${user?.firstName}${user?.lastName && " "}${user?.lastName}` });
|
||||||
|
}
|
||||||
|
}, [user]);
|
||||||
|
|
||||||
|
const onFormSubmit = async ({ name }: FormData) => {
|
||||||
|
try {
|
||||||
|
if (!user?._id) return;
|
||||||
|
if (name === "") return;
|
||||||
|
|
||||||
|
await mutateAsync({ newName: name});
|
||||||
|
createNotification({
|
||||||
|
text: "Successfully renamed user",
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
createNotification({
|
||||||
|
text: "Failed to rename user",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
onSubmit={handleSubmit(onFormSubmit)}
|
||||||
|
className="p-4 bg-mineshaft-900 mb-6 rounded-lg border border-mineshaft-600"
|
||||||
|
>
|
||||||
|
<p className="text-xl font-semibold text-mineshaft-100 mb-4">
|
||||||
|
User name
|
||||||
|
</p>
|
||||||
|
<div className="mb-2 max-w-md">
|
||||||
|
<Controller
|
||||||
|
defaultValue=""
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
||||||
|
<Input placeholder={`${user?.firstName} ${user?.lastName}`} {...field} />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
control={control}
|
||||||
|
name="name"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
isLoading={isLoading}
|
||||||
|
colorSchema="primary"
|
||||||
|
variant="outline_bg"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { UserNameSection } from "./UserNameSection";
|
||||||
@@ -26,15 +26,13 @@ export const ProjectSettingsPage = () => {
|
|||||||
<p className="text-3xl font-semibold text-gray-200">{t("settings.project.title")}</p>
|
<p className="text-3xl font-semibold text-gray-200">{t("settings.project.title")}</p>
|
||||||
</div>
|
</div>
|
||||||
<Tab.Group>
|
<Tab.Group>
|
||||||
<Tab.List className="mb-6 w-full border-b-2 border-mineshaft-800">
|
<Tab.List className="mb-4 w-full border-b-2 border-mineshaft-800">
|
||||||
{tabs.map((tab) => (
|
{tabs.map((tab) => (
|
||||||
<Tab as={Fragment} key={tab.key}>
|
<Tab as={Fragment} key={tab.key}>
|
||||||
{({ selected }) => (
|
{({ selected }) => (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`w-30 p-4 font-semibold outline-none ${
|
className={`w-30 py-2 mx-2 mr-4 font-medium text-sm outline-none ${selected ? "border-b border-white text-white" : "text-mineshaft-400"}`}
|
||||||
selected ? "border-b-2 border-white text-white" : "text-mineshaft-400"
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{tab.name}
|
{tab.name}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
+1
-3
@@ -1,6 +1,5 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { yupResolver } from "@hookform/resolvers/yup";
|
import { yupResolver } from "@hookform/resolvers/yup";
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
|
|
||||||
@@ -27,7 +26,6 @@ export const ProjectNameChangeSection = () => {
|
|||||||
control,
|
control,
|
||||||
reset
|
reset
|
||||||
} = useForm<FormData>({ resolver: yupResolver(formSchema) });
|
} = useForm<FormData>({ resolver: yupResolver(formSchema) });
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentWorkspace) {
|
if (currentWorkspace) {
|
||||||
@@ -67,7 +65,7 @@ export const ProjectNameChangeSection = () => {
|
|||||||
className="p-4 bg-mineshaft-900 mb-6 rounded-lg border border-mineshaft-600"
|
className="p-4 bg-mineshaft-900 mb-6 rounded-lg border border-mineshaft-600"
|
||||||
>
|
>
|
||||||
<h2 className="text-xl font-semibold flex-1 text-mineshaft-100 mb-8">
|
<h2 className="text-xl font-semibold flex-1 text-mineshaft-100 mb-8">
|
||||||
{t("common.display-name")}
|
Project Name
|
||||||
</h2>
|
</h2>
|
||||||
<div className="max-w-md">
|
<div className="max-w-md">
|
||||||
<Controller
|
<Controller
|
||||||
|
|||||||
Reference in New Issue
Block a user