diff --git a/client/pages/adminpage/setup.js b/client/pages/adminpage/setup.js
index f8c96672..c6736cd1 100644
--- a/client/pages/adminpage/setup.js
+++ b/client/pages/adminpage/setup.js
@@ -1,6 +1,6 @@
import React from 'react';
-import { Input, Button, Container, Icon } from '../../components/';
+import { Input, Button, Container, Icon, NgIf, Loader } from '../../components/';
import { Config, Admin } from '../../model/';
import { notify, FormObjToJSON, alert, prompt } from '../../helpers';
import { bcrypt_password } from '../../helpers/bcrypt';
@@ -12,116 +12,368 @@ export class SetupPage extends React.Component {
constructor(props){
super(props);
this.state = {
- stage: 0,
- password: "",
- enable_telemetry: false,
- creating_password: false
+ busy: false,
};
}
- createPassword(e){
- this.setState({creating_password: true});
- e.preventDefault();
- const enableLog = (value) => {
- Config.all().then((config) => {
- config.log.telemetry.value = value;
- config = FormObjToJSON(config);
- config.connections = window.CONFIG.connections;
- Config.save(config, false);
- });
- };
+ componentDidMount() {
const start = (e) => {
e.preventDefault();
this.props.history.push("/");
};
- Config.all().then((config) => {
- this.setState({enable_telemetry: config.log.telemetry.value}, () => {
- if(this.state.enable_telemetry === true) return;
- this.unlisten = this.props.history.listen((location, action) => {
- this.unlisten();
- alert.now((
-
-
- Help making this software better by sending crash reports and anonymous usage statistics
-
-
-
- ));
- });
- });
- bcrypt_password(this.state.password)
- .then((hash) => {
- config.auth.admin.value = hash;
- config = FormObjToJSON(config);
- config.connections = window.CONFIG.connections;
- Config.save(config, false)
- .then(() => Admin.login(this.state.password))
- .then(() => this.setState({stage: 1, creating_password: false}))
- .catch((err) => {
- notify.send(err && err.message, "error");
- this.setState({creating_password: false});
- });
- })
+ Config.all().then((config) => {
+ if(config.log.telemetry.value === true) return;
+ this.unlisten = this.props.history.listen((location, action) => {
+ this.unlisten();
+ alert.now((
+
+
+ Help making this software better by sending crash reports and anonymous usage statistics
+
+
+
+ ));
+ });
+ });
+ }
+
+ onAdminPassword(p, done){
+ this.setState({busy: true});
+ Config.all().then((config) => {
+ return bcrypt_password(p).then((hash) => {
+ config = FormObjToJSON(config);
+ config.connections = window.CONFIG.connections;
+ config.auth.admin = hash;
+ Config.save(config, false)
+ .then(() => Admin.login(p))
+ .then(() => this.setState({busy: false}, done))
+ .catch((err) => {
+ this.setState({busy: false});
+ notify.send(err && err.message, "error");
+ });
+ }).catch((err) => {
+ this.setState({busy: false});
+ notify.send("Hash error: " + JSON.stringify(err), "error");
+ });
+ }).catch((err) => {
+ notify.send(err && err.message, "error");
+ this.setState({busy: false});
+ });
+ }
+
+ onExposeInstance(choice, done){
+ this.setState({busy: true});
+ return Config.all().then((config) => {
+ config = FormObjToJSON(config);
+ config.connections = window.CONFIG.connections;
+ switch(choice){
+ case "tunnel":
+ config.features.server.enable_tunnel = true;
+ break;
+ default:
+ config.features.server.enable_tunnel = false;
+ break;
+ }
+ Config.save(config, false)
+ .then(() => this.setState({busy: false}, done))
.catch((err) => {
- notify.send("Hash error: " + JSON.stringify(err), "error");
- this.setState({creating_password: false});
+ notify.send(err && err.message, "error");
+ this.setState({busy: false});
});
+ }).catch((err) => {
+ notify.send(err && err.message, "error");
+ this.setState({busy: false});
});
}
enableLog(value){
Config.all().then((config) => {
- config.log.telemetry.value = value;
config = FormObjToJSON(config);
config.connections = window.CONFIG.connections;
+ config.log.telemetry = value;
Config.save(config, false);
});
- }
+ };
- start(e){
- e.preventDefault();
- this.props.history.push("/");
+ summaryCall(){
+ this.setState({busy: true});
+ return Config.all().then((config) => {
+ this.setState({busy: false});
+ return [
+ {
+ "name_success": "SSL is configured properly",
+ "name_failure": "SSL is not configured properly",
+ "pass": window.location.protocol !== "http:",
+ "severe": true,
+ "message": "This can lead to data leaks. Please use a SSL certificate or expose your instance via a filestash domain"
+ }, {
+ "name_success": "Application is running as " + config.private.user.value,
+ "name_failure": "Application is running as root",
+ "pass": objectGet(config, ["private", "user", "value"]) !== "root",
+ "severe": true,
+ "message": "This is dangerous, you should use another user with less privileges"
+ }, {
+ "name_success": "Emacs is installed",
+ "name_failure": "Emacs is not installed",
+ "pass": objectGet(config, ["private", "emacs", "value"]),
+ "severe": false,
+ "message": "If you want to use all the org-mode features of Filestash, you need to install emacs"
+ }, {
+ "name_success": "Pdftotext is installed",
+ "name_failure": "Pdftotext is not installed",
+ "pass": objectGet(config, ["private", "pdftotext", "value"]),
+ "severe": false,
+ "message": "You won't be able to search through PDF documents without it"
+ }
+ ];
+ }).catch((err) => {
+ notify.send(err && err.message, "error");
+ this.setState({busy: false});
+ });
}
-
- renderStage(stage){
- if(stage === 0){
- return (
-
-
You made it chief! { this.state.creating_password === true ? : null}
-
- Let's start by protecting the admin area with a password:
-
-
-
-
- );
- }
- return (
-
-
Welcome to the engine room
-
- This is the place where you can configure filestash to your liking. Feel free to poke around.
- You can come back by navigating at `{window.location.origin + "/admin"}`.
- Have fun!
-
-
- );
+ tunnelCall(){
+ this.setState({busy: true});
+ return Config.all().then((config) => {
+ //this.setState({busy: false});
+ return objectGet(config, ["features", "server", "tunnel_url", "value"]);
+ });
}
render(){
return (
- { this.renderStage(this.state.stage) }
+
);
}
}
+
+
+class MultiStepForm extends React.Component {
+ constructor(props){
+ super(props);
+ this.state = {
+ current: parseInt(window.location.hash.replace("#", "")) || 0,
+ answer_password: "",
+ has_answered_password: false,
+ answer_expose: "",
+ has_answered_expose: false,
+ deps: [],
+ redirect_uri: null,
+ working_message: "Working"
+ };
+ }
+
+ componentDidMount(){
+ if(this.state.current == 2){
+ this.fetchDependencies();
+ }
+ }
+
+ onAdminPassword(e){
+ e.preventDefault();
+ this.props.onAdminPassword(this.state.answer_password, () => {
+ this.setState({current: 1, has_answered_password: true});
+ });
+ }
+
+ onExposeInstance(value, e){
+ e.preventDefault();
+ this.setState({answer_expose: value});
+ this.props.onExposeInstance(value, () => {
+ if(value === "tunnel"){
+ const waitForDomain = (count = 0) => {
+ return this.props.tunnelCall().then((url) => {
+ console.log("- config tunnel_url: %s - %d", url, count);
+ if(url && /\.filestash\.app$/.test(url) === true){
+ return Promise.resolve(url);
+ }
+ if(count > 10){
+ this.setState({working_message: "Building your domain"});
+ }else if(count > 30){
+ this.setState({working_message: "Processing ."+".".repeat(count % 3)});
+ }
+ if(count >= 60){
+ return Promise.reject({message: "Couldn't create a domain name"});
+ }
+ return new Promise((done) => window.setTimeout(done, 1000))
+ .then(() => waitForDomain(count + 1));
+ });
+ };
+ waitForDomain().then((url) => {
+ // TODO
+ console.log("DONE: ", url, this.state);
+ this.setState({redirect_uri: url});
+ }).catch((err) => {
+ window.location.hash = "#2";
+ window.location.reload();
+ });
+ } else {
+ this.setState({current: 2, has_answered_expose: true}, () => {
+ this.onStepChange(2);
+ });
+ }
+ });
+ }
+
+ onStepChange(n){
+ this.setState({current: n});
+ if(n === 2){
+ this.fetchDependencies();
+ }
+ }
+
+ fetchDependencies() {
+ this.props.summaryCall().then((deps) => {
+ this.setState({deps: deps});
+ });
+ }
+
+ render() {
+ const hideMenu = ;
+ if(this.state.current === 0) {
+ return (
+
+
+ Step 1/2: Secure your instance
+
+
+
+
Create your admin password:
+
+
+
+ {hideMenu}
+
+ );
+ } else if(this.state.current === 1) {
+ return (
+
+
+ Step 2/2: Expose your instance to the internet ?
+
+
+
+
+
+ Your instance is available at
{this.state.redirect_uri}.
+ You will be redirected in
window.location.href = this.state.redirect_uri} /> seconds
+
+
+
+
+
+
+
+ {this.state.working_message}
+
+
+
+ {hideMenu}
+
+ );
+ } else if(this.state.current === 2) {
+ return (
+
+
+ Summary
+
+
+
+
+
+ Verifying
+
+
+ {
+ this.state.deps.map((dep, idx) => {
+ return (
+
+ {dep.pass ? dep.name_success : dep.name_failure}{dep.pass ? null : ": " + dep.message}
+
+ );
+ })
+ }
+
+
+
+
+ );
+ }
+ return null;
+ }
+}
+
+const FormStage = (props) => {
+ return (
+
+ { props.navleft === true ? props.onStepChange(props.current - 1)}/> : null}
+ { props.children }
+ { props.navright === true ? props.onStepChange(props.current + 1)}/> : null }
+
+ );
+};
+
+class Countdown extends React.Component {
+ constructor(props){
+ super(props);
+ this.state = { count: props.max };
+ }
+
+ componentDidMount(){
+ this.timeout = window.setInterval(() => {
+ if(this.state.count - 1 >= 0){
+ this.setState({count: this.state.count - 1}, () => {
+ if(this.state.count === 0) this.props.onZero();
+ });
+ }
+ }, 1000);
+ }
+
+ componentWillUnmount(){
+ window.clearInterval(this.timeout);
+ }
+
+ render(){
+ return(
+ {this.state.count}
+ );
+ }
+}
+
+function objectGet(obj, paths){
+ let value = obj;
+ for(let i=0; i