web-base/fileControlPanel/src/elements/token-list.js
2021-03-31 20:40:46 +02:00

286 lines
11 KiB
JavaScript

import * as React from "react";
import Icon from "./icon";
import moment from "moment";
import {Popup} from "./popup";
import Alert from "./alert";
import {useEffect, useState} from "react";
import ReactTooltip from 'react-tooltip';
export function TokenList(props) {
let api = props.api;
let selectedFiles = props.selectedFiles || [];
let directories = props.directories || {};
let [tokens, setTokens] = useState(null);
let [alerts, setAlerts] = useState([]);
let [hideRevoked, setHideRevoked] = useState(true);
let [popup, setPopup] = useState({
tokenType: "download",
maxFiles: 0,
maxSize: 0,
extensions: "",
durability: 24 * 60 * 2,
visible: false,
directory: 0
});
useEffect(() => {
setTimeout(() => {
if (tokens) {
let hasChanged = false;
let newTokens = tokens.slice();
for (let token of newTokens) {
if (token.tooltip) hasChanged = true;
token.tooltip = false;
}
if (hasChanged) setTokens(newTokens);
}
}, 1500);
}, [tokens]);
function fetchTokens() {
api.listTokens().then((res) => {
if (res.success) {
setTokens(res.tokens);
} else {
pushAlert(res, "Error fetching tokens");
setTokens([]);
}
});
}
let rows = [];
if (tokens === null) {
fetchTokens();
} else {
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
const validUntil = token.valid_until;
const revoked = validUntil !== null && moment(validUntil).isSameOrBefore(new Date());
if (revoked && hideRevoked) {
continue;
}
const timeStr = (validUntil === null ? "Forever" : moment(validUntil).format("Do MMM YYYY, HH:mm"));
const tooltipPropsRevoke = (revoked ? {} : {
"data-tip": "Revoke", "data-place": "bottom", "data-type": "error",
"data-effect": "solid", "data-for": "tooltip-token-" + token.uid,
});
const tooltipPropsCopy = (revoked ? {} : {
"data-tip": "", "data-place": "bottom", "data-type": "info",
"data-effect": "solid", "data-for": "tooltip-token-" + token.uid,
});
rows.push(
<tr key={"token-" + token.uid} className={revoked ? "token-revoked" : ""}>
<td>{token.token}</td>
<td>{token.type}</td>
<td>{timeStr}</td>
<td>
{ revoked ? <></> : <ReactTooltip id={"tooltip-token-" + token.uid}
getContent={(x) => { if (x === "Revoke") return x; return (!!token.tooltip ? "Coped successfully!" : "Copy to Clipboard"); }} /> }
<Icon icon={"times"} className={"clickable text-" + (revoked ? "secondary" : "danger")}
onClick={() => (revoked ? null : onRevokeToken(token.token))} disabled={revoked}
{...tooltipPropsRevoke} />
<Icon icon={"save"} className={"clickable text-" + (revoked ? "secondary" : "info")}
onClick={() => (revoked ? null : onCopyToken(i))} disabled={revoked}
{...tooltipPropsCopy} />
</td>
</tr>
);
}
}
let alertElements = [];
for (let i = 0; i < alerts.length; i++) {
const alert = alerts[i];
alertElements.push(
<Alert key={"alert-" + i} {...alert} onClose={() => removeAlert(i)}/>
);
}
let options = [];
for (const [uid, dir] of Object.entries(directories)) {
options.push(
<option key={"option-" + dir} value={uid}>{dir}</option>
);
}
return <>
<h4>
<Icon icon={"sync"} className={"mx-3 clickable small"} onClick={fetchTokens}/>
Tokens
</h4>
<div className={"form-check p-3 ml-3"}>
<input type={"checkbox"} checked={hideRevoked} name={"hide-revoked"}
className={"form-check-input"} style={{marginTop: "0.2rem"}}
onChange={(e) => setHideRevoked(e.target.checked)}/>
<label htmlFor={"hide-revoked"} className={"form-check-label pl-2"}>Hide revoked</label>
</div>
<table className={"table token-table"}>
<thead>
<tr>
<th>Token</th>
<th>Type</th>
<th>Valid Until</th>
<th/>
</tr>
</thead>
<tbody>
{rows.length > 0 ? rows :
<tr>
<td colSpan={4} className={"text-center text-black-50"}>
No active tokens connected with this account
</td>
</tr>
}
</tbody>
</table>
<div>
<button type={"button"} className={"btn btn-success m-2"} onClick={onPopupOpen}>
<Icon icon={"plus"} className={"mr-1"}/>
Create Token
</button>
</div>
<div>
{alertElements}
</div>
<Popup title={"Create Token"} visible={popup.visible} buttons={["Ok", "Cancel"]}
onClose={onPopupClose} onClick={onPopupButton}>
<div className={"form-group"}>
<label>Token Durability in minutes (0 = forever):</label>
<input type={"number"} min={0} className={"form-control"}
value={popup.durability} onChange={(e) => onPopupChange(e, "durability")}/>
</div>
<div className="form-group">
<label>Token Type:</label>
<select value={popup.tokenType} className={"form-control"}
onChange={(e) => onPopupChange(e, "tokenType")}>
<option value={"upload"}>Upload</option>
<option value={"download"}>Download</option>
</select>
</div>
{popup.tokenType === "upload" ?
<>
<div className={"form-group"}>
<label>Destination Directory:</label>
<select value={popup.directory} className={"form-control"}
onChange={(e) => onPopupChange(e, "directory")}>
{ options }
</select>
</div>
<b>Upload Restrictions:</b>
<div className={"form-group"}>
<label>Max. Files (0 = unlimited):</label>
<input type={"number"} min={0} max={25} className={"form-control"}
value={popup.maxFiles}
onChange={(e) => onPopupChange(e, "maxFiles")}/>
</div>
<div className={"form-group"}>
<label>Max. Size per file in MiB (0 = unlimited):</label>
<input type={"number"} min={0} max={10} className={"form-control"}
value={popup.maxSize} onChange={(e) => onPopupChange(e, "maxSize")}/>
</div>
<div className={"form-group"}>
<label>Allowed Extensions:</label>
<input type={"text"} placeholder={"(no restrictions)"} maxLength={256}
className={"form-control"}
value={popup.extensions}
onChange={(e) => onPopupChange(e, "extensions")}/>
</div>
</> :
<></>
}
</Popup>
</>;
function pushAlert(res, title) {
let newAlerts = alerts.slice();
newAlerts.push({type: "danger", message: res.msg, title: title});
setAlerts(newAlerts);
}
function removeAlert(i) {
if (i >= 0 && i < alerts.length) {
let newAlerts = alerts.slice();
newAlerts.splice(i, 1);
setAlerts(newAlerts);
}
}
function onRevokeToken(token) {
api.revokeToken(token).then((res) => {
if (res.success) {
let newTokens = tokens.slice();
for (const tokenObj of newTokens) {
if (tokenObj.token === token) {
tokenObj.valid_until = moment();
break;
}
}
setTokens(newTokens);
} else {
pushAlert(res, "Error revoking token");
}
});
}
function onPopupOpen() {
setPopup({...popup, visible: true});
}
function onPopupClose() {
setPopup({...popup, visible: false});
}
function onPopupChange(e, key) {
setPopup({...popup, [key]: e.target.value});
}
function onPopupButton(btn) {
if (btn === "Ok") {
let durability = popup.durability;
let validUntil = (durability === 0 ? null : moment().add(durability, "hours").format("YYYY-MM-DD HH:mm:ss"));
if (popup.tokenType === "download") {
api.createDownloadToken(durability, selectedFiles).then((res) => {
if (!res.success) {
pushAlert(res, "Error creating token");
} else {
let newTokens = tokens.slice();
newTokens.push({token: res.token, valid_until: validUntil, type: "download"});
setTokens(newTokens);
}
});
} else if (popup.tokenType === "upload") {
let parentId = popup.directory === 0 ? null : popup.directory;
let maxSize = Math.round(popup.maxSize * 1024 * 1024);
api.createUploadToken(durability, parentId, popup.maxFiles, maxSize, popup.extensions).then((res) => {
if (!res.success) {
pushAlert(res, "Error creating token");
} else {
let newTokens = tokens.slice();
newTokens.push({uid: res.tokenId, token: res.token, valid_until: validUntil, type: "upload"});
setTokens(newTokens);
}
});
}
}
onPopupClose();
}
function onCopyToken(index) {
let newTokens = tokens.slice();
let token = newTokens[index].token;
let url = window.location.href;
if (!url.endsWith("/")) url += "/";
url += token;
navigator.clipboard.writeText(url);
newTokens[index].tooltip = true;
setTokens(newTokens);
}
}