Admin Dashboard overview
This commit is contained in:
parent
18bb6bffa7
commit
d38ad87220
@ -21,7 +21,7 @@ namespace Core\API {
|
|||||||
"allowed_extensions" => new ArrayType("allowed_extensions", Parameter::TYPE_STRING),
|
"allowed_extensions" => new ArrayType("allowed_extensions", Parameter::TYPE_STRING),
|
||||||
"trusted_domains" => new ArrayType("trusted_domains", Parameter::TYPE_STRING),
|
"trusted_domains" => new ArrayType("trusted_domains", Parameter::TYPE_STRING),
|
||||||
"user_registration_enabled" => new Parameter("user_registration_enabled", Parameter::TYPE_BOOLEAN),
|
"user_registration_enabled" => new Parameter("user_registration_enabled", Parameter::TYPE_BOOLEAN),
|
||||||
"captcha_provider" => new StringType("captcha_provider", -1, true, "none", CaptchaProvider::PROVIDERS),
|
"captcha_provider" => new StringType("captcha_provider", -1, true, "disabled", CaptchaProvider::PROVIDERS),
|
||||||
"mail_enabled" => new Parameter("mail_enabled", Parameter::TYPE_BOOLEAN),
|
"mail_enabled" => new Parameter("mail_enabled", Parameter::TYPE_BOOLEAN),
|
||||||
"mail_port" => new IntegerType("mail_port", 1, 65535),
|
"mail_port" => new IntegerType("mail_port", 1, 65535),
|
||||||
"rate_limiting_enabled" => new Parameter("rate_limiting_enabled", Parameter::TYPE_BOOLEAN),
|
"rate_limiting_enabled" => new Parameter("rate_limiting_enabled", Parameter::TYPE_BOOLEAN),
|
||||||
|
@ -46,11 +46,12 @@ class Stats extends Request {
|
|||||||
"server" => [
|
"server" => [
|
||||||
"version" => WEBBASE_VERSION,
|
"version" => WEBBASE_VERSION,
|
||||||
"server" => $_SERVER["SERVER_SOFTWARE"] ?? "Unknown",
|
"server" => $_SERVER["SERVER_SOFTWARE"] ?? "Unknown",
|
||||||
"memory_usage" => memory_get_usage(),
|
"memoryUsage" => memory_get_usage(),
|
||||||
"load_avg" => $loadAvg,
|
"loadAverage" => $loadAvg,
|
||||||
"database" => $this->context->getSQL()->getStatus(),
|
"database" => $this->context->getSQL()->getStatus(),
|
||||||
"mail" => $settings->isMailEnabled(),
|
"mail" => $settings->isMailEnabled(),
|
||||||
"captcha" => $settings->getCaptchaProvider()?->jsonSerialize()
|
"captcha" => $settings->getCaptchaProvider()?->jsonSerialize(),
|
||||||
|
"rateLimiting" => $settings->isRateLimitingEnabled()
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -1470,6 +1470,10 @@ namespace Core\API\User {
|
|||||||
'token' => new StringType('token', 36),
|
'token' => new StringType('token', 36),
|
||||||
));
|
));
|
||||||
$this->userToken = null;
|
$this->userToken = null;
|
||||||
|
$this->rateLimiting = new RateLimiting(
|
||||||
|
new RateLimitRule(10, 30, RateLimitRule::SECOND),
|
||||||
|
new RateLimitRule(30, 1, RateLimitRule::MINUTE),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getToken(): ?UserToken {
|
public function getToken(): ?UserToken {
|
||||||
|
@ -109,7 +109,7 @@ class Settings {
|
|||||||
$settings->timeZone = date_default_timezone_get();
|
$settings->timeZone = date_default_timezone_get();
|
||||||
|
|
||||||
// captcha
|
// captcha
|
||||||
$settings->captchaProvider = "none";
|
$settings->captchaProvider = "disabled";
|
||||||
$settings->captchaSiteKey = "";
|
$settings->captchaSiteKey = "";
|
||||||
$settings->captchaSecretKey = "";
|
$settings->captchaSecretKey = "";
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
return [
|
return [
|
||||||
"title" => "Einstellungen",
|
"title" => "Einstellungen",
|
||||||
"information" => "Informationen",
|
"information" => "Informationen",
|
||||||
|
"disabled" => "Deaktiviert",
|
||||||
|
"enabled" => "Aktiviert",
|
||||||
|
|
||||||
# API Key
|
# API Key
|
||||||
"api_key" => "API Schlüssel",
|
"api_key" => "API Schlüssel",
|
||||||
@ -44,6 +46,8 @@ return [
|
|||||||
"captcha_provider" => "Captcha Anbieter",
|
"captcha_provider" => "Captcha Anbieter",
|
||||||
"captcha_site_key" => "Öffentlicher Captcha Schlüssel",
|
"captcha_site_key" => "Öffentlicher Captcha Schlüssel",
|
||||||
"captcha_secret_key" => "Geheimer Captcha Schlüssel",
|
"captcha_secret_key" => "Geheimer Captcha Schlüssel",
|
||||||
|
"recaptcha" => "Google reCaptcha",
|
||||||
|
"hcaptcha" => "hCaptcha",
|
||||||
|
|
||||||
# redis
|
# redis
|
||||||
"rate_limit" => "Rate-Limit",
|
"rate_limit" => "Rate-Limit",
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
return [
|
return [
|
||||||
"title" => "Settings",
|
"title" => "Settings",
|
||||||
"information" => "Information",
|
"information" => "Information",
|
||||||
|
"disabled" => "Disabled",
|
||||||
|
"enabled" => "Enabled",
|
||||||
|
|
||||||
# API Key
|
# API Key
|
||||||
"api_key" => "API Key",
|
"api_key" => "API Key",
|
||||||
@ -44,6 +46,8 @@ return [
|
|||||||
"captcha_provider" => "Captcha Provider",
|
"captcha_provider" => "Captcha Provider",
|
||||||
"captcha_site_key" => "Captcha Site Key",
|
"captcha_site_key" => "Captcha Site Key",
|
||||||
"captcha_secret_key" => "Secret Captcha Key",
|
"captcha_secret_key" => "Secret Captcha Key",
|
||||||
|
"recaptcha" => "Google reCaptcha",
|
||||||
|
"hcaptcha" => "hCaptcha",
|
||||||
|
|
||||||
# redis
|
# redis
|
||||||
"rate_limit" => "Rate Limiting",
|
"rate_limit" => "Rate Limiting",
|
||||||
|
@ -6,11 +6,11 @@ use Core\Objects\ApiObject;
|
|||||||
|
|
||||||
abstract class CaptchaProvider extends ApiObject {
|
abstract class CaptchaProvider extends ApiObject {
|
||||||
|
|
||||||
const NONE = "none";
|
const DISABLED = "disabled";
|
||||||
const RECAPTCHA = "recaptcha";
|
const RECAPTCHA = "recaptcha";
|
||||||
const HCAPTCHA = "hcaptcha";
|
const HCAPTCHA = "hcaptcha";
|
||||||
|
|
||||||
const PROVIDERS = [self::NONE, self::RECAPTCHA, self::HCAPTCHA];
|
const PROVIDERS = [self::DISABLED, self::RECAPTCHA, self::HCAPTCHA];
|
||||||
|
|
||||||
private string $siteKey;
|
private string $siteKey;
|
||||||
private string $secretKey;
|
private string $secretKey;
|
||||||
|
@ -2,8 +2,18 @@ import * as React from "react";
|
|||||||
import {Link} from "react-router-dom";
|
import {Link} from "react-router-dom";
|
||||||
import {useCallback, useContext, useEffect, useState} from "react";
|
import {useCallback, useContext, useEffect, useState} from "react";
|
||||||
import {LocaleContext} from "shared/locale";
|
import {LocaleContext} from "shared/locale";
|
||||||
import {ArrowCircleRight, BugReport, Groups, LibraryBooks, People} from "@mui/icons-material";
|
import {humanReadableSize} from "shared/util";
|
||||||
import {CircularProgress} from "@mui/material";
|
import {sprintf} from "sprintf-js";
|
||||||
|
import {
|
||||||
|
ArrowCircleRight,
|
||||||
|
BugReport,
|
||||||
|
CheckCircle,
|
||||||
|
Groups,
|
||||||
|
HighlightOff,
|
||||||
|
LibraryBooks,
|
||||||
|
People
|
||||||
|
} from "@mui/icons-material";
|
||||||
|
import {Box, CircularProgress, Paper, Table, TableBody, TableCell, TableRow} from "@mui/material";
|
||||||
|
|
||||||
const StatBox = (props) => <div className={"col-lg-3 col-6"}>
|
const StatBox = (props) => <div className={"col-lg-3 col-6"}>
|
||||||
<div className={"small-box bg-" + props.color}>
|
<div className={"small-box bg-" + props.color}>
|
||||||
@ -24,6 +34,19 @@ const StatBox = (props) => <div className={"col-lg-3 col-6"}>
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
const StatusLine = (props) => {
|
||||||
|
const {enabled, text, ...other} = props;
|
||||||
|
if (enabled) {
|
||||||
|
return <Box display="grid" gridTemplateColumns={"30px auto"}>
|
||||||
|
<CheckCircle color={"primary"} title={text} /> {text}
|
||||||
|
</Box>
|
||||||
|
} else {
|
||||||
|
return <Box display="grid" gridTemplateColumns={"30px auto"}>
|
||||||
|
<HighlightOff color={"secondary"} title={text} /> {text}
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default function Overview(props) {
|
export default function Overview(props) {
|
||||||
|
|
||||||
const [fetchStats, setFetchStats] = useState(true);
|
const [fetchStats, setFetchStats] = useState(true);
|
||||||
@ -32,7 +55,7 @@ export default function Overview(props) {
|
|||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
requestModules(props.api, ["general", "admin"], currentLocale).then(data => {
|
requestModules(props.api, ["general", "admin", "settings"], currentLocale).then(data => {
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
props.showDialog("Error fetching translations: " + data.msg);
|
props.showDialog("Error fetching translations: " + data.msg);
|
||||||
}
|
}
|
||||||
@ -56,12 +79,10 @@ export default function Overview(props) {
|
|||||||
onFetchStats();
|
onFetchStats();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
/*
|
let loadAvg = stats ? stats.server.loadAverage : null;
|
||||||
let loadAvg = this.state.server.load_avg;
|
if (Array.isArray(loadAvg)) {
|
||||||
if (Array.isArray(this.state.server.load_avg)) {
|
loadAvg = loadAvg.map(v => sprintf("%.1f", v)).join(", ");
|
||||||
loadAvg = this.state.server.load_avg.join(" ");
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<div className={"content-header"}>
|
<div className={"content-header"}>
|
||||||
@ -100,6 +121,59 @@ export default function Overview(props) {
|
|||||||
link={"/admin/logs"} />
|
link={"/admin/logs"} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Box m={2} p={2} component={Paper}>
|
||||||
|
<h4>Server Stats</h4><hr />
|
||||||
|
{stats === null ? <CircularProgress /> :
|
||||||
|
<Table>
|
||||||
|
<TableBody>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Web-Base Version</TableCell>
|
||||||
|
<TableCell>{stats.server.version}</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Server</TableCell>
|
||||||
|
<TableCell>{stats.server.server ?? "Unknown"}</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Load Average</TableCell>
|
||||||
|
<TableCell>{loadAvg}</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Memory Usage</TableCell>
|
||||||
|
<TableCell>{humanReadableSize(stats.server.memoryUsage)}</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Database</TableCell>
|
||||||
|
<TableCell>{stats.server.database}</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Captcha</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<StatusLine enabled={!!stats.server.captcha}
|
||||||
|
text={L("settings." + (stats.server.captcha ? stats.server.captcha.name : "disabled"))}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Mail</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<StatusLine enabled={!!stats.server.mail}
|
||||||
|
text={L("settings." + (stats.server.mail ? "enabled" : "disabled"))}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Rate-Limiting</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<StatusLine enabled={!!stats.server.rateLimiting}
|
||||||
|
text={L("settings." + (stats.server.rateLimiting ? "enabled" : "disabled"))}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
}
|
||||||
|
</Box>
|
||||||
</section>
|
</section>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
@ -283,10 +283,16 @@ export default function SettingsView(props) {
|
|||||||
</FormGroup>
|
</FormGroup>
|
||||||
];
|
];
|
||||||
} else if (selectedTab === "captcha") {
|
} else if (selectedTab === "captcha") {
|
||||||
|
let captchaOptions = {};
|
||||||
|
["disabled", "recaptcha", "hcaptcha"].reduce((map, key) => {
|
||||||
|
map[key] = L("settings." + key);
|
||||||
|
return map;
|
||||||
|
}, captchaOptions);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
renderSelection("captcha_provider", {"none": L("settings.none"), "recaptcha": "Google reCaptcha", "hcaptcha": "hCaptcha"}),
|
renderSelection("captcha_provider", captchaOptions),
|
||||||
renderTextInput("captcha_site_key", settings.captcha_provider === "none"),
|
renderTextInput("captcha_site_key", settings.captcha_provider === "disabled"),
|
||||||
renderPasswordInput("captcha_secret_key", settings.captcha_provider === "none"),
|
renderPasswordInput("captcha_secret_key", settings.captcha_provider === "disabled"),
|
||||||
];
|
];
|
||||||
} else if (selectedTab === "redis") {
|
} else if (selectedTab === "redis") {
|
||||||
return [
|
return [
|
||||||
|
Loading…
Reference in New Issue
Block a user