mirror of
				https://github.com/mickael-kerjean/filestash.git
				synced 2025-11-04 05:27:04 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			180 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			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>
 | 
						|
    )
 | 
						|
}
 |