mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2025-11-03 17:13:33 +00:00
Log in as user support
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { IconArrowsCross, IconBolt, IconBoltOff, IconDisc } from "@tabler/icons-react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { HasPermission } from "src/components";
|
||||
import { useHostReport } from "src/hooks";
|
||||
import { T } from "src/locale";
|
||||
|
||||
@@ -15,100 +16,111 @@ const Dashboard = () => {
|
||||
<div className="row row-deck row-cards">
|
||||
<div className="col-12 my-4">
|
||||
<div className="row row-cards">
|
||||
<div className="col-sm-6 col-lg-3">
|
||||
<a
|
||||
href="/nginx/proxy"
|
||||
className="card card-sm card-link card-link-pop"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
navigate("/nginx/proxy");
|
||||
}}
|
||||
>
|
||||
<div className="card-body">
|
||||
<div className="row align-items-center">
|
||||
<div className="col-auto">
|
||||
<span className="bg-green text-white avatar">
|
||||
<IconBolt />
|
||||
</span>
|
||||
</div>
|
||||
<div className="col">
|
||||
<div className="font-weight-medium">
|
||||
<T id="proxy-hosts.count" data={{ count: hostReport?.proxy }} />
|
||||
<HasPermission permission="proxyHosts" type="view" hideError>
|
||||
<div className="col-sm-6 col-lg-3">
|
||||
<a
|
||||
href="/nginx/proxy"
|
||||
className="card card-sm card-link card-link-pop"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
navigate("/nginx/proxy");
|
||||
}}
|
||||
>
|
||||
<div className="card-body">
|
||||
<div className="row align-items-center">
|
||||
<div className="col-auto">
|
||||
<span className="bg-green text-white avatar">
|
||||
<IconBolt />
|
||||
</span>
|
||||
</div>
|
||||
<div className="col">
|
||||
<div className="font-weight-medium">
|
||||
<T id="proxy-hosts.count" data={{ count: hostReport?.proxy }} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div className="col-sm-6 col-lg-3">
|
||||
<a
|
||||
href="/nginx/redirection"
|
||||
className="card card-sm card-link card-link-pop"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
navigate("/nginx/redirection");
|
||||
}}
|
||||
>
|
||||
<div className="card-body">
|
||||
<div className="row align-items-center">
|
||||
<div className="col-auto">
|
||||
<span className="bg-yellow text-white avatar">
|
||||
<IconArrowsCross />
|
||||
</span>
|
||||
</div>
|
||||
<div className="col">
|
||||
<T id="redirection-hosts.count" data={{ count: hostReport?.redirection }} />
|
||||
</a>
|
||||
</div>
|
||||
</HasPermission>
|
||||
<HasPermission permission="redirectionHosts" type="view" hideError>
|
||||
<div className="col-sm-6 col-lg-3">
|
||||
<a
|
||||
href="/nginx/redirection"
|
||||
className="card card-sm card-link card-link-pop"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
navigate("/nginx/redirection");
|
||||
}}
|
||||
>
|
||||
<div className="card-body">
|
||||
<div className="row align-items-center">
|
||||
<div className="col-auto">
|
||||
<span className="bg-yellow text-white avatar">
|
||||
<IconArrowsCross />
|
||||
</span>
|
||||
</div>
|
||||
<div className="col">
|
||||
<T
|
||||
id="redirection-hosts.count"
|
||||
data={{ count: hostReport?.redirection }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div className="col-sm-6 col-lg-3">
|
||||
<a
|
||||
href="/nginx/stream"
|
||||
className="card card-sm card-link card-link-pop"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
navigate("/nginx/stream");
|
||||
}}
|
||||
>
|
||||
<div className="card-body">
|
||||
<div className="row align-items-center">
|
||||
<div className="col-auto">
|
||||
<span className="bg-blue text-white avatar">
|
||||
<IconDisc />
|
||||
</span>
|
||||
</div>
|
||||
<div className="col">
|
||||
<T id="streams.count" data={{ count: hostReport?.stream }} />
|
||||
</a>
|
||||
</div>
|
||||
</HasPermission>
|
||||
<HasPermission permission="streams" type="view" hideError>
|
||||
<div className="col-sm-6 col-lg-3">
|
||||
<a
|
||||
href="/nginx/stream"
|
||||
className="card card-sm card-link card-link-pop"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
navigate("/nginx/stream");
|
||||
}}
|
||||
>
|
||||
<div className="card-body">
|
||||
<div className="row align-items-center">
|
||||
<div className="col-auto">
|
||||
<span className="bg-blue text-white avatar">
|
||||
<IconDisc />
|
||||
</span>
|
||||
</div>
|
||||
<div className="col">
|
||||
<T id="streams.count" data={{ count: hostReport?.stream }} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div className="col-sm-6 col-lg-3">
|
||||
<a
|
||||
href="/nginx/404"
|
||||
className="card card-sm card-link card-link-pop"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
navigate("/nginx/404");
|
||||
}}
|
||||
>
|
||||
<div className="card-body">
|
||||
<div className="row align-items-center">
|
||||
<div className="col-auto">
|
||||
<span className="bg-red text-white avatar">
|
||||
<IconBoltOff />
|
||||
</span>
|
||||
</div>
|
||||
<div className="col">
|
||||
<T id="dead-hosts.count" data={{ count: hostReport?.dead }} />
|
||||
</a>
|
||||
</div>
|
||||
</HasPermission>
|
||||
<HasPermission permission="deadHosts" type="view" hideError>
|
||||
<div className="col-sm-6 col-lg-3">
|
||||
<a
|
||||
href="/nginx/404"
|
||||
className="card card-sm card-link card-link-pop"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
navigate("/nginx/404");
|
||||
}}
|
||||
>
|
||||
<div className="card-body">
|
||||
<div className="row align-items-center">
|
||||
<div className="col-auto">
|
||||
<span className="bg-red text-white avatar">
|
||||
<IconBoltOff />
|
||||
</span>
|
||||
</div>
|
||||
<div className="col">
|
||||
<T id="dead-hosts.count" data={{ count: hostReport?.dead }} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</HasPermission>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { IconDotsVertical, IconEdit, IconLock, IconPower, IconShield, IconTrash } from "@tabler/icons-react";
|
||||
import {
|
||||
IconDotsVertical,
|
||||
IconEdit,
|
||||
IconLock,
|
||||
IconLogin2,
|
||||
IconPower,
|
||||
IconShield,
|
||||
IconTrash,
|
||||
} from "@tabler/icons-react";
|
||||
import { createColumnHelper, getCoreRowModel, useReactTable } from "@tanstack/react-table";
|
||||
import { useMemo } from "react";
|
||||
import type { User } from "src/api/backend";
|
||||
@@ -24,6 +32,7 @@ interface Props {
|
||||
onDeleteUser?: (id: number) => void;
|
||||
onDisableToggle?: (id: number, enabled: boolean) => void;
|
||||
onNewUser?: () => void;
|
||||
onLoginAs?: (id: number) => void;
|
||||
}
|
||||
export default function Table({
|
||||
data,
|
||||
@@ -36,6 +45,7 @@ export default function Table({
|
||||
onDeleteUser,
|
||||
onDisableToggle,
|
||||
onNewUser,
|
||||
onLoginAs,
|
||||
}: Props) {
|
||||
const columnHelper = createColumnHelper<User>();
|
||||
const columns = useMemo(
|
||||
@@ -153,6 +163,24 @@ export default function Table({
|
||||
<IconPower size={16} />
|
||||
<T id={info.row.original.isDisabled ? "action.enable" : "action.disable"} />
|
||||
</a>
|
||||
{info.row.original.isDisabled ? (
|
||||
<div className="dropdown-item text-muted">
|
||||
<IconLogin2 size={16} />
|
||||
<T id="user.login-as" data={{ name: info.row.original.name }} />
|
||||
</div>
|
||||
) : (
|
||||
<a
|
||||
className="dropdown-item"
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
onLoginAs?.(info.row.original.id);
|
||||
}}
|
||||
>
|
||||
<IconLogin2 size={16} />
|
||||
<T id="user.login-as" data={{ name: info.row.original.name }} />
|
||||
</a>
|
||||
)}
|
||||
<div className="dropdown-divider" />
|
||||
<a
|
||||
className="dropdown-item"
|
||||
@@ -176,7 +204,16 @@ export default function Table({
|
||||
},
|
||||
}),
|
||||
],
|
||||
[columnHelper, currentUserId, onEditUser, onDisableToggle, onDeleteUser, onEditPermissions, onSetPassword],
|
||||
[
|
||||
columnHelper,
|
||||
currentUserId,
|
||||
onEditUser,
|
||||
onDisableToggle,
|
||||
onDeleteUser,
|
||||
onEditPermissions,
|
||||
onSetPassword,
|
||||
onLoginAs,
|
||||
],
|
||||
);
|
||||
|
||||
const tableInstance = useReactTable<User>({
|
||||
|
||||
@@ -4,14 +4,16 @@ import { useState } from "react";
|
||||
import Alert from "react-bootstrap/Alert";
|
||||
import { deleteUser, toggleUser } from "src/api/backend";
|
||||
import { Button, LoadingPage } from "src/components";
|
||||
import { useAuthState } from "src/context";
|
||||
import { useUser, useUsers } from "src/hooks";
|
||||
import { T } from "src/locale";
|
||||
import { showDeleteConfirmModal, showPermissionsModal, showSetPasswordModal, showUserModal } from "src/modals";
|
||||
import { showObjectSuccess } from "src/notifications";
|
||||
import { showError, showObjectSuccess } from "src/notifications";
|
||||
import Table from "./Table";
|
||||
|
||||
export default function TableWrapper() {
|
||||
const queryClient = useQueryClient();
|
||||
const { loginAs } = useAuthState();
|
||||
const [search, setSearch] = useState("");
|
||||
const { isFetching, isLoading, isError, error, data } = useUsers(["permissions"]);
|
||||
const { data: currentUser } = useUser("me");
|
||||
@@ -24,6 +26,16 @@ export default function TableWrapper() {
|
||||
return <Alert variant="danger">{error?.message || "Unknown error"}</Alert>;
|
||||
}
|
||||
|
||||
const handleLoginAs = async (id: number) => {
|
||||
try {
|
||||
await loginAs(id);
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
showError(err.message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (id: number) => {
|
||||
await deleteUser(id);
|
||||
showObjectSuccess("user", "deleted");
|
||||
@@ -103,6 +115,7 @@ export default function TableWrapper() {
|
||||
}
|
||||
onDisableToggle={handleDisableToggle}
|
||||
onNewUser={() => showUserModal("new")}
|
||||
onLoginAs={handleLoginAs}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user