mirror of
https://gitlab.com/comunic/comunicconsole
synced 2024-12-25 13:08:52 +00:00
Display admin actions
This commit is contained in:
parent
54c9b84945
commit
0cd6de200d
42
package-lock.json
generated
42
package-lock.json
generated
@ -1788,6 +1788,35 @@
|
||||
"react-transition-group": "^4.4.0"
|
||||
}
|
||||
},
|
||||
"@material-ui/data-grid": {
|
||||
"version": "4.0.0-alpha.26",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/data-grid/-/data-grid-4.0.0-alpha.26.tgz",
|
||||
"integrity": "sha512-mA2mncqQAISRLl7FR5NSmlus0wLQ5MSbIjg7WJyGKoOojNwEhK46nmYPvukljXMRoaKQpiv4U+ULI5CuuxlXgQ==",
|
||||
"requires": {
|
||||
"@material-ui/utils": "^5.0.0-alpha.14",
|
||||
"prop-types": "^15.7.2",
|
||||
"reselect": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@material-ui/utils": {
|
||||
"version": "5.0.0-alpha.31",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-5.0.0-alpha.31.tgz",
|
||||
"integrity": "sha512-4OzVD12+HbfWMftwiHCBforgjkhzbWMdK9GTQLQcekjdG2qpi41BGvanPpHjlxegzou0A2MEaULBvWqsKrUP9A==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"@types/prop-types": "^15.7.3",
|
||||
"@types/react-is": "^16.7.1 || ^17.0.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-is": "^17.0.0"
|
||||
}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@material-ui/icons": {
|
||||
"version": "4.11.2",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.11.2.tgz",
|
||||
@ -2432,6 +2461,14 @@
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/react-is": {
|
||||
"version": "17.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.0.tgz",
|
||||
"integrity": "sha512-A0DQ1YWZ0RG2+PV7neAotNCIh8gZ3z7tQnDJyS2xRPDNtAtSPcJ9YyfMP8be36Ha0kQRzbZCrrTMznA4blqO5g==",
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/react-router": {
|
||||
"version": "5.1.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.14.tgz",
|
||||
@ -13187,6 +13224,11 @@
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
|
||||
},
|
||||
"reselect": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.0.0.tgz",
|
||||
"integrity": "sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA=="
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.18.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz",
|
||||
|
@ -4,6 +4,7 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@material-ui/core": "^4.11.4",
|
||||
"@material-ui/data-grid": "^4.0.0-alpha.26",
|
||||
"@material-ui/icons": "^4.11.2",
|
||||
"@testing-library/jest-dom": "^5.12.0",
|
||||
"@testing-library/react": "^11.2.6",
|
||||
|
@ -17,7 +17,7 @@ export interface AdminAccount {
|
||||
name: string;
|
||||
email: string;
|
||||
time_create: number;
|
||||
roles: Array<"manage_admins" | "manage_users" | "access_full_admin_logs">;
|
||||
roles: Array<"manage_admins" | "manage_users" | "access_all_admin_logs">;
|
||||
}
|
||||
|
||||
export interface NewAdminGeneralSettings {
|
||||
|
30
src/helpers/AdminLogsHelper.ts
Normal file
30
src/helpers/AdminLogsHelper.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { serverRequest } from "./APIHelper";
|
||||
|
||||
export interface AdminLogMessage {
|
||||
id: number;
|
||||
admin_id: number;
|
||||
ip: string;
|
||||
time: number;
|
||||
action: string;
|
||||
args: any;
|
||||
format: string;
|
||||
}
|
||||
|
||||
export class AdminLogsHelper {
|
||||
/**
|
||||
* Get the list of admin log actions from the server
|
||||
*/
|
||||
static async GetLogs(): Promise<AdminLogMessage[]> {
|
||||
return (await serverRequest("logs/list")).map((el: any) => {
|
||||
if (typeof el.action === "string") return el;
|
||||
|
||||
for (let key in el.action) {
|
||||
if (!el.action.hasOwnProperty(key)) continue;
|
||||
|
||||
el.args = el.action[key];
|
||||
el.action = key;
|
||||
}
|
||||
return el;
|
||||
});
|
||||
}
|
||||
}
|
143
src/ui/routes/AccountLogsRoute.tsx
Normal file
143
src/ui/routes/AccountLogsRoute.tsx
Normal file
@ -0,0 +1,143 @@
|
||||
import {
|
||||
Paper,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
} from "@material-ui/core";
|
||||
import React from "react";
|
||||
import { AccountHelper, AdminAccount } from "../../helpers/AccountHelper";
|
||||
import {
|
||||
AdminLogMessage,
|
||||
AdminLogsHelper,
|
||||
} from "../../helpers/AdminLogsHelper";
|
||||
import { AsyncWidget } from "../widgets/AsyncWidget";
|
||||
import { PageTitle } from "../widgets/PageTitle";
|
||||
import { TimestampWidget } from "../widgets/TimestampWidget";
|
||||
|
||||
/**
|
||||
* View admin logs
|
||||
*
|
||||
* @author Pierre Hubert
|
||||
*/
|
||||
export class AccountLogsRoute extends React.Component<
|
||||
{},
|
||||
{ logs: AdminLogMessage[]; admins: AdminAccount[] }
|
||||
> {
|
||||
constructor(p: any) {
|
||||
super(p);
|
||||
|
||||
this.load = this.load.bind(this);
|
||||
this.build = this.build.bind(this);
|
||||
}
|
||||
|
||||
async load() {
|
||||
const admins = await AccountHelper.GetAdminsList();
|
||||
const logs = await AdminLogsHelper.GetLogs();
|
||||
|
||||
this.setState({
|
||||
admins: admins,
|
||||
logs: logs,
|
||||
});
|
||||
}
|
||||
|
||||
getAdminName(id: number): string {
|
||||
const admin = this.state.admins.find((a) => a.id === id);
|
||||
return admin ? admin.name : "Unknown admin";
|
||||
}
|
||||
|
||||
build() {
|
||||
return (
|
||||
<div>
|
||||
<PageTitle name="Admin activity records" />
|
||||
|
||||
<TableContainer component={Paper}>
|
||||
<Table size="small">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Admin</TableCell>
|
||||
<TableCell align="right">Time</TableCell>
|
||||
<TableCell align="right">IP address</TableCell>
|
||||
<TableCell align="right">Action</TableCell>
|
||||
<TableCell align="right">Details</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{this.state.logs.reverse().map((message) => {
|
||||
let formattedMessage = message.format;
|
||||
for (let arg in message.args) {
|
||||
if (message.args.hasOwnProperty(arg))
|
||||
formattedMessage =
|
||||
formattedMessage.replace(
|
||||
"{" + arg + "}",
|
||||
message.args[arg]
|
||||
);
|
||||
}
|
||||
|
||||
// Handle [admin] tag
|
||||
const regex: any =
|
||||
"\\[admin\\][0-9]+\\[/admin\\]";
|
||||
const adminsReferences = Array.from(
|
||||
formattedMessage.matchAll(regex)
|
||||
);
|
||||
|
||||
for (let entry of adminsReferences) {
|
||||
const pattern = entry[0];
|
||||
const adminId = Number(
|
||||
entry[0].split("]")[1].split("[")[0]
|
||||
);
|
||||
const adminName =
|
||||
this.getAdminName(adminId);
|
||||
formattedMessage = formattedMessage.replace(
|
||||
pattern,
|
||||
adminName
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TableRow key={message.id} hover>
|
||||
<TableCell>
|
||||
{this.getAdminName(
|
||||
message.admin_id
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<TimestampWidget
|
||||
time={message.time}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{message.ip}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{message.action}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{formattedMessage}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<p style={{ color: "white" }}>
|
||||
Note: your old activity records are automatically deleted
|
||||
after a period of time.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<AsyncWidget
|
||||
errorMessage="Failed to retrieve admin logs!"
|
||||
onBuild={this.build}
|
||||
load={this.load}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ import {
|
||||
import { Home, Person } from "@material-ui/icons";
|
||||
import GroupIcon from "@material-ui/icons/Group";
|
||||
import CloseSharpIcon from "@material-ui/icons/CloseSharp";
|
||||
import HistoryIcon from "@material-ui/icons/History";
|
||||
import React from "react";
|
||||
import {
|
||||
BrowserRouter as Router,
|
||||
@ -33,6 +34,7 @@ import { AccountSettingsRoute } from "./AccountSettingsRoute";
|
||||
import { HomeRoute } from "./HomeRoute";
|
||||
import { NotFoundRoute } from "./NotFoundRoute";
|
||||
import { AccountsListRoute } from "./AccountsListRoute";
|
||||
import { AccountLogsRoute } from "./AccountLogsRoute";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
@ -93,6 +95,11 @@ function Menu() {
|
||||
icon={<GroupIcon />}
|
||||
uri="/accounts"
|
||||
/>
|
||||
<MainMenuItem
|
||||
title="Admin Logs"
|
||||
icon={<HistoryIcon />}
|
||||
uri="/logs"
|
||||
/>
|
||||
<Divider />
|
||||
<MainMenuItem
|
||||
title="My account"
|
||||
@ -201,7 +208,11 @@ export function MainRoute() {
|
||||
</Route>
|
||||
|
||||
<Route path="/accounts/:id">
|
||||
<AccountSettingsRoute></AccountSettingsRoute>
|
||||
<AccountSettingsRoute />
|
||||
</Route>
|
||||
|
||||
<Route path="/logs">
|
||||
<AccountLogsRoute />
|
||||
</Route>
|
||||
|
||||
<Route path="*">
|
||||
|
Loading…
Reference in New Issue
Block a user