mirror of
https://github.com/kopia/kopia.git
synced 2026-01-28 16:23:04 -05:00
htmlui: added UI to browse contents of directories and download files
This commit is contained in:
@@ -8,21 +8,18 @@ import 'react-table/react-table.css'
|
||||
import { SourcesTable } from "./SourcesTable";
|
||||
import { PoliciesTable } from "./PoliciesTable";
|
||||
import { SnapshotsTable } from "./SnapshotsTable";
|
||||
import { DirectoryObject } from "./DirectoryObject";
|
||||
import Navbar from 'react-bootstrap/Navbar';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import Nav from 'react-bootstrap/Nav';
|
||||
import Container from 'react-bootstrap/Container';
|
||||
|
||||
import { withRouter } from "react-router";
|
||||
|
||||
import {
|
||||
BrowserRouter as Router,
|
||||
Switch,
|
||||
Route,
|
||||
} from "react-router-dom";
|
||||
|
||||
const SnapshotsTableWithRouter = withRouter(SnapshotsTable);
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<Router>
|
||||
@@ -40,21 +37,10 @@ function App() {
|
||||
|
||||
<Container>
|
||||
<Switch>
|
||||
<Route path="/snapshots/single-source/">
|
||||
<SnapshotsTableWithRouter />
|
||||
</Route>
|
||||
<Route path="/snapshots/dir/">
|
||||
<p>not implemented: directory browser</p>
|
||||
</Route>
|
||||
<Route path="/snapshots/file/">
|
||||
<p>not implemented: file browser</p>
|
||||
</Route>
|
||||
<Route path="/snapshots">
|
||||
<SourcesTable />
|
||||
</Route>
|
||||
<Route path="/policies">
|
||||
<PoliciesTable />
|
||||
</Route>
|
||||
<Route path="/snapshots/single-source/" component={SnapshotsTable} />
|
||||
<Route path="/snapshots/dir/:oid" component={DirectoryObject} />
|
||||
<Route path="/snapshots" component={SourcesTable} />
|
||||
<Route path="/policies" component={PoliciesTable} />
|
||||
<Route exact path="/">
|
||||
<p>not implemented: Status</p>
|
||||
</Route>
|
||||
|
||||
81
htmlui/src/DirectoryItems.js
Normal file
81
htmlui/src/DirectoryItems.js
Normal file
@@ -0,0 +1,81 @@
|
||||
import React, { Component } from 'react';
|
||||
import ReactTable from 'react-table';
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import {
|
||||
sizeDisplayName,
|
||||
objectLink,
|
||||
} from './uiutil';
|
||||
|
||||
function objectName(name, typeID) {
|
||||
if (typeID === "d") {
|
||||
return name + "/";
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
function sizeInfo(item) {
|
||||
if (item.size) {
|
||||
return sizeDisplayName(item.size);
|
||||
}
|
||||
const summ = item.summ;
|
||||
if (!summ) {
|
||||
return "";
|
||||
}
|
||||
return sizeDisplayName(summ.size) + ", " + summ.files + " files, " + summ.dirs + " dirs";
|
||||
}
|
||||
|
||||
function sizeForSorting(item) {
|
||||
if (item.size) {
|
||||
return item.size;
|
||||
}
|
||||
|
||||
if (item.summ && item.summ.size) {
|
||||
return item.summ.size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function sizeSortMethod(a, b, desc) {
|
||||
const l = sizeForSorting(a);
|
||||
const r = sizeForSorting(b);
|
||||
|
||||
if (l < r) {
|
||||
return -1;
|
||||
}
|
||||
if (l > r) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function directoryLinkOrDownload(x) {
|
||||
if (x.obj.startsWith("k")) {
|
||||
return <Link to={objectLink(x.obj)}>{objectName(x.name, x.type)}</Link>;
|
||||
}
|
||||
|
||||
return <a href={"/api/v1/objects/" + x.obj + "?fname=" + x.name}>{x.name}</a>;
|
||||
}
|
||||
|
||||
export class DirectoryItems extends Component {
|
||||
render() {
|
||||
const columns = [{
|
||||
id: "name",
|
||||
Header: 'Name',
|
||||
accessor: x => directoryLinkOrDownload(x),
|
||||
}, {
|
||||
id: "mtime",
|
||||
accessor: "mtime",
|
||||
Header: "Last Mod",
|
||||
}, {
|
||||
id: "size",
|
||||
accessor: x => sizeInfo(x),
|
||||
Header: "Size",
|
||||
sortMethod: sizeSortMethod,
|
||||
}]
|
||||
|
||||
return <ReactTable data={this.props.items} columns={columns} />;
|
||||
}
|
||||
}
|
||||
61
htmlui/src/DirectoryObject.js
Normal file
61
htmlui/src/DirectoryObject.js
Normal file
@@ -0,0 +1,61 @@
|
||||
import React, { Component } from 'react';
|
||||
import axios from 'axios';
|
||||
|
||||
import { DirectoryItems } from "./DirectoryItems";
|
||||
import Button from 'react-bootstrap/Button';
|
||||
import Spinner from 'react-bootstrap/Spinner';
|
||||
|
||||
export class DirectoryObject extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
items: [],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchDirectory(this.props);
|
||||
}
|
||||
|
||||
fetchDirectory(props) {
|
||||
console.log('fetching props:', props);
|
||||
let oid = props.match.params.oid;
|
||||
|
||||
this.setState({
|
||||
isLoading: true,
|
||||
});
|
||||
axios.get('/api/v1/objects/' + oid).then(result => {
|
||||
this.setState({
|
||||
items: result.data.entries,
|
||||
isLoading: false,
|
||||
});
|
||||
}).catch(error => this.setState({
|
||||
error,
|
||||
isLoading: false
|
||||
}));
|
||||
}
|
||||
|
||||
componentWillReceiveProps(props) {
|
||||
this.fetchDirectory(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
let { items, isLoading, error } = this.state;
|
||||
if (error) {
|
||||
return <p>ERROR: {error.message}</p>;
|
||||
}
|
||||
if (isLoading) {
|
||||
return <Spinner animation="border" variant="primary" />;
|
||||
}
|
||||
|
||||
return <div>
|
||||
<Button size="xxl" onClick={this.props.history.goBack} >
|
||||
Back
|
||||
</Button>
|
||||
<DirectoryItems items={items} />
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { Component } from 'react';
|
||||
import ReactTable from 'react-table';
|
||||
import Button from 'react-bootstrap/Button';
|
||||
import axios from 'axios';
|
||||
|
||||
import {
|
||||
@@ -67,11 +66,6 @@ export class PoliciesTable extends Component {
|
||||
accessor: x => timesOfDayDisplayName(x.policy.scheduling.timesOfDay),
|
||||
}]
|
||||
|
||||
return <div>
|
||||
<Button size="xxl">
|
||||
flat button
|
||||
</Button>
|
||||
<ReactTable data={items} columns={columns} />;
|
||||
</div>
|
||||
return <ReactTable data={items} columns={columns} />;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,6 @@ export class SnapshotsTable extends Component {
|
||||
selectedSnapshot: null,
|
||||
});
|
||||
const u = '/api/v1/snapshots?host=' + q.host + '&userName=' + q.userName + '&path=' + q.path;
|
||||
console.log('u', u);
|
||||
axios.get(u).then(result => {
|
||||
console.log('got snapshots', result.data);
|
||||
this.setState({
|
||||
|
||||
@@ -25,7 +25,7 @@ export class SourcesTable extends Component {
|
||||
selectedUser: allUsers,
|
||||
};
|
||||
}
|
||||
;
|
||||
|
||||
componentDidMount() {
|
||||
this.setState({ isLoading: true });
|
||||
axios.get('/api/v1/sources').then(result => {
|
||||
@@ -54,6 +54,7 @@ export class SourcesTable extends Component {
|
||||
hostClicked(h) {
|
||||
alert('host clicked ' + h);
|
||||
}
|
||||
|
||||
render() {
|
||||
let { sources, isLoading, error } = this.state;
|
||||
if (error) {
|
||||
@@ -124,7 +125,7 @@ export class SourcesTable extends Component {
|
||||
<Dropdown.Item onClick={() => this.selectHost(allHosts)}>(all)</Dropdown.Item>
|
||||
{uniqueHosts.map(v => <Dropdown.Item onClick={() => this.selectHost(v)}>{v}</Dropdown.Item>)}
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</Dropdown>
|
||||
|
||||
|
||||
<Dropdown>
|
||||
@@ -139,7 +140,7 @@ export class SourcesTable extends Component {
|
||||
</Dropdown>
|
||||
</Row>
|
||||
|
||||
<p></p>
|
||||
<p></p>
|
||||
<Row><ReactTable data={sources} columns={columns} /></Row>
|
||||
</div>;
|
||||
|
||||
|
||||
@@ -29,6 +29,9 @@ function toDecimalUnitString(f, thousand, prefixes, suffix) {
|
||||
|
||||
|
||||
export function sizeDisplayName(s) {
|
||||
if (s === undefined) {
|
||||
return "";
|
||||
}
|
||||
return toDecimalUnitString(s, 1000, base10UnitPrefixes, "B");
|
||||
}
|
||||
|
||||
@@ -62,5 +65,5 @@ export function objectLink(n) {
|
||||
if (n.startsWith("k")) {
|
||||
return "/snapshots/dir/" + n;
|
||||
}
|
||||
return "/snapshots/file/" + n;
|
||||
return "/api/v1/objects/" + n;
|
||||
}
|
||||
Reference in New Issue
Block a user