Files
2023-10-07 22:47:37 +11:00

180 lines
5.4 KiB
JavaScript

import React, { useState, useEffect, useRef, useCallback } from "react";
import { FormBuilder, Loader, Button, Icon } from "../../components/";
import { Config, Log, Audit } from "../../model/";
import { FormObjToJSON, notify, format, nop, debounce } from "../../helpers/";
import { t } from "../../locales/";
import "./logger.scss";
export function LogPage({ isSaving = nop }) {
return (
<div className="component_logpage sticky">
<h2>Logging</h2>
<LogComponent isSaving={isSaving} />
<h2>Activity Report</h2>
<AuditComponent />
</div>
);
}
const formRender = ($input, props, struct, onChange) => (
<label className={"no-select input_type_" + props.params["type"]}>
<div>
<span>
{ format(struct.label) }:
</span>
<div style={{ width: "100%" }}>
{ $input }
</div>
</div>
<div>
<span className="nothing"></span>
<div style={{ width: "100%" }}>
{
struct.description && (
<div className="description">{struct.description}</div>
)
}
</div>
</div>
</label>
);
function LogComponent({ isSaving }) {
const [log, setLog] = useState("");
const [form, setForm] = useState({});
const [config, setConfig] = useState({});
const $log = useRef();
const filename = () => {
const t = new Date().toISOString().substring(0, 10).replace(/-/g, "");
return `access_${t}.log`;
};
const onChange = (r) => {
const c = Object.assign({}, config);
c["log"] = r[""]["params"];
c["connections"] = window.CONFIG.connections;
delete c["constant"];
isSaving(true);
Config.save(c, true, () => {
isSaving(false);
}, () => {
isSaving(false);
notify.send(err && err.message || t("Oops"), "error");
});
};
const fetchLogs = () => {
Log.get(1024*100).then((log) => { // get only the last 100kb of log
setLog(log + "\n\n\n\n\n");
if ($log.current.scrollTop === 0) {
$log.current.scrollTop = $log.current.scrollHeight;
}
}).catch((err) => {
setLog(err && err.message || t("Oops"));
});
};
useEffect(() => {
Config.all().then((config) => {
setForm({ "": { "params": config["log"] } });
setConfig(FormObjToJSON(config));
});
fetchLogs();
const id = setInterval(fetchLogs, 5000);
return () => {
clearInterval(id);
Config.clear();
isSaving(false);
};
}, []);
return (
<React.Fragment>
<div style={{ minHeight: "150px" }}>
<FormBuilder
form={form}
onChange={onChange}
render={formRender} />
</div>
<pre style={{ height: "350px" }} ref={$log}>
{ log === "" ? <Loader/> : log }
</pre>
<div>
<a href={Log.url()} download={filename()}>
<Button className="primary">Download</Button>
</a>
</div>
<br/><br/>
</React.Fragment>
);
}
function AuditComponent() {
const [loading, setLoading] = useState(false);
const [formSpec, setFormSpec] = useState(null);
const [searchParams, setSearchParams] = useState({});
const [debouncedSearchParams, setDebouncedSearchParams] = useState({});
const [render, setRender] = useState(null);
const onChange = (a) => {
const sp = {};
const formBlocks = Object.keys(a[""]);
for(let i=0; i<formBlocks.length; i++) {
let params = Object.keys(a[""][formBlocks[i]]);
for(let j=0; j<params.length; j++) {
const key = params[j];
const value = a[""][formBlocks[i]][params[j]];
if(value !== null && value !== "") sp[key] = value;
}
}
setSearchParams(sp);
debouncedSetSearchParams(sp);
setLoading(true);
};
const debouncedSetSearchParams = useCallback(debounce((a) => {
setDebouncedSearchParams(a);
}, 1000), []);
useEffect(() => {
setLoading(true);
const ctrl = new AbortController();
Audit.get(searchParams, ctrl).then(([form, html]) => {
setLoading(false);
if(formSpec === null) setFormSpec({ "": form });
setRender(html);
}).catch((err) => {
if(err.code === "ABORTED") return;
setLoading(false);
setRender(err.message);
});
return () => ctrl.abort();
}, [debouncedSearchParams]);
return (
<div className="component_audit">
{
formSpec && (
<FormBuilder
form={formSpec}
onChange={onChange}
render={formRender} />
)
}
<div style={{minHeight: "200px"}}>
{
loading ? (
<div className="center"><Icon name="loading" /></div>
) : (
<div dangerouslySetInnerHTML={{ __html: render }}></div>
)
}
</div>
</div>
)
}