mirror of https://github.com/micromata/borgbackup-butler.git

Kai Reinhard
18.35.2019 1dab687e769cc830adeb2ef53642abad77e1f7a6
BorgRepConfig...Repo config...
7 files modified
216 ■■■■■ changed files
borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ButlerCache.java 10 ●●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/config/BorgRepoConfig.java 9 ●●●● patch | view | raw | blame | history
borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ReposRest.java 22 ●●●● patch | view | raw | blame | history
borgbutler-webapp/src/components/views/repos/RepoArchiveListView.jsx 118 ●●●●● patch | view | raw | blame | history
borgbutler-webapp/src/components/views/repos/RepoCard.jsx 2 ●●● patch | view | raw | blame | history
borgbutler-webapp/src/components/views/repos/RepoConfigPanel.jsx 53 ●●●●● patch | view | raw | blame | history
borgbutler-webapp/src/containers/WebApp.jsx 2 ●●● patch | view | raw | blame | history
borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ButlerCache.java
@@ -124,9 +124,15 @@
        this.repoCacheAccess.clear();
    }
    public void clearRepoCacheAccess(String repo) {
        if (this.repoCacheAccess.get(repo) != null) {
            log.info("Clearing repository cache '" + repo + "'...");
            this.repoCacheAccess.remove(repo);
        }
    }
    public void clearRepoCacheAccess(Repository repository) {
        log.info("Clearing repository cache '" + repository.getName() + "'...");
        this.repoCacheAccess.remove(repository.getName());
        clearRepoCacheAccess(repository.getName());
    }
    /**
borgbutler-core/src/main/java/de/micromata/borgbutler/config/BorgRepoConfig.java
@@ -1,6 +1,5 @@
package de.micromata.borgbutler.config;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
@@ -29,7 +28,6 @@
    private String passwordCommand;
    @Getter
    @Setter
    @JsonIgnore
    private String id;
    public String[] getEnvironmentVariables() {
@@ -51,4 +49,11 @@
        if (StringUtils.isBlank(value)) return;
        list.add(variable + "=" + value);
    }
   public void copyFrom(BorgRepoConfig other) {
        this.displayName = other.displayName;
        this.repo = other.repo;
        this.passphrase = other.passphrase;
        this.passwordCommand = other.passwordCommand;
   }
}
borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ReposRest.java
@@ -10,10 +10,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.util.List;
@@ -68,6 +65,23 @@
        return JsonUtils.toJson(repoConfig, prettyPrinter);
    }
    @POST
    @Path("repoConfig")
    @Produces(MediaType.TEXT_PLAIN)
    public void setRepoConfig(String jsonConfig) {
        BorgRepoConfig newRepoConfig = JsonUtils.fromJson(BorgRepoConfig.class, jsonConfig);
        BorgRepoConfig repoConfig = ConfigurationHandler.getConfiguration().getRepoConfig(newRepoConfig.getId());
        if (repoConfig == null) {
            log.error("Can't find repo config '" + newRepoConfig.getId() + "'. Can't save new settings.");
            return;
        }
        ButlerCache.getInstance().clearRepoCacheAccess(repoConfig.getRepo());
        ButlerCache.getInstance().clearRepoCacheAccess(newRepoConfig.getRepo());
        repoConfig.copyFrom(newRepoConfig);
        ConfigurationHandler.getInstance().save();
    }
    /**
     *
     * @param id id or name of repo.
borgbutler-webapp/src/components/views/repos/RepoArchiveListView.jsx
@@ -1,5 +1,5 @@
import React from 'react'
import {Nav, NavLink, TabContent, Table, TabPane} from 'reactstrap';
import {Badge, Nav, NavLink, TabContent, Table, TabPane} from 'reactstrap';
import {Link} from "react-router-dom";
import classNames from 'classnames';
import {PageHeader} from '../../general/BootstrapComponents';
@@ -13,8 +13,10 @@
    state = {
        id: this.props.match.params.id,
        displayName: this.props.match.params.displayName,
        isFetching: false,
        activeTab: '1',
        redirectOnError: true
    };
    componentDidMount = () => {
@@ -43,7 +45,12 @@
                    repo: json
                })
            })
            .catch(() => this.setState({isFetching: false, failed: true}));
            .catch(() => {
                this.setState({isFetching: false, failed: true})
                if (this.state.redirectOnError && this.state.activeTab !== '3') {
                    this.setState({activeTab: '3', redirectOnError: false});
                }
            });
    };
    toggleTab = tab => () => {
@@ -52,22 +59,46 @@
        })
    };
    afterSave() {
        if (!this.state.failed) {
            this.setState({
                activeTab: '1'
            })
        }
    }
    afterCancel() {
        if (!this.state.failed) {
            this.setState({
                activeTab: '1'
            })
        }
    }
    render = () => {
        let content = undefined;
        let errorBadge = '';
        let content1 = undefined;
        let content2 = undefined;
        const repo = this.state.repo;
        let pageHeader = '';
        const displayName = (this.state.displayName) ? this.state.displayName : `Error: id=${this.state.id}`;
        let pageHeader = <React.Fragment>
            {displayName}
        </React.Fragment>;
        if (this.state.isFetching) {
            content = <JobMonitorPanel repo={this.state.id}/>;
            content1 = <JobMonitorPanel repo={this.state.id}/>;
            content2 = content1;
        } else if (this.state.failed) {
            content = <ErrorAlert
            content1 = <ErrorAlert
                title={'Cannot load Repositories'}
                description={'Something went wrong during contacting the rest api.'}
                description={'Something went wrong, may-be wrong configuration?'}
                action={{
                    handleClick: this.fetchRepo,
                    title: 'Try again'
                }}
            />;
            content2 = content1;
            errorBadge = <Badge color="danger" pill>!</Badge>;
        } else if (this.state.repo) {
            pageHeader = <React.Fragment>
                {repo.displayName}
@@ -128,30 +159,7 @@
                    <td>{repo.cache.path}</td>
                </tr>
            }
            content = <React.Fragment>
                <Nav tabs>
                    <NavLink
                        className={classNames({active: this.state.activeTab === '1'})}
                        onClick={this.toggleTab('1')}
                    >
                        Archives
                    </NavLink>
                    <NavLink
                        className={classNames({active: this.state.activeTab === '2'})}
                        onClick={this.toggleTab('2')}
                    >
                        Information
                    </NavLink>
                    <NavLink
                        className={classNames({active: this.state.activeTab === '3'})}
                        onClick={this.toggleTab('3')}
                    >
                        Configuration
                    </NavLink>
                </Nav>
                <TabContent activeTab={this.state.activeTab}>
                    <TabPane tabId={'1'}>
                        <Table hover>
            content1 = <Table hover>
                            <tbody>
                            <tr>
                                <th>Archive</th>
@@ -174,10 +182,8 @@
                                    </tr>);
                            })}
                            </tbody>
                        </Table>
                    </TabPane>
                    <TabPane tabId={'2'}>
                        <Table striped bordered hover>
            </Table>;
            content2 = <Table striped bordered hover>
                            <tbody>
                            <tr>
                                <td>Id</td>
@@ -199,20 +205,44 @@
                            {encryption}
                            {cachePath}
                            </tbody>
                        </Table>
                    </TabPane>
                    <TabPane tabId={'3'}>
                        <RepoConfigPanel id={repo.id}/>
                    </TabPane>
                </TabContent>
            </React.Fragment>;
            </Table>;
        }
        return <React.Fragment>
            <PageHeader>
                {pageHeader}
            </PageHeader>
            {content}
            <Nav tabs>
                <NavLink
                    className={classNames({active: this.state.activeTab === '1'})}
                    onClick={this.toggleTab('1')}
                >
                    Archives {errorBadge}
                </NavLink>
                <NavLink
                    className={classNames({active: this.state.activeTab === '2'})}
                    onClick={this.toggleTab('2')}
                >
                    Information {errorBadge}
                </NavLink>
                <NavLink
                    className={classNames({active: this.state.activeTab === '3'})}
                    onClick={this.toggleTab('3')}
                >
                    Configuration
                </NavLink>
            </Nav>
            <TabContent activeTab={this.state.activeTab}>
                <TabPane tabId={'1'}>
                    {content1}
                </TabPane>
                <TabPane tabId={'2'}>
                    {content2}
                </TabPane>
                <TabPane tabId={'3'}>
                    <RepoConfigPanel id={this.state.id} afterCancel={this.afterCancel} afterSave={this.afterSave} repoError={this.state.failed}/>
                </TabPane>
            </TabContent>
        </React.Fragment>;
    };
@@ -221,6 +251,8 @@
        this.fetchRepo = this.fetchRepo.bind(this);
        this.toggleTab = this.toggleTab.bind(this);
        this.afterCancel = this.afterCancel.bind(this);
        this.afterSave = this.afterSave.bind(this);
    }
}
borgbutler-webapp/src/components/views/repos/RepoCard.jsx
@@ -18,7 +18,7 @@
        let repoText = this.buildItem(null, content);
        return <React.Fragment>
            <Card tag={Link} to={`/repoArchives/${repo.id}`} outline color="success" className={'repo'}
            <Card tag={Link} to={`/repoArchives/${repo.id}/${repo.displayName}`} outline color="success" className={'repo'}
                  style={{backgroundColor: '#fff'}}>
                <CardHeader>{repo.displayName}</CardHeader>
                <CardBody>
borgbutler-webapp/src/components/views/repos/RepoConfigPanel.jsx
@@ -7,9 +7,7 @@
import PropTypes from "prop-types";
import ErrorAlert from "../../general/ErrorAlert";
class RepoConfigPanel
    extends React
        .Component {
class RepoConfigPanel extends React.Component {
    constructor(props) {
        super(props);
@@ -61,27 +59,42 @@
        this.setState({repoConfig: {...this.state.repoConfig, [event.target.name]: event.target.value}});
    }
    onSave(event) {
        this.setState({
            loading: true
        })
        this.setState({
            loading: false
        })
        this.setReload();
    async onSave(event) {
        const response = fetch(getRestServiceUrl("repos/repoConfig"), {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(this.state.repoConfig)
        });
        if (response) await response;
        if (this.props.afterSave) {
            this.props.afterSave();
        }
    }
    onCancel() {
        this.setReload();
    async onCancel() {
        const response = this.fetch();
        if (response) await response;
        if (this.props.afterCancel) {
            this.props.afterCancel();
        }
    }
    render() {
        let content;
        let repoError = '';
        if (this.props.repoError) {
            repoError =<ErrorAlert
                title={'Cannot access repository'}
                description={'Repo not available or mis-configured (please refer the log files for more details).'}
            />
        }
        if (this.state.isFetching) {
            content = <React.Fragment>Loading...</React.Fragment>;
        } else if (this.state.failed) {
            content = <ErrorAlert
                title={'Cannot load config or repository'}
                title={'Cannot load config of repository'}
                description={'Something went wrong during contacting the rest api.'}
                action={{
                    handleClick: this.fetchRepo,
@@ -109,7 +122,7 @@
                                         onChange={this.handleTextChange}
                                         placeholder="Enter the password command to get the command from."/>
                    <FormLabelInputField label={'Password'} fieldLength={6} type={'password'}
                                         name={'passwordCommand'} value={repoConfig.password}
                                         name={'passphrase'} value={repoConfig.passphrase}
                                         onChange={this.handleTextChange}
                                         hint={"It's recommended to use password command instead."}
                    />
@@ -125,13 +138,15 @@
                <LoadingOverlay active={this.state.loading}/>
            </React.Fragment>;
        }
        return <React.Fragment>{content}</React.Fragment>;
        return <React.Fragment>{content}{repoError}</React.Fragment>;
    }
}
RepoConfigPanel
    .propTypes = {
    id: PropTypes.string
RepoConfigPanel.propTypes = {
    afterCancel: PropTypes.func.isRequired,
    afterSave: PropTypes.func.isRequired,
    id: PropTypes.string,
    repoError: PropTypes.bool
};
export default RepoConfigPanel;
borgbutler-webapp/src/containers/WebApp.jsx
@@ -81,7 +81,7 @@
                                    />
                                ))
                            }
                            <Route path={'/repoArchives/:id'} component={RepoArchiveListView}/>
                            <Route path={'/repoArchives/:id/:displayName'} component={RepoArchiveListView}/>
                            <Route path={'/archives/:repoId/:archiveId'} component={ArchiveView}/>
                        </Switch>
                    </div>