mirror of
https://github.com/mickael-kerjean/filestash.git
synced 2025-11-02 03:54:59 +08:00
improvement (login): new backend API to generate login form in the frontend
This commit is contained in:
@ -1,5 +1,4 @@
|
|||||||
/* latin-ext */
|
/* latin-ext */
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Source Code Pro';
|
font-family: 'Source Code Pro';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
@ -8,9 +7,7 @@
|
|||||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* latin */
|
/* latin */
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Source Code Pro';
|
font-family: 'Source Code Pro';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
@ -19,9 +16,7 @@
|
|||||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* latin-ext */
|
/* latin-ext */
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Source Code Pro';
|
font-family: 'Source Code Pro';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
@ -30,9 +25,7 @@
|
|||||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* latin */
|
/* latin */
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Source Code Pro';
|
font-family: 'Source Code Pro';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
@ -77,6 +70,10 @@ html {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.center{
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Input, Select, Enabler } from './';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import { Input, Textarea, Select, Enabler } from './';
|
||||||
import { FormObjToJSON, bcrypt_password, format } from '../helpers/';
|
import { FormObjToJSON, bcrypt_password, format } from '../helpers/';
|
||||||
|
|
||||||
import "./formbuilder.scss";
|
import "./formbuilder.scss";
|
||||||
@ -21,6 +23,15 @@ export class FormBuilder extends React.Component {
|
|||||||
|
|
||||||
if(Array.isArray(struct)) return null;
|
if(Array.isArray(struct)) return null;
|
||||||
else if(isALeaf(struct) === false){
|
else if(isALeaf(struct) === false){
|
||||||
|
const [normal, advanced] = function(s){
|
||||||
|
let _normal = [];
|
||||||
|
let _advanced = [];
|
||||||
|
for (let key in s){
|
||||||
|
const tmp = {key: key, data: s[key]};
|
||||||
|
'id' in s[key] ? _advanced.push(tmp) : _normal.push(tmp);
|
||||||
|
}
|
||||||
|
return [_normal, _advanced];
|
||||||
|
}(struct);
|
||||||
if(level <= 1){
|
if(level <= 1){
|
||||||
return (
|
return (
|
||||||
<div className="formbuilder">
|
<div className="formbuilder">
|
||||||
@ -28,14 +39,25 @@ export class FormBuilder extends React.Component {
|
|||||||
key ? <h2 className="no-select">{ format(key) }</h2> : ""
|
key ? <h2 className="no-select">{ format(key) }</h2> : ""
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Object.keys(struct).map((key, index) => {
|
normal.map((s, index) => {
|
||||||
return (
|
return (
|
||||||
<div key={key+"-"+index}>
|
<div key={s.key+"-"+index}>
|
||||||
{ this.section(struct[key], key, level + 1) }
|
{ this.section(s.data, s.key, level + 1) }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
<div className="advanced_form">
|
||||||
|
{
|
||||||
|
advanced.map((s, index) => {
|
||||||
|
return (
|
||||||
|
<div key={s.key+"-"+index}>
|
||||||
|
{ this.section(s.data, s.key, level + 1) }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -78,6 +100,7 @@ export class FormBuilder extends React.Component {
|
|||||||
FormObjToJSON(this.props.form)
|
FormObjToJSON(this.props.form)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return ( <FormElement render={this.props.render} onChange={onChange.bind(this)} {...id} params={struct} target={target} name={ format(struct.label) } /> );
|
return ( <FormElement render={this.props.render} onChange={onChange.bind(this)} {...id} params={struct} target={target} name={ format(struct.label) } /> );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +113,7 @@ export class FormBuilder extends React.Component {
|
|||||||
const FormElement = (props) => {
|
const FormElement = (props) => {
|
||||||
const id = props.id !== undefined ? {id: props.id} : {};
|
const id = props.id !== undefined ? {id: props.id} : {};
|
||||||
let struct = props.params;
|
let struct = props.params;
|
||||||
let $input = ( <Input onChange={(e) => props.onChange(e.target.value)} {...id} name={props.name} type="text" defaultValue={struct.value} placeholder={struct.placeholder} /> );
|
let $input = ( <Input onChange={(e) => props.onChange(e.target.value)} {...id} name={struct.label} type="text" defaultValue={struct.value} placeholder={struct.placeholder} /> );
|
||||||
switch(props.params["type"]){
|
switch(props.params["type"]){
|
||||||
case "text":
|
case "text":
|
||||||
const onTextChange = (value) => {
|
const onTextChange = (value) => {
|
||||||
@ -99,14 +122,14 @@ const FormElement = (props) => {
|
|||||||
}
|
}
|
||||||
props.onChange(value);
|
props.onChange(value);
|
||||||
};
|
};
|
||||||
$input = ( <Input onChange={(e) => onTextChange(e.target.value)} {...id} name={props.name} type="text" value={struct.value || ""} placeholder={struct.placeholder}/> );
|
$input = ( <Input onChange={(e) => onTextChange(e.target.value)} {...id} name={struct.label} type="text" value={struct.value || ""} placeholder={struct.placeholder} readOnly={struct.readonly}/> );
|
||||||
break;
|
break;
|
||||||
case "number":
|
case "number":
|
||||||
const onNumberChange = (value) => {
|
const onNumberChange = (value) => {
|
||||||
value = value === "" ? null : parseInt(value);
|
value = value === "" ? null : parseInt(value);
|
||||||
props.onChange(value);
|
props.onChange(value);
|
||||||
};
|
};
|
||||||
$input = ( <Input onChange={(e) => onNumberChange(e.target.value)} {...id} name={props.name} type="number" value={struct.value || ""} placeholder={struct.placeholder} /> );
|
$input = ( <Input onChange={(e) => onNumberChange(e.target.value)} {...id} name={struct.label} type="number" value={struct.value || ""} placeholder={struct.placeholder} /> );
|
||||||
break;
|
break;
|
||||||
case "password":
|
case "password":
|
||||||
const onPasswordChange = (value) => {
|
const onPasswordChange = (value) => {
|
||||||
@ -115,56 +138,53 @@ const FormElement = (props) => {
|
|||||||
}
|
}
|
||||||
props.onChange(value);
|
props.onChange(value);
|
||||||
};
|
};
|
||||||
$input = ( <Input onChange={(e) => onPasswordChange(e.target.value)} {...id} name={props.name} type="password" value={struct.value || ""} placeholder={struct.placeholder} /> );
|
$input = ( <Input onChange={(e) => onPasswordChange(e.target.value)} {...id} name={struct.label} type="password" value={struct.value || ""} placeholder={struct.placeholder} /> );
|
||||||
|
break;
|
||||||
|
case "long_password":
|
||||||
|
const onLongPasswordChange = (value) => {
|
||||||
|
if(value === ""){
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
props.onChange(value);
|
||||||
|
};
|
||||||
|
$input = (
|
||||||
|
<Textarea {...id} disabledEnter={true} value={struct.value || ""} onChange={(e) => onLongPasswordChange(e.target.value)} type="text" rows="1" name={struct.label} placeholder={struct.placeholder} autoComplete="new-password" />
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case "bcrypt":
|
case "bcrypt":
|
||||||
const onBcryptChange = (value) => {
|
const onBcryptChange = (value) => {
|
||||||
if(value === ""){
|
if(value === ""){
|
||||||
return props.onChange(null);
|
return props.onChange(null);
|
||||||
}
|
}
|
||||||
bcrypt_password(value).then((hash) => {
|
return bcrypt_password(value).then((hash) => {
|
||||||
props.onChange(hash);
|
props.onChange(hash);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$input = ( <Input onChange={(e) => onBcryptChange(e.target.value)} {...id} name={props.name} type="password" defaultValue={struct.value || ""} placeholder={struct.placeholder} /> );
|
$input = ( <Input onChange={(e) => onBcryptChange(e.target.value)} {...id} name={struct.label} type="password" defaultValue={struct.value || ""} placeholder={struct.placeholder} /> );
|
||||||
break;
|
break;
|
||||||
case "hidden":
|
case "hidden":
|
||||||
$input = ( <Input name={props.name} type="hidden" defaultValue={struct.value} /> );
|
$input = ( <Input name={struct.label} type="hidden" defaultValue={struct.value} /> );
|
||||||
break;
|
break;
|
||||||
case "boolean":
|
case "boolean":
|
||||||
$input = ( <Input onChange={(e) => props.onChange(e.target.checked)} {...id} name={props.name} type="checkbox" checked={struct.value === null ? !!struct.default : struct.value} /> );
|
$input = ( <Input onChange={(e) => props.onChange(e.target.checked)} {...id} name={struct.label} type="checkbox" checked={struct.value === null ? !!struct.default : struct.value} /> );
|
||||||
break;
|
break;
|
||||||
case "select":
|
case "select":
|
||||||
$input = ( <Select onChange={(e) => props.onChange(e.target.value)} {...id} name={props.name} choices={struct.options} value={struct.value === null ? struct.default : struct.value} placeholder={struct.placeholder} />);
|
$input = ( <Select onChange={(e) => props.onChange(e.target.value)} {...id} name={struct.label} choices={struct.options} value={struct.value === null ? struct.default : struct.value} placeholder={struct.placeholder} />);
|
||||||
break;
|
break;
|
||||||
case "enable":
|
case "enable":
|
||||||
$input = ( <Enabler onChange={(e) => props.onChange(e.target.checked)} {...id} name={props.name} target={props.target} defaultValue={struct.value === null ? struct.default : struct.value} /> );
|
$input = ( <Enabler onChange={(e) => props.onChange(e.target.checked)} {...id} name={struct.label} target={props.target} defaultValue={struct.value === null ? struct.default : struct.value} /> );
|
||||||
break;
|
break;
|
||||||
case "image":
|
case "image":
|
||||||
$input = ( <img {...id} src={props.value} /> );
|
$input = ( <img {...id} src={struct.value} /> );
|
||||||
|
break;
|
||||||
|
case "oauth2":
|
||||||
|
$input = null;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(props.render){
|
|
||||||
return props.render($input, props, struct, props.onChange.bind(null, null));
|
return props.render($input, props, struct, props.onChange.bind(null, null));
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
FormElement.PropTypes = {
|
||||||
<label className={"no-select input_type_" + props.params["type"]}>
|
render: PropTypes.func.isRequired
|
||||||
<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>) : null }
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -9,8 +9,8 @@
|
|||||||
font-size: 0.95em;
|
font-size: 0.95em;
|
||||||
}
|
}
|
||||||
|
|
||||||
input::placeholder{
|
input::placeholder, textarea::placeholder{
|
||||||
opacity: 0.5;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
label.input_type_hidden{
|
label.input_type_hidden{
|
||||||
@ -25,4 +25,9 @@
|
|||||||
padding: 0 15px;
|
padding: 0 15px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img{
|
||||||
|
max-height: 111px;
|
||||||
|
border: 9px solid rgba(0,0,0,0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ export { Input, Select, Enabler } from './input';
|
|||||||
export { Textarea } from './textarea';
|
export { Textarea } from './textarea';
|
||||||
export { Button } from './button';
|
export { Button } from './button';
|
||||||
export { Container } from './container';
|
export { Container } from './container';
|
||||||
export { NgIf } from './ngif';
|
export { NgIf, NgShow } from './ngif';
|
||||||
export { Card } from './card';
|
export { Card } from './card';
|
||||||
export { Loader } from './loader';
|
export { Loader } from './loader';
|
||||||
export { Fab } from './fab';
|
export { Fab } from './fab';
|
||||||
|
|||||||
@ -13,8 +13,6 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
|
|
||||||
|
|
||||||
border-bottom: 2px solid rgba(70, 99, 114, 0.1);
|
border-bottom: 2px solid rgba(70, 99, 114, 0.1);
|
||||||
transition: border-color 0.2s ease-out;
|
transition: border-color 0.2s ease-out;
|
||||||
&:focus{
|
&:focus{
|
||||||
@ -22,6 +20,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input.component_input[readonly], textarea.component_textarea[readonly]{
|
||||||
|
border-bottom-style: dashed;
|
||||||
|
cursor: not-allowed;
|
||||||
|
font-style: italic;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
.component_select{
|
.component_select{
|
||||||
background: inherit;
|
background: inherit;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
|
|||||||
@ -27,3 +27,35 @@ NgIf.propTypes = {
|
|||||||
cond: PropTypes.bool.isRequired,
|
cond: PropTypes.bool.isRequired,
|
||||||
type: PropTypes.string
|
type: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export class NgShow extends React.Component {
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let clean_prop = Object.assign({}, this.props);
|
||||||
|
delete clean_prop.cond;
|
||||||
|
delete clean_prop.children;
|
||||||
|
delete clean_prop.type;
|
||||||
|
if(this.props.cond){
|
||||||
|
if(this.props.type === "inline"){
|
||||||
|
return <span {...clean_prop}>{this.props.children}</span>;
|
||||||
|
}else{
|
||||||
|
return <div {...clean_prop}>{this.props.children}</div>;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
return (
|
||||||
|
<div style={{display: "none"}}>
|
||||||
|
{this.props.children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NgShow.propTypes = {
|
||||||
|
cond: PropTypes.bool.isRequired,
|
||||||
|
type: PropTypes.string
|
||||||
|
};
|
||||||
|
|||||||
@ -10,10 +10,14 @@ export class Textarea extends React.Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
let className = "component_textarea";
|
let className = "component_textarea";
|
||||||
|
if(/Firefox/.test(navigator.userAgent)){
|
||||||
|
className += " firefox";
|
||||||
|
}
|
||||||
if((this.refs.el !== undefined && this.refs.el.value.length > 0) || (this.props.value !== undefined && this.props.value.length > 0)){
|
if((this.refs.el !== undefined && this.refs.el.value.length > 0) || (this.props.value !== undefined && this.props.value.length > 0)){
|
||||||
className += " hasText";
|
className += " hasText";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const disabledEnter = (e) => {
|
const disabledEnter = (e) => {
|
||||||
if(e.key === "Enter" && e.shiftKey === false){
|
if(e.key === "Enter" && e.shiftKey === false){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: password;
|
font-family: 'text-security-disc';
|
||||||
src: url('textarea.woff');
|
src: url('textarea.woff') format('woff2');
|
||||||
}
|
}
|
||||||
|
|
||||||
.component_textarea{
|
.component_textarea{
|
||||||
@ -20,14 +20,22 @@
|
|||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
max-height: 31px; /* Firefox was doing some weird things */
|
min-height: 30px;
|
||||||
|
max-height: 30px;
|
||||||
|
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
&[name="password"]{
|
&[name="password"]{
|
||||||
&.hasText{
|
resize: none;
|
||||||
font-family: 'password';
|
|
||||||
}
|
|
||||||
-webkit-text-security:disc!important;
|
-webkit-text-security:disc!important;
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-khtml-user-select: none;
|
||||||
|
-moz-user-select: moz-none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
&.firefox.hasText{
|
||||||
|
font-family: 'text-security-disc';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
border-bottom: 2px solid rgba(70, 99, 114, 0.1);
|
border-bottom: 2px solid rgba(70, 99, 114, 0.1);
|
||||||
|
|||||||
Binary file not shown.
@ -1,4 +1,4 @@
|
|||||||
export { URL_HOME, goToHome, URL_FILES, goToFiles, URL_VIEWER, goToViewer, URL_LOGIN, goToLogin, URL_LOGOUT, goToLogout } from './navigate';
|
export { URL_HOME, goToHome, URL_FILES, goToFiles, URL_VIEWER, goToViewer, URL_LOGIN, goToLogin, URL_LOGOUT, goToLogout, urlParams } from './navigate';
|
||||||
export { opener } from './mimetype';
|
export { opener } from './mimetype';
|
||||||
export { debounce, throttle } from './backpressure';
|
export { debounce, throttle } from './backpressure';
|
||||||
export { encrypt, decrypt, bcrypt_password } from './crypto';
|
export { encrypt, decrypt, bcrypt_password } from './crypto';
|
||||||
|
|||||||
@ -40,3 +40,20 @@ function encode_path(path){
|
|||||||
export function prepare(path){
|
export function prepare(path){
|
||||||
return encodeURIComponent(decodeURIComponent(path)); // to send our url correctly without using directly '/'
|
return encodeURIComponent(decodeURIComponent(path)); // to send our url correctly without using directly '/'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function urlParams() {
|
||||||
|
let p = "";
|
||||||
|
if(window.location.hash){
|
||||||
|
p += window.location.hash.replace(/^\#/, "");
|
||||||
|
}
|
||||||
|
if(window.location.search){
|
||||||
|
if(p !== "") p += "&";
|
||||||
|
p += window.location.search.replace(/^\?/, "");
|
||||||
|
}
|
||||||
|
return p.split("&").reduce((mem, chunk) => {
|
||||||
|
const d = chunk.split("=");
|
||||||
|
if(d.length !== 2) return mem;
|
||||||
|
mem[decodeURIComponent(d[0])] = decodeURIComponent(d[1]);
|
||||||
|
return mem;
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
|||||||
@ -7,22 +7,9 @@ class SessionManager{
|
|||||||
.then(data => data.result);
|
.then(data => data.result);
|
||||||
}
|
}
|
||||||
|
|
||||||
url(type){
|
oauth2(url){
|
||||||
if(type === 'dropbox'){
|
|
||||||
let url = '/api/session/auth/dropbox';
|
|
||||||
return http_get(url)
|
return http_get(url)
|
||||||
.then(data => data.result);
|
.then(data => data.result);
|
||||||
}else if(type === 'gdrive'){
|
|
||||||
let url = '/api/session/auth/gdrive';
|
|
||||||
return http_get(url)
|
|
||||||
.then(data => data.result);
|
|
||||||
}else if(type === 'custombackend'){
|
|
||||||
let url = '/api/session/auth/custombackend';
|
|
||||||
return http_get(url)
|
|
||||||
.then(data => data.result);
|
|
||||||
}else{
|
|
||||||
return Promise.reject({message: 'no authorization backend', code: 'UNKNOWN_PROVIDER'});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
authenticate(params){
|
authenticate(params){
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FormBuilder } from '../../components/';
|
import { FormBuilder } from '../../components/';
|
||||||
import { Config } from '../../model/';
|
import { Config } from '../../model/';
|
||||||
|
import { format } from '../../helpers';
|
||||||
|
|
||||||
export class ConfigPage extends React.Component {
|
export class ConfigPage extends React.Component {
|
||||||
constructor(props){
|
constructor(props){
|
||||||
@ -32,7 +33,7 @@ export class ConfigPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onChange(form){
|
onChange(form){
|
||||||
form.connections = window.CONFIG.connections
|
form.connections = window.CONFIG.connections;
|
||||||
Config.save(form);
|
Config.save(form);
|
||||||
this.setState({refresh: Math.random()});
|
this.setState({refresh: Math.random()});
|
||||||
}
|
}
|
||||||
@ -40,7 +41,26 @@ export class ConfigPage extends React.Component {
|
|||||||
render(){
|
render(){
|
||||||
return (
|
return (
|
||||||
<form className="sticky">
|
<form className="sticky">
|
||||||
<FormBuilder form={this.state.form} onChange={this.onChange.bind(this)} />
|
<FormBuilder form={this.state.form} onChange={this.onChange.bind(this)} render={ ($input, props, struct, onChange) => {
|
||||||
|
return (
|
||||||
|
<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>) : null }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
}}/>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,7 +23,7 @@ export class DashboardPage extends React.Component {
|
|||||||
let [backend, config] = data;
|
let [backend, config] = data;
|
||||||
this.setState({
|
this.setState({
|
||||||
backend_available: backend,
|
backend_available: backend,
|
||||||
backend_enabled: window.CONFIG.connections.map((conn) => {
|
backend_enabled: window.CONFIG["connections"].map((conn) => {
|
||||||
return createFormBackend(backend, conn);
|
return createFormBackend(backend, conn);
|
||||||
}),
|
}),
|
||||||
config: config
|
config: config
|
||||||
@ -126,6 +126,8 @@ export class DashboardPage extends React.Component {
|
|||||||
);
|
);
|
||||||
if(struct.label === "label"){
|
if(struct.label === "label"){
|
||||||
$checkbox = null;
|
$checkbox = null;
|
||||||
|
} else if(struct.readonly === true) {
|
||||||
|
$checkbox = null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<label className={"no-select input_type_" + props.params["type"]}>
|
<label className={"no-select input_type_" + props.params["type"]}>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FormBuilder, Loader, Button, Icon } from '../../components/';
|
import { FormBuilder, Loader, Button, Icon } from '../../components/';
|
||||||
import { Config, Log } from '../../model/';
|
import { Config, Log } from '../../model/';
|
||||||
import { FormObjToJSON, notify } from '../../helpers/';
|
import { FormObjToJSON, notify, format } from '../../helpers/';
|
||||||
|
|
||||||
import "./logger.scss";
|
import "./logger.scss";
|
||||||
|
|
||||||
@ -48,12 +48,34 @@ export class LogPage extends React.Component {
|
|||||||
let tmp = "access_";
|
let tmp = "access_";
|
||||||
tmp += new Date().toISOString().substring(0,10).replace(/-/g, "");
|
tmp += new Date().toISOString().substring(0,10).replace(/-/g, "");
|
||||||
tmp += ".log";
|
tmp += ".log";
|
||||||
}
|
};
|
||||||
return (
|
return (
|
||||||
<div className="component_logpage">
|
<div className="component_logpage">
|
||||||
<h2>Logging { this.state.loading === true ? <Icon style={{height: '40px'}} name="loading"/> : null}</h2>
|
<h2>Logging { this.state.loading === true ? <Icon style={{height: '40px'}} name="loading"/> : null}</h2>
|
||||||
<div style={{minHeight: '150px'}}>
|
<div style={{minHeight: '150px'}}>
|
||||||
<FormBuilder form={this.state.form} onChange={this.onChange.bind(this)} />
|
<FormBuilder form={this.state.form} onChange={this.onChange.bind(this)}
|
||||||
|
render={ ($input, props, struct, onChange) => {
|
||||||
|
return (
|
||||||
|
<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>) : null
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
}} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<pre style={{height: '350px'}} ref="$log">
|
<pre style={{height: '350px'}} ref="$log">
|
||||||
|
|||||||
@ -3,9 +3,9 @@ import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
|
|||||||
|
|
||||||
import './connectpage.scss';
|
import './connectpage.scss';
|
||||||
import { Session } from '../model/';
|
import { Session } from '../model/';
|
||||||
import { Container, NgIf, Loader, Notification } from '../components/';
|
import { Container, NgIf, NgShow, Loader, Notification } from '../components/';
|
||||||
import { ForkMe, RememberMe, Credentials, Form } from './connectpage/';
|
import { ForkMe, RememberMe, Credentials, Form } from './connectpage/';
|
||||||
import { cache, notify } from '../helpers/';
|
import { cache, notify, urlParams } from '../helpers/';
|
||||||
|
|
||||||
import { Alert } from '../components/';
|
import { Alert } from '../components/';
|
||||||
|
|
||||||
@ -15,35 +15,27 @@ export class ConnectPage extends React.Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
credentials: {},
|
credentials: {},
|
||||||
remember_me: window.localStorage.hasOwnProperty('credentials') ? true : false,
|
remember_me: window.localStorage.hasOwnProperty('credentials') ? true : false,
|
||||||
loading: false,
|
loading: true,
|
||||||
doing_a_third_party_login: false
|
doing_a_third_party_login: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount(){
|
componentWillMount(){
|
||||||
function getParam(name) {
|
const urlData = urlParams();
|
||||||
const regex = new RegExp("[?&#]" + name.replace(/[\[\]]/g, "\\$&") + "(=([^&#]*)|&|#|$)");
|
if(Object.keys(urlData).length === 0){
|
||||||
const results = regex.exec(window.location.href);
|
return;
|
||||||
if (!results) return null;
|
|
||||||
if (!results[2]) return '';
|
|
||||||
return decodeURIComponent(results[2].replace(/\+/g, " "));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = getParam('state');
|
if(!urlData.type){
|
||||||
if(state === "dropbox"){
|
urlData.type = urlData.state;
|
||||||
this.setState({doing_a_third_party_login: true});
|
|
||||||
this.authenticate({bearer: getParam('access_token'), type: 'dropbox'});
|
|
||||||
}else if(state === "googledrive"){
|
|
||||||
this.setState({doing_a_third_party_login: true});
|
|
||||||
this.authenticate({code: getParam('code'), type: 'gdrive'});
|
|
||||||
}else if(state === "custombackend"){
|
|
||||||
this.setState({doing_a_third_party_login: true});
|
|
||||||
this.authenticate({code: getParam('code'), type: 'custombackend'});
|
|
||||||
}
|
}
|
||||||
|
this.setState({
|
||||||
|
doing_a_third_party_login: true,
|
||||||
|
loading: true
|
||||||
|
}, () => this.authenticate(urlData));
|
||||||
}
|
}
|
||||||
|
|
||||||
authenticate(params){
|
authenticate(params){
|
||||||
this.setState({loading: true});
|
|
||||||
Session.authenticate(params)
|
Session.authenticate(params)
|
||||||
.then(Session.currentUser)
|
.then(Session.currentUser)
|
||||||
.then((user) => {
|
.then((user) => {
|
||||||
@ -64,37 +56,18 @@ export class ConnectPage extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
initiateAuthToThirdParty(source){
|
|
||||||
if(source === 'dropbox'){
|
|
||||||
this.setState({loading: true});
|
|
||||||
Session.url('dropbox').then((url) => {
|
|
||||||
window.location.href = url;
|
|
||||||
}).catch((err) => {
|
|
||||||
this.setState({loading: false});
|
|
||||||
notify.send(err, 'error');
|
|
||||||
});
|
|
||||||
}else if(source === 'google'){
|
|
||||||
this.setState({loading: true});
|
|
||||||
Session.url('gdrive').then((url) => {
|
|
||||||
window.location.href = url;
|
|
||||||
}).catch((err) => {
|
|
||||||
this.setState({loading: false});
|
|
||||||
notify.send(err, 'error');
|
|
||||||
});
|
|
||||||
}else if(source === 'custombackend'){
|
|
||||||
Session.url('custombackend').then((url) => {
|
|
||||||
window.location.href = url;
|
|
||||||
}).catch((err) => {
|
|
||||||
this.setState({loading: false});
|
|
||||||
notify.send(err, 'error');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onFormSubmit(data, credentials){
|
onFormSubmit(data, credentials){
|
||||||
this.setState({credentials: credentials}, () => {
|
if('oauth2' in data){
|
||||||
this.authenticate(data);
|
this.setState({loading: true});
|
||||||
|
Session.oauth2(data.oauth2).then((url) => {
|
||||||
|
window.location.href = url;
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
credentials: credentials,
|
||||||
|
loading: true
|
||||||
|
}, () => this.authenticate(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
setRemember(state){
|
setRemember(state){
|
||||||
@ -105,6 +78,12 @@ export class ConnectPage extends React.Component {
|
|||||||
this.setState({credentials: creds});
|
this.setState({credentials: creds});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setLoading(value){
|
||||||
|
if(this.state.doing_a_third_party_login !== true){
|
||||||
|
this.setState({loading: value});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="component_page_connect">
|
<div className="component_page_connect">
|
||||||
@ -115,16 +94,16 @@ export class ConnectPage extends React.Component {
|
|||||||
<NgIf cond={this.state.loading === true}>
|
<NgIf cond={this.state.loading === true}>
|
||||||
<Loader/>
|
<Loader/>
|
||||||
</NgIf>
|
</NgIf>
|
||||||
<NgIf cond={this.state.loading === false}>
|
<NgShow cond={this.state.loading === false}>
|
||||||
<ReactCSSTransitionGroup transitionName="form" transitionLeave={false} transitionEnter={false} transitionAppear={true} transitionAppearTimeout={500}>
|
<ReactCSSTransitionGroup transitionName="form" transitionLeave={false} transitionEnter={false} transitionAppear={true} transitionAppearTimeout={500}>
|
||||||
<Form credentials={this.state.credentials}
|
<Form credentials={this.state.credentials}
|
||||||
onThirdPartyLogin={this.initiateAuthToThirdParty.bind(this)}
|
onLoadingChange={this.setLoading.bind(this)}
|
||||||
onSubmit={this.onFormSubmit.bind(this)} />
|
onSubmit={this.onFormSubmit.bind(this)} />
|
||||||
</ReactCSSTransitionGroup>
|
</ReactCSSTransitionGroup>
|
||||||
<ReactCSSTransitionGroup transitionName="remember" transitionLeave={false} transitionEnter={false} transitionAppear={true} transitionAppearTimeout={5000}>
|
<ReactCSSTransitionGroup transitionName="remember" transitionLeave={false} transitionEnter={false} transitionAppear={true} transitionAppearTimeout={5000}>
|
||||||
<RememberMe state={this.state.remember_me} onChange={this.setRemember.bind(this)}/>
|
<RememberMe state={this.state.remember_me} onChange={this.setRemember.bind(this)}/>
|
||||||
</ReactCSSTransitionGroup>
|
</ReactCSSTransitionGroup>
|
||||||
</NgIf>
|
</NgShow>
|
||||||
<NgIf cond={this.state.doing_a_third_party_login === false}>
|
<NgIf cond={this.state.doing_a_third_party_login === false}>
|
||||||
<Credentials remember_me={this.state.remember_me}
|
<Credentials remember_me={this.state.remember_me}
|
||||||
onRememberMeChange={this.setRemember.bind(this)}
|
onRememberMeChange={this.setRemember.bind(this)}
|
||||||
|
|||||||
@ -5,13 +5,14 @@ import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
|
|||||||
import { ModalPrompt } from '../../components/';
|
import { ModalPrompt } from '../../components/';
|
||||||
import { encrypt, decrypt, memory, prompt } from '../../helpers/';
|
import { encrypt, decrypt, memory, prompt } from '../../helpers/';
|
||||||
|
|
||||||
|
const CREDENTIALS_CACHE = "credentials",
|
||||||
|
CREDENTIALS_KEY = "credentials_key";
|
||||||
|
|
||||||
export class Credentials extends React.Component {
|
export class Credentials extends React.Component {
|
||||||
constructor(props){
|
constructor(props){
|
||||||
super(props);
|
super(props);
|
||||||
const key = memory.get('credentials_key') || ''; // we use a clojure for the "key" because we
|
const key = memory.get(CREDENTIALS_KEY) || ''; // we use a clojure for the "key" because we require control
|
||||||
// want to persist it in memory, not just in the
|
// without the influence of the react component lifecycle
|
||||||
// state which is kill whenever react decide
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
key: key || '',
|
key: key || '',
|
||||||
message: '',
|
message: '',
|
||||||
@ -20,7 +21,7 @@ export class Credentials extends React.Component {
|
|||||||
|
|
||||||
if(this.props.remember_me === true){
|
if(this.props.remember_me === true){
|
||||||
if(key === ""){
|
if(key === ""){
|
||||||
let raw = window.localStorage.hasOwnProperty('credentials');
|
let raw = window.localStorage.hasOwnProperty(CREDENTIALS_CACHE);
|
||||||
if(raw){
|
if(raw){
|
||||||
this.promptForExistingPassword();
|
this.promptForExistingPassword();
|
||||||
}else{
|
}else{
|
||||||
@ -33,7 +34,10 @@ export class Credentials extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(new_props){
|
componentWillReceiveProps(new_props){
|
||||||
if(new_props.remember_me === false){
|
if(window.CONFIG["remember_me"] === false){
|
||||||
|
window.localStorage.clear();
|
||||||
|
return;
|
||||||
|
} else if(new_props.remember_me === false){
|
||||||
window.localStorage.clear();
|
window.localStorage.clear();
|
||||||
} else if(new_props.remember_me === true){
|
} else if(new_props.remember_me === true){
|
||||||
this.saveCreds(new_props.credentials);
|
this.saveCreds(new_props.credentials);
|
||||||
@ -42,7 +46,7 @@ export class Credentials extends React.Component {
|
|||||||
if(new_props.remember_me === true && this.props.remember_me === false){
|
if(new_props.remember_me === true && this.props.remember_me === false){
|
||||||
this.promptForNewPassword();
|
this.promptForNewPassword();
|
||||||
}else if(new_props.remember_me === false && this.props.remember_me === true){
|
}else if(new_props.remember_me === false && this.props.remember_me === true){
|
||||||
memory.set('credentials_key', '');
|
memory.set(CREDENTIALS_KEY, '');
|
||||||
this.setState({key: ''});
|
this.setState({key: ''});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,11 +57,11 @@ export class Credentials extends React.Component {
|
|||||||
(key) => {
|
(key) => {
|
||||||
if(!key.trim()) return Promise.reject("Password can\'t be empty");
|
if(!key.trim()) return Promise.reject("Password can\'t be empty");
|
||||||
this.setState({key: key});
|
this.setState({key: key});
|
||||||
memory.set('credentials_key', key);
|
memory.set(CREDENTIALS_KEY, key);
|
||||||
return this.hidrate_credentials(key);
|
return this.hidrate_credentials(key);
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
memory.set('credentials_key', '');
|
memory.set(CREDENTIALS_KEY, '');
|
||||||
this.setState({key: ''});
|
this.setState({key: ''});
|
||||||
},
|
},
|
||||||
'password'
|
'password'
|
||||||
@ -68,7 +72,7 @@ export class Credentials extends React.Component {
|
|||||||
"Pick a Master Password",
|
"Pick a Master Password",
|
||||||
(key) => {
|
(key) => {
|
||||||
if(!key.trim()) return Promise.reject("Password can\'t be empty");
|
if(!key.trim()) return Promise.reject("Password can\'t be empty");
|
||||||
memory.set('credentials_key', key);
|
memory.set(CREDENTIALS_KEY, key);
|
||||||
this.setState({key: key}, () => {
|
this.setState({key: key}, () => {
|
||||||
this.saveCreds(this.props.credentials);
|
this.saveCreds(this.props.credentials);
|
||||||
});
|
});
|
||||||
@ -80,7 +84,7 @@ export class Credentials extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hidrate_credentials(key){
|
hidrate_credentials(key){
|
||||||
let raw = window.localStorage.getItem('credentials');
|
let raw = window.localStorage.getItem(CREDENTIALS_CACHE);
|
||||||
if(raw){
|
if(raw){
|
||||||
try{
|
try{
|
||||||
let credentials = decrypt(raw, key);
|
let credentials = decrypt(raw, key);
|
||||||
@ -96,9 +100,9 @@ export class Credentials extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
saveCreds(creds){
|
saveCreds(creds){
|
||||||
const key = memory.get('credentials_key');
|
const key = memory.get(CREDENTIALS_KEY);
|
||||||
if(key){
|
if(key){
|
||||||
window.localStorage.setItem('credentials', encrypt(creds, key));
|
window.localStorage.setItem(CREDENTIALS_CACHE, encrypt(creds, key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Container, Card, NgIf, Input, Button, Textarea, FormBuilder } from "../../components/";
|
import { Container, Card, NgIf, Input, Button, Textarea, FormBuilder } from "../../components/";
|
||||||
import { gid, settings_get, settings_put, createFormBackend } from "../../helpers/";
|
import { gid, settings_get, settings_put, createFormBackend, FormObjToJSON } from "../../helpers/";
|
||||||
import { Session, Backend } from "../../model/";
|
import { Session, Backend } from "../../model/";
|
||||||
import "./form.scss";
|
import "./form.scss";
|
||||||
import img_drive from "../../assets/img/google-drive.png";
|
import img_drive from "../../assets/img/google-drive.png";
|
||||||
@ -10,25 +10,34 @@ export class Form extends React.Component {
|
|||||||
constructor(props){
|
constructor(props){
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
select: window.CONFIG["connections"].length > 2 ? 2 : 0,
|
select: function(){
|
||||||
backends: JSON.parse(JSON.stringify(window.CONFIG["connections"])).map((backend) => {
|
const connLength = window.CONFIG["connections"].length;
|
||||||
return backend;
|
if(connLength < 4) return 0;
|
||||||
}),
|
else if(connLength < 5) return 1;
|
||||||
dummy: null
|
return 2;
|
||||||
|
}(),
|
||||||
|
backends_enabled: []
|
||||||
};
|
};
|
||||||
|
|
||||||
const select = settings_get("login_tab");
|
const select = settings_get("login_tab");
|
||||||
if(select !== null && select < window.CONFIG["connections"].length){ this.state.select = select; }
|
if(select !== null && select < window.CONFIG["connections"].length){ this.state.select = select; }
|
||||||
this.rerender = this.rerender.bind(this);
|
this.rerender = () => this.setState({_refresh: !this.state._refresh});
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(){
|
componentDidMount(){
|
||||||
window.addEventListener("resize", this.rerender);
|
window.addEventListener("resize", this.rerender);
|
||||||
this.publishState(this.props);
|
Backend.all().then((backend) => {
|
||||||
Backend.all().then((b) => {
|
this.props.onLoadingChange(false);
|
||||||
this.setState({
|
this.setState({
|
||||||
backend_available: b
|
backends_available: backend,
|
||||||
|
backends_enabled: window.CONFIG["connections"].map((conn) => {
|
||||||
|
return createFormBackend(backend, conn);
|
||||||
|
})
|
||||||
|
}, () => {
|
||||||
|
return this.publishState(this.props);
|
||||||
});
|
});
|
||||||
|
}).catch((err) => {
|
||||||
|
this.props.error(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,96 +54,40 @@ export class Form extends React.Component {
|
|||||||
|
|
||||||
publishState(props){
|
publishState(props){
|
||||||
for(let key in props.credentials){
|
for(let key in props.credentials){
|
||||||
this.state.backends = this.state.backends.map((backend) => {
|
this.state.backends_enabled = this.state.backends_enabled.map((backend) => {
|
||||||
if(backend["type"] + "_" + backend["label"] === key){
|
const b = backend[Object.keys(backend)[0]];
|
||||||
backend = props.credentials[key];
|
if(b["type"].value + "_" + b["label"].value === key){
|
||||||
|
for(let k in b){
|
||||||
|
if(props.credentials[key][k]){
|
||||||
|
b[k].value = props.credentials[key][k];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return backend;
|
return backend;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.setState({backends: this.state.backends});
|
this.setState({backends_enabled: this.state.backends_enabled});
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubmit(e){
|
onSubmit(e){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const authData = this.state.backends[this.state.select],
|
const data = () => {
|
||||||
key = authData["type"]+"_"+authData["label"];
|
const tmp = this.state.backends_enabled[this.state.select];
|
||||||
|
return tmp[Object.keys(tmp)[0]];
|
||||||
this.props.credentials[key] = authData;
|
};
|
||||||
this.props.onSubmit(authData, this.props.credentials);
|
const dataToBeSubmitted = JSON.parse(JSON.stringify(FormObjToJSON(data())));
|
||||||
}
|
const key = dataToBeSubmitted["type"] + "_" + dataToBeSubmitted["label"];
|
||||||
|
delete dataToBeSubmitted.image;
|
||||||
onFormUpdate(n, values){
|
delete dataToBeSubmitted.label;
|
||||||
this.state.backends[n] = values;
|
delete dataToBeSubmitted.advanced;
|
||||||
this.setState({backends: this.state.backends});
|
this.props.credentials[key] = dataToBeSubmitted;
|
||||||
}
|
this.props.onSubmit(dataToBeSubmitted, this.props.credentials);
|
||||||
|
|
||||||
redirect(service){
|
|
||||||
this.props.onThirdPartyLogin(service);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onTypeChange(n){
|
onTypeChange(n){
|
||||||
this.setState({select: n});
|
this.setState({select: n});
|
||||||
}
|
}
|
||||||
|
|
||||||
rerender(){
|
|
||||||
this.setState({_refresh: !this.state._refresh});
|
|
||||||
}
|
|
||||||
|
|
||||||
render2() {
|
|
||||||
const _marginTop = () => {
|
|
||||||
let size = 300;
|
|
||||||
const $screen = document.querySelector(".login-form");
|
|
||||||
if($screen) size = $screen.offsetHeight;
|
|
||||||
|
|
||||||
size = Math.round((document.body.offsetHeight - size) / 2);
|
|
||||||
if(size < 0) return 0;
|
|
||||||
if(size > 150) return 150;
|
|
||||||
return size;
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<Card style={{marginTop: _marginTop()+"px"}} className="no-select component_page_connection_form">
|
|
||||||
<NgIf cond={ window.CONFIG["connections"].length > 1 }>
|
|
||||||
<div className={"buttons "+((window.innerWidth < 600) ? "scroll-x" : "")}>
|
|
||||||
{
|
|
||||||
this.state.backends.map((backend, i) => {
|
|
||||||
return (
|
|
||||||
<Button key={"menu-"+i} className={i === this.state.select ? "active primary" : ""} onClick={this.onTypeChange.bind(this, i)}>
|
|
||||||
{backend.label}
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</NgIf>
|
|
||||||
<div>
|
|
||||||
<form onSubmit={this.onSubmit.bind(this)} autoComplete="off" autoCapitalize="off" spellCheck="false" autoCorrect="off">
|
|
||||||
{
|
|
||||||
this.state.backends.map((conn, i) => {
|
|
||||||
console.log(this.state.backend_available);
|
|
||||||
return (
|
|
||||||
<NgIf key={"form-"+i} cond={this.state.select === i}>
|
|
||||||
<FormBuilder form={{"": createFormBackend(this.state.backend_available, conn)}} onChange={() => {this.setState({refresh: !this.state.refresh})}}
|
|
||||||
render={ ($input, props, struct, onChange) => {
|
|
||||||
return (
|
|
||||||
<div style={{width: '100%'}}>
|
|
||||||
{ $input }
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}}/>
|
|
||||||
</NgIf>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
<Button theme="emphasis">CONNECT</Button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// <FormBuilder onChange={() => {}} form={{"": conn}}/>
|
|
||||||
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const _marginTop = () => {
|
const _marginTop = () => {
|
||||||
let size = 300;
|
let size = 300;
|
||||||
@ -146,16 +99,17 @@ export class Form extends React.Component {
|
|||||||
if(size > 150) return 150;
|
if(size > 150) return 150;
|
||||||
return size;
|
return size;
|
||||||
};
|
};
|
||||||
let className = (window.innerWidth < 600) ? "scroll-x" : "";
|
|
||||||
return (
|
return (
|
||||||
<Card style={{marginTop: _marginTop()+"px"}} className="no-select component_page_connection_form">
|
<Card style={{marginTop: _marginTop()+"px"}} className="no-select component_page_connection_form">
|
||||||
<NgIf cond={ CONFIG["connections"].length > 1 }>
|
<NgIf cond={ window.CONFIG["connections"].length > 1 }>
|
||||||
<div className={"buttons "+((window.innerWidth < 600) ? "scroll-x" : "")}>
|
<div className={"buttons "+((window.innerWidth < 600) ? "scroll-x" : "")}>
|
||||||
{
|
{
|
||||||
this.state.backends.map((backend, i) => {
|
this.state.backends_enabled.map((backend, i) => {
|
||||||
|
const key = Object.keys(backend)[0];
|
||||||
return (
|
return (
|
||||||
<Button key={"menu-"+i} className={i === this.state.select ? "active primary" : ""} onClick={this.onTypeChange.bind(this, i)}>
|
<Button key={"menu-"+i} className={i === this.state.select ? "active primary" : ""} onClick={this.onTypeChange.bind(this, i)}>
|
||||||
{backend.label}
|
{ backend[key].label.value }
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
@ -165,415 +119,42 @@ export class Form extends React.Component {
|
|||||||
<div>
|
<div>
|
||||||
<form onSubmit={this.onSubmit.bind(this)} autoComplete="off" autoCapitalize="off" spellCheck="false" autoCorrect="off">
|
<form onSubmit={this.onSubmit.bind(this)} autoComplete="off" autoCapitalize="off" spellCheck="false" autoCorrect="off">
|
||||||
{
|
{
|
||||||
this.state.backends.map((conn, i) => {
|
this.state.backends_enabled.map((form, i) => {
|
||||||
|
const key = Object.keys(form)[0];
|
||||||
|
if(!form[key]) return null;
|
||||||
|
else if(this.state.select !== i) return null;
|
||||||
return (
|
return (
|
||||||
<NgIf key={"form-"+i} cond={this.state.select === i}>
|
<FormBuilder form={form[key]} onChange={this.rerender.bind(this)} key={"form"+i}
|
||||||
<NgIf cond={conn.type === "webdav"}>
|
render={ ($input, props, struct, onChange) => {
|
||||||
<WebDavForm values={conn} config={CONFIG["connections"][i]} onChange={this.onFormUpdate.bind(this, i)} />
|
if(struct.type === "image"){
|
||||||
</NgIf>
|
return (
|
||||||
<NgIf cond={conn.type === "ftp"}>
|
<div className="center">
|
||||||
<FtpForm values={conn} config={CONFIG["connections"][i]} onChange={this.onFormUpdate.bind(this, i)} />
|
{ $input }
|
||||||
</NgIf>
|
</div>
|
||||||
<NgIf cond={conn.type === "sftp"}>
|
);
|
||||||
<SftpForm values={conn} config={CONFIG["connections"][i]} onChange={this.onFormUpdate.bind(this, i)} />
|
} else if(struct.enabled === true){
|
||||||
</NgIf>
|
return null;
|
||||||
<NgIf cond={conn.type === "git"}>
|
} else if(struct.label === "advanced") return (
|
||||||
<GitForm values={conn} config={CONFIG["connections"][i]} onChange={this.onFormUpdate.bind(this, i)} />
|
<label style={{color: "rgba(0,0,0,0.4)"}}>
|
||||||
</NgIf>
|
{ $input }
|
||||||
<NgIf cond={conn.type === "s3"}>
|
advanced
|
||||||
<S3Form values={conn} config={CONFIG["connections"][i]} onChange={this.onFormUpdate.bind(this, i)} />
|
</label>
|
||||||
</NgIf>
|
);
|
||||||
<NgIf cond={conn.type === "dropbox"} className="third-party">
|
return (
|
||||||
<DropboxForm values={conn} config={CONFIG["connections"][i]} onThirdPartyLogin={this.redirect.bind(this)} />
|
<label className={"no-select input_type_" + props.params["type"]}>
|
||||||
</NgIf>
|
<div>
|
||||||
<NgIf cond={conn.type === "gdrive"} className="third-party">
|
{ $input }
|
||||||
<GDriveForm values={conn} config={CONFIG["connections"][i]} onThirdPartyLogin={this.redirect.bind(this)} />
|
</div>
|
||||||
</NgIf>
|
</label>
|
||||||
<NgIf cond={conn.type === "custombackend"} className="third-party">
|
);
|
||||||
<CustomForm values={conn} config={CONFIG["connections"][i]} onThirdPartyLogin={this.redirect.bind(this)} />
|
}} />
|
||||||
</NgIf>
|
|
||||||
</NgIf>
|
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
<Button theme="emphasis">CONNECT</Button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const WebDavForm = formHelper(function(props){
|
|
||||||
const onAdvanced = (value) => {
|
|
||||||
if(value === true){
|
|
||||||
props.values.path = "";
|
|
||||||
}else{
|
|
||||||
delete props.values.path;
|
|
||||||
}
|
|
||||||
props.onChange(props.values);
|
|
||||||
};
|
|
||||||
const is_advanced = props.advanced(props.values.path);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<NgIf cond={props.should_appear("url")}>
|
|
||||||
<Input value={props.values["url"] || ""} onChange={(e) => props.onChange("url", e.target.value)} type={props.input_type("url")} name="url" placeholder="Address*" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("username")}>
|
|
||||||
<Input value={props.values["username"] || ""} onChange={(e) => props.onChange("username", e.target.value)} type={props.input_type("username")} name="username" placeholder="Username" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("password")}>
|
|
||||||
<Input value={props.values["password"] || ""} onChange={(e) => props.onChange("password", e.target.value)} type={props.input_type("password")} name="password" placeholder="Password" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("advanced")}>
|
|
||||||
<label>
|
|
||||||
<input checked={is_advanced} onChange={(e) => onAdvanced(e.target.checked)} type="checkbox" autoComplete="new-password"/> Advanced
|
|
||||||
</label>
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={is_advanced} className="advanced_form">
|
|
||||||
<NgIf cond={props.should_appear("path")}>
|
|
||||||
<Input value={props.values["path"] || ""} onChange={(e) => props.onChange("path", e.target.value)} type={props.input_type("path")} name="path" placeholder="Path" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
</NgIf>
|
|
||||||
<Button theme="emphasis">CONNECT</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const FtpForm = formHelper(function(props){
|
|
||||||
const onAdvanced = (value) => {
|
|
||||||
if(value === true){
|
|
||||||
props.values.path = "";
|
|
||||||
props.values.port = "";
|
|
||||||
}else{
|
|
||||||
delete props.values.path;
|
|
||||||
delete props.values.port;
|
|
||||||
}
|
|
||||||
props.onChange(props.values);
|
|
||||||
};
|
|
||||||
const is_advanced = props.advanced(
|
|
||||||
props.values.path,
|
|
||||||
props.values.port
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<NgIf cond={props.should_appear("hostname")}>
|
|
||||||
<Input value={props.values["hostname"] || ""} onChange={(e) => props.onChange("hostname", e.target.value)} type={props.input_type("hostname")} name="hostname" placeholder="Hostname*" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("username")}>
|
|
||||||
<Input value={props.values["username"] || ""} onChange={(e) => props.onChange("username", e.target.value)} type={props.input_type("username")} name="username" placeholder="Username" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("password")}>
|
|
||||||
<Input value={props.values["password"] || ""} onChange={(e) => props.onChange("password", e.target.value)} type={props.input_type("password")} name="password" placeholder="Password" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("advanced")}>
|
|
||||||
<label>
|
|
||||||
<input checked={is_advanced} onChange={e => onAdvanced(e.target.checked)} type="checkbox" autoComplete="new-password"/> Advanced
|
|
||||||
</label>
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={is_advanced} className="advanced_form">
|
|
||||||
<NgIf cond={props.should_appear("path")}>
|
|
||||||
<Input value={props.values["path"] || ""} onChange={(e) => props.onChange("path", e.target.value)} type={props.input_type("path")} name="path" placeholder="Path" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("port")}>
|
|
||||||
<Input value={props.values["port"] || ""} onChange={(e) => props.onChange("port", e.target.value)} type={props.input_type("port")} name="port" placeholder="Port" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("conn")}>
|
|
||||||
<Input value={props.values["conn"] || ""} onChange={(e) => props.onChange("conn", e.target.value)} type={props.input_type("conn")} name="conn" placeholder="Number of connections" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
</NgIf>
|
|
||||||
<Button type="submit" theme="emphasis">CONNECT</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const SftpForm = formHelper(function(props){
|
|
||||||
const onAdvanced = (value) => {
|
|
||||||
if(value === true){
|
|
||||||
props.values.path = "";
|
|
||||||
props.values.port = "";
|
|
||||||
props.values.passphrase = "";
|
|
||||||
}else{
|
|
||||||
delete props.values.path;
|
|
||||||
delete props.values.port;
|
|
||||||
delete props.values.passphrase;
|
|
||||||
}
|
|
||||||
props.onChange();
|
|
||||||
};
|
|
||||||
const is_advanced = props.advanced(
|
|
||||||
props.values.path,
|
|
||||||
props.values.port,
|
|
||||||
props.values.passphrase
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<NgIf cond={props.should_appear("hostname")}>
|
|
||||||
<Input value={props.values["hostname"] || ""} onChange={(e) => props.onChange("hostname", e.target.value)} value={props.values.hostname || ""} onChange={(e) => props.onChange("hostname", e.target.value)} type={props.input_type("hostname")} name="host" placeholder="Hostname*" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("username")}>
|
|
||||||
<Input value={props.values["username"] || ""} onChange={(e) => props.onChange("username", e.target.value)} type={props.input_type("username")} name="username" placeholder="Username" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("password")}>
|
|
||||||
<Textarea disabledEnter={true} value={props.values["password"] || ""} onChange={(e) => props.onChange("password", e.target.value)} type="text" style={props.input_type("password") === "hidden" ? {visibility: "hidden", position: "absolute"} : {} } rows="1" name="password" placeholder="Password" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("advanced")}>
|
|
||||||
<label>
|
|
||||||
<input checked={is_advanced} onChange={e => onAdvanced(e.target.checked)} type="checkbox" autoComplete="new-password"/> Advanced
|
|
||||||
</label>
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={is_advanced} className="advanced_form">
|
|
||||||
<NgIf cond={props.should_appear("path")}>
|
|
||||||
<Input value={props.values["path"] || ""} onChange={(e) => props.onChange("path", e.target.value)} type={props.input_type("path")} name="path" placeholder="Path" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("port")}>
|
|
||||||
<Input value={props.values["port"] || ""} onChange={(e) => props.onChange("port", e.target.value)} type={props.input_type("port")} name="port" placeholder="Port" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("passphrase")}>
|
|
||||||
<Input value={props.values["passphrase"] || ""} onChange={(e) => props.onChange("passphrase", e.target.value)} type={props.input_type("passphrase")} name="port" placeholder="Passphrase" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
</NgIf>
|
|
||||||
<Button type="submit" theme="emphasis">CONNECT</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
const GitForm = formHelper(function(props){
|
|
||||||
const onAdvanced = (value) => {
|
|
||||||
if(value === true){
|
|
||||||
props.values.path = "";
|
|
||||||
props.values.passphrase = "";
|
|
||||||
props.values.commit = "";
|
|
||||||
props.values.branch = "";
|
|
||||||
props.values.author_email = "";
|
|
||||||
props.values.author_name = "";
|
|
||||||
props.values.committer_email = "";
|
|
||||||
props.values.committer_name = "";
|
|
||||||
}else{
|
|
||||||
delete props.values.path;
|
|
||||||
delete props.values.passphrase;
|
|
||||||
delete props.values.commit;
|
|
||||||
delete props.values.branch;
|
|
||||||
delete props.values.author_email;
|
|
||||||
delete props.values.author_name;
|
|
||||||
delete props.values.committer_email;
|
|
||||||
delete props.values.committer_name;
|
|
||||||
}
|
|
||||||
props.onChange();
|
|
||||||
};
|
|
||||||
const is_advanced = props.advanced(
|
|
||||||
props.values.path,
|
|
||||||
props.values.passphrase,
|
|
||||||
props.values.commit,
|
|
||||||
props.values.branch,
|
|
||||||
props.values.author_email,
|
|
||||||
props.values.author_name,
|
|
||||||
props.values.committer_email,
|
|
||||||
props.values.committer_name
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<NgIf cond={props.should_appear("repo")}>
|
|
||||||
<Input value={props.values["repo"] || ""} onChange={(e) => props.onChange("repo", e.target.value)} type={props.input_type("repo")} name="repo" placeholder="Repository*" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("username")}>
|
|
||||||
<Input value={props.values["username"] || ""} onChange={(e) => props.onChange("username", e.target.value)} type={props.input_type("username")} name="username" placeholder="Username" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("password")}>
|
|
||||||
<Textarea disabledEnter={true} value={props.values["password"] || ""} onChange={(e) => props.onChange("password", e.target.value)} type="text" style={props.input_type("password") === "hidden" ? {visibility: "hidden", position: "absolute"} : {} } rows="1" name="password" placeholder="Password" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<Input name="uid" value={gid()} type="hidden" />
|
|
||||||
<NgIf cond={props.should_appear("advanced")}>
|
|
||||||
<label>
|
|
||||||
<input checked={is_advanced} onChange={(e) => onAdvanced(e.target.checked)} type="checkbox" autoComplete="new-password"/> Advanced
|
|
||||||
</label>
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={is_advanced} className="advanced_form">
|
|
||||||
<NgIf cond={props.should_appear("path")}>
|
|
||||||
<Input value={props.values["path"] || ""} onChange={(e) => props.onChange("path", e.target.value)} type={props.input_type("path")} name="path" placeholder="Path" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("passphrase")}>
|
|
||||||
<Input value={props.values["passphrase"] || ""} onChange={(e) => props.onChange("passphrase", e.target.value)} type={props.input_type("passphrase")} name="passphrase" placeholder="Passphrase" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("commit")}>
|
|
||||||
<Input value={props.values["commit"] || ""} onChange={(e) => props.onChange("commit", e.target.value)} type={props.input_type("commit")} name="commit" placeholder='Commit Format: default to \"{action}({filename}): {path}\"' autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("branch")}>
|
|
||||||
<Input value={props.values["branch"] || ""} onChange={(e) => props.onChange("branch", e.target.value)} type={props.input_type("branch")} name="branch" placeholder='Branch: default to "master"' autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("author_email")}>
|
|
||||||
<Input value={props.values["author_email"] || ""} onChange={(e) => props.onChange("author_email", e.target.value)} type={props.input_type("author_email")} name="author_email" placeholder="Author email" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("author_name")}>
|
|
||||||
<Input value={props.values["author_name"] || ""} onChange={(e) => props.onChange("author_name", e.target.value)} type={props.input_type("author_name")} name="author_name" placeholder="Author name" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("committer_email")}>
|
|
||||||
<Input value={props.values["committer_email"] || ""} onChange={(e) => props.onChange("committer_email", e.target.value)} type={props.input_type("committer_email")} name="committer_email" placeholder="Committer email" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("committer_name")}>
|
|
||||||
<Input value={props.values["committer_name"] || ""} onChange={(e) => props.onChange("committer_name", e.target.value)} type={props.input_type("committer_name")} name="committer_name" placeholder="Committer name" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
</NgIf>
|
|
||||||
<Button type="submit" theme="emphasis">CONNECT</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const S3Form = formHelper(function(props){
|
|
||||||
const onAdvanced = (value) => {
|
|
||||||
if(value === true){
|
|
||||||
props.values.path = "";
|
|
||||||
props.values.endpoint = "";
|
|
||||||
props.values.region = "";
|
|
||||||
props.values.encryption_key = "";
|
|
||||||
}else{
|
|
||||||
delete props.values.path;
|
|
||||||
delete props.values.endpoint;
|
|
||||||
delete props.values.region;
|
|
||||||
delete props.values.encryption_key;
|
|
||||||
}
|
|
||||||
props.onChange();
|
|
||||||
};
|
|
||||||
const is_advanced = props.advanced(
|
|
||||||
props.values.path,
|
|
||||||
props.values.endpoint,
|
|
||||||
props.values.region,
|
|
||||||
props.values.encryption_key
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<NgIf cond={props.should_appear("access_key_id")}>
|
|
||||||
<Input value={props.values["access_key_id"] || ""} onChange={(e) => props.onChange("access_key_id", e.target.value)} value={props.values.access_key_id || ""} onChange={(e) => props.onChange("access_key_id", e.target.value)} type={props.input_type("access_key_id")} name="access_key_id" placeholder="Access Key ID*" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("secret_access_key")}>
|
|
||||||
<Input value={props.values["secret_access_key"] || ""} onChange={(e) => props.onChange("secret_access_key", e.target.value)} type={props.input_type("secret_access_key")} name="secret_access_key" placeholder="Secret Access Key*" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("advanced")}>
|
|
||||||
<label>
|
|
||||||
<input checked={is_advanced} onChange={(e) => onAdvanced(e.target.checked)} type="checkbox" autoComplete="new-password"/> Advanced
|
|
||||||
</label>
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={is_advanced} className="advanced_form">
|
|
||||||
<NgIf cond={props.should_appear("path")}>
|
|
||||||
<Input value={props.values["path"] || ""} onChange={(e) => props.onChange("path", e.target.value)} type={props.input_type("path")} name="path" placeholder="Path" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("encryption_key")}>
|
|
||||||
<Input value={props.values["encryption_key"] || ""} onChange={(e) => props.onChange("encryption_key", e.target.value)} type={props.input_type("encryption_key")} name="encryption_key" placeholder="Encryption Key" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("region")}>
|
|
||||||
<Input value={props.values["region"] || ""} onChange={(e) => props.onChange("region", e.target.value)} type={props.input_type("region")} name="region" placeholder="Region" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
<NgIf cond={props.should_appear("endpoint")}>
|
|
||||||
<Input value={props.values["endpoint"] || ""} onChange={(e) => props.onChange("endpoint", e.target.value)} type={props.input_type("endpoint")} name="endpoint" placeholder="Endpoint" autoComplete="new-password" />
|
|
||||||
</NgIf>
|
|
||||||
</NgIf>
|
|
||||||
<Button type="submit" theme="emphasis">CONNECT</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const DropboxForm = formHelper(function(props){
|
|
||||||
const redirect = () => {
|
|
||||||
return props.onThirdPartyLogin("dropbox");
|
|
||||||
};
|
|
||||||
|
|
||||||
if(CONFIG.connections.length === 1 && CONFIG.auto_connect === true){
|
|
||||||
redirect();
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
AUTHENTICATING ...
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div onClick={redirect}>
|
|
||||||
<img src={img_dropbox} />
|
|
||||||
</div>
|
|
||||||
<Button type="button" onClick={redirect} theme="emphasis">LOGIN WITH DROPBOX</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const GDriveForm = formHelper(function(props){
|
|
||||||
const redirect = () => {
|
|
||||||
return props.onThirdPartyLogin("google");
|
|
||||||
};
|
|
||||||
|
|
||||||
if(CONFIG.connections.length === 1 && CONFIG.auto_connect === true){
|
|
||||||
redirect();
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
AUTHENTICATING ...
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div onClick={redirect}>
|
|
||||||
<img src={img_drive}/>
|
|
||||||
</div>
|
|
||||||
<Button type="button" onClick={redirect} theme="emphasis">LOGIN WITH GOOGLE</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const CustomForm = formHelper(function(props){
|
|
||||||
const redirect = () => {
|
|
||||||
return props.onThirdPartyLogin("custombackend");
|
|
||||||
};
|
|
||||||
|
|
||||||
if(CONFIG.connections.length === 1 && CONFIG.auto_connect === true){
|
|
||||||
redirect();
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
AUTHENTICATING ...
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Button type="button" onClick={redirect} theme="emphasis">LOGIN</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
function formHelper(WrappedComponent){
|
|
||||||
return (props) => {
|
|
||||||
const helpers = {
|
|
||||||
should_appear: function(key){
|
|
||||||
const val = props.config[key];
|
|
||||||
if(val === undefined) return true;
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
input_type: function(key){
|
|
||||||
if(["password", "passphrase", "secret_access_key"].indexOf(key) !== -1){
|
|
||||||
return "password";
|
|
||||||
}
|
|
||||||
return "text";
|
|
||||||
},
|
|
||||||
onChange: function(key, value){
|
|
||||||
let values = props.values;
|
|
||||||
if(typeof key === "string") values[key] = value;
|
|
||||||
props.onChange(values);
|
|
||||||
},
|
|
||||||
advanced: function(){
|
|
||||||
let res = false;
|
|
||||||
for (let i=0; i < arguments.length; i++){
|
|
||||||
if(arguments[i] !== undefined) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<WrappedComponent {...props} {...helpers} />
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|||||||
@ -19,22 +19,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
form{
|
form{
|
||||||
.formbuilder{
|
|
||||||
fieldset{ padding: 0; border: none; }
|
|
||||||
legend{ display: none; }
|
|
||||||
}
|
|
||||||
|
|
||||||
padding-top: 5px;
|
padding-top: 5px;
|
||||||
label{
|
label{
|
||||||
color: rgba(0,0,0,0.4);
|
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
display: block;
|
display: block;
|
||||||
|
text-transform: capitalize;
|
||||||
}
|
}
|
||||||
.advanced_form{
|
.advanced_form{
|
||||||
max-height: 156px;
|
max-height: 156px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
margin-top: 5px
|
margin-top: 5px;
|
||||||
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
button.emphasis{
|
button.emphasis{
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
@ -47,6 +43,11 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
input[type="checkbox"]{
|
||||||
|
top: 0;
|
||||||
|
width: inherit;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,7 @@ export class LogoutPage extends React.Component {
|
|||||||
Session.logout()
|
Session.logout()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
cache.destroy();
|
cache.destroy();
|
||||||
this.props.history.push('/');
|
this.props.history.push('/login');
|
||||||
})
|
})
|
||||||
.catch((res) => {
|
.catch((res) => {
|
||||||
console.warn(res)
|
console.warn(res)
|
||||||
|
|||||||
@ -41,13 +41,9 @@ func (d *Driver) Drivers() map[string]IBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Nothing struct {}
|
type Nothing struct {}
|
||||||
|
|
||||||
func (b Nothing) Init(params map[string]string, app *App) (IBackend, error) {
|
func (b Nothing) Init(params map[string]string, app *App) (IBackend, error) {
|
||||||
return &Nothing{}, nil
|
return &Nothing{}, nil
|
||||||
}
|
}
|
||||||
func (b Nothing) Info() string {
|
|
||||||
return "N/A"
|
|
||||||
}
|
|
||||||
func (b Nothing) Ls(path string) ([]os.FileInfo, error) {
|
func (b Nothing) Ls(path string) ([]os.FileInfo, error) {
|
||||||
return nil, NewError("", 401)
|
return nil, NewError("", 401)
|
||||||
}
|
}
|
||||||
@ -69,7 +65,6 @@ func (b Nothing) Touch(path string) error {
|
|||||||
func (b Nothing) Save(path string, file io.Reader) error {
|
func (b Nothing) Save(path string, file io.Reader) error {
|
||||||
return NewError("", 401)
|
return NewError("", 401)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b Nothing) LoginForm() Form {
|
func (b Nothing) LoginForm() Form {
|
||||||
return Form{}
|
return Form{}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,7 +23,7 @@ type Configuration struct {
|
|||||||
currentElement *FormElement
|
currentElement *FormElement
|
||||||
cache KeyValueStore
|
cache KeyValueStore
|
||||||
form []Form
|
form []Form
|
||||||
conn []map[string]interface{}
|
Conn []map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Form struct {
|
type Form struct {
|
||||||
@ -40,7 +40,7 @@ type FormElement struct {
|
|||||||
Placeholder string `json:"placeholder,omitempty"`
|
Placeholder string `json:"placeholder,omitempty"`
|
||||||
Opts []string `json:"options,omitempty"`
|
Opts []string `json:"options,omitempty"`
|
||||||
Target []string `json:"target,omitempty"`
|
Target []string `json:"target,omitempty"`
|
||||||
Enabled bool `json:"enabled"`
|
ReadOnly bool `json:"readonly"`
|
||||||
Default interface{} `json:"default"`
|
Default interface{} `json:"default"`
|
||||||
Value interface{} `json:"value"`
|
Value interface{} `json:"value"`
|
||||||
}
|
}
|
||||||
@ -123,7 +123,7 @@ func NewConfiguration() Configuration {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
conn: make([]map[string]interface{}, 0),
|
Conn: make([]map[string]interface{}, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,7 +219,7 @@ func (this *Configuration) Load() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract enabled backends
|
// Extract enabled backends
|
||||||
this.conn = func(cFile []byte) []map[string]interface{} {
|
this.Conn = func(cFile []byte) []map[string]interface{} {
|
||||||
var d struct {
|
var d struct {
|
||||||
Connections []map[string]interface{} `json:"connections"`
|
Connections []map[string]interface{} `json:"connections"`
|
||||||
}
|
}
|
||||||
@ -286,8 +286,8 @@ func (this *Configuration) Initialise() {
|
|||||||
this.Get("general.host").Set(env).String()
|
this.Get("general.host").Set(env).String()
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(this.conn) == 0 {
|
if len(this.Conn) == 0 {
|
||||||
this.conn = []map[string]interface{}{
|
this.Conn = []map[string]interface{}{
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"type": "webdav",
|
"type": "webdav",
|
||||||
"label": "WebDav",
|
"label": "WebDav",
|
||||||
@ -332,7 +332,7 @@ func (this Configuration) Save() Configuration {
|
|||||||
}
|
}
|
||||||
return string(a)
|
return string(a)
|
||||||
})
|
})
|
||||||
v, _ = sjson.Set(v, "connections", this.conn)
|
v, _ = sjson.Set(v, "connections", this.Conn)
|
||||||
|
|
||||||
// deploy the config in our config.json
|
// deploy the config in our config.json
|
||||||
file, err := os.Create(configPath)
|
file, err := os.Create(configPath)
|
||||||
@ -363,7 +363,7 @@ func (this Configuration) Export() interface{} {
|
|||||||
AutoConnect: this.Get("general.auto_connect").Bool(),
|
AutoConnect: this.Get("general.auto_connect").Bool(),
|
||||||
Name: this.Get("general.name").String(),
|
Name: this.Get("general.name").String(),
|
||||||
RememberMe: this.Get("general.remember_me").Bool(),
|
RememberMe: this.Get("general.remember_me").Bool(),
|
||||||
Connections: this.conn,
|
Connections: this.Conn,
|
||||||
EnableSearch: this.Get("features.search.enable").Bool(),
|
EnableSearch: this.Get("features.search.enable").Bool(),
|
||||||
EnableShare: this.Get("features.share.enable").Bool(),
|
EnableShare: this.Get("features.share.enable").Bool(),
|
||||||
MimeTypes: AllMimeTypes(),
|
MimeTypes: AllMimeTypes(),
|
||||||
|
|||||||
@ -16,7 +16,6 @@ type IBackend interface {
|
|||||||
Mv(from string, to string) error
|
Mv(from string, to string) error
|
||||||
Save(path string, file io.Reader) error
|
Save(path string, file io.Reader) error
|
||||||
Touch(path string) error
|
Touch(path string) error
|
||||||
Info() string
|
|
||||||
LoginForm() Form
|
LoginForm() Form
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -78,9 +78,7 @@ func AdminBackend(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||||||
|
|
||||||
drivers := Backend.Drivers()
|
drivers := Backend.Drivers()
|
||||||
for key := range drivers {
|
for key := range drivers {
|
||||||
if obj, ok := drivers[key].(interface{ LoginForm() Form }); ok {
|
backends[key] = drivers[key].LoginForm()
|
||||||
backends[key] = obj.LoginForm()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SendSuccessResult(res, backends)
|
SendSuccessResult(res, backends)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package ctrl
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"github.com/mickael-kerjean/mux"
|
"github.com/mickael-kerjean/mux"
|
||||||
. "github.com/mickael-kerjean/nuage/server/common"
|
. "github.com/mickael-kerjean/nuage/server/common"
|
||||||
"github.com/mickael-kerjean/nuage/server/model"
|
"github.com/mickael-kerjean/nuage/server/model"
|
||||||
@ -44,13 +45,14 @@ func SessionAuthenticate(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if obj, ok := backend.(interface {
|
if obj, ok := backend.(interface {
|
||||||
OAuthToken(*map[string]string) error
|
OAuthToken(*map[string]interface{}) error
|
||||||
}); ok {
|
}); ok {
|
||||||
err := obj.OAuthToken(&session)
|
err := obj.OAuthToken(&ctx.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
SendErrorResult(res, NewError("Can't authenticate (OAuth error)", 401))
|
SendErrorResult(res, NewError("Can't authenticate (OAuth error)", 401))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
session = model.MapStringInterfaceToMapStringString(ctx.Body)
|
||||||
backend, err = model.NewBackend(&ctx, session)
|
backend, err = model.NewBackend(&ctx, session)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
SendErrorResult(res, NewError("Can't authenticate", 401))
|
SendErrorResult(res, NewError("Can't authenticate", 401))
|
||||||
@ -126,7 +128,7 @@ func SessionOAuthBackend(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||||||
}
|
}
|
||||||
obj, ok := b.(interface{ OAuthURL() string })
|
obj, ok := b.(interface{ OAuthURL() string })
|
||||||
if ok == false {
|
if ok == false {
|
||||||
SendErrorResult(res, NewError("No backend authentication ("+b.Info()+")", 500))
|
SendErrorResult(res, NewError(fmt.Sprintf("This backend doesn't support oauth: '%s'", a["type"]), 500))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
SendSuccessResult(res, obj.OAuthURL())
|
SendSuccessResult(res, obj.OAuthURL())
|
||||||
|
|||||||
@ -14,12 +14,14 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ETAG_INDEX string
|
||||||
|
|
||||||
func StaticHandler(_path string, ctx App) http.Handler {
|
func StaticHandler(_path string, ctx App) http.Handler {
|
||||||
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||||
header := res.Header()
|
header := res.Header()
|
||||||
header.Set("Content-Type", mime.TypeByExtension(filepath.Ext(req.URL.Path)))
|
header.Set("Content-Type", mime.TypeByExtension(filepath.Ext(req.URL.Path)))
|
||||||
header.Set("Cache-Control", "max-age=2592000")
|
header.Set("Cache-Control", "max-age=2592000")
|
||||||
SecureHeader(&header)
|
header.Set("X-Content-Type-Options", "nosniff")
|
||||||
|
|
||||||
if strings.HasSuffix(req.URL.Path, "/") {
|
if strings.HasSuffix(req.URL.Path, "/") {
|
||||||
http.NotFound(res, req)
|
http.NotFound(res, req)
|
||||||
@ -39,10 +41,23 @@ func StaticHandler(_path string, ctx App) http.Handler {
|
|||||||
|
|
||||||
func DefaultHandler(_path string, ctx App) http.Handler {
|
func DefaultHandler(_path string, ctx App) http.Handler {
|
||||||
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||||
|
_path := GetAbsolutePath(_path)
|
||||||
|
|
||||||
header := res.Header()
|
header := res.Header()
|
||||||
header.Set("Content-Type", "text/html")
|
header.Set("Content-Type", "text/html")
|
||||||
header.Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
header.Set("Cache-Control", "no-cache")
|
||||||
SecureHeader(&header)
|
header.Set("X-XSS-Protection", "1; mode=block")
|
||||||
|
header.Set("X-Content-Type-Options", "nosniff")
|
||||||
|
header.Set("X-Frame-Options", "DENY")
|
||||||
|
if ETAG_INDEX == "" {
|
||||||
|
ETAG_INDEX = hashFile(_path)
|
||||||
|
}
|
||||||
|
if req.Header.Get("If-None-Match") == ETAG_INDEX {
|
||||||
|
res.WriteHeader(http.StatusNotModified)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
header.Set("Etag", ETAG_INDEX)
|
||||||
|
|
||||||
|
|
||||||
// Redirect to the admin section on first boot to setup the stuff
|
// Redirect to the admin section on first boot to setup the stuff
|
||||||
if req.URL.String() != URL_SETUP && Config.Get("auth.admin").String() == "" {
|
if req.URL.String() != URL_SETUP && Config.Get("auth.admin").String() == "" {
|
||||||
@ -50,12 +65,11 @@ func DefaultHandler(_path string, ctx App) http.Handler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
p := _path
|
if _, err := os.Open(_path+".gz"); err == nil && strings.Contains(req.Header.Get("Accept-Encoding"), "gzip") {
|
||||||
if _, err := os.Open(path.Join(GetCurrentDir(), p+".gz")); err == nil && strings.Contains(req.Header.Get("Accept-Encoding"), "gzip") {
|
|
||||||
res.Header().Set("Content-Encoding", "gzip")
|
res.Header().Set("Content-Encoding", "gzip")
|
||||||
p += ".gz"
|
_path += ".gz"
|
||||||
}
|
}
|
||||||
http.ServeFile(res, req, GetAbsolutePath(p))
|
http.ServeFile(res, req, _path)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,20 +77,10 @@ func AboutHandler(ctx App) http.Handler {
|
|||||||
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||||
header := res.Header()
|
header := res.Header()
|
||||||
header.Set("Content-Type", "text/html")
|
header.Set("Content-Type", "text/html")
|
||||||
SecureHeader(&header)
|
header.Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||||
|
header.Set("X-XSS-Protection", "1; mode=block")
|
||||||
hash := func(path string) string {
|
header.Set("X-Content-Type-Options", "nosniff")
|
||||||
f, err := os.OpenFile(path, os.O_RDONLY, os.ModePerm)
|
header.Set("X-Frame-Options", "DENY")
|
||||||
if err != nil {
|
|
||||||
return "__"
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
h := md5.New()
|
|
||||||
if _, err := io.Copy(h, f); err != nil {
|
|
||||||
return "__"
|
|
||||||
}
|
|
||||||
return base32.HexEncoding.EncodeToString(h.Sum(nil))[:6]
|
|
||||||
}
|
|
||||||
|
|
||||||
page := `<!DOCTYPE html>
|
page := `<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
@ -104,7 +108,7 @@ func AboutHandler(ctx App) http.Handler {
|
|||||||
App []string
|
App []string
|
||||||
Plugins [][]string
|
Plugins [][]string
|
||||||
}{
|
}{
|
||||||
App: []string{"Nuage " + APP_VERSION, BUILD_NUMBER + "_" + hash(filepath.Join(GetCurrentDir(), "/nuage"))},
|
App: []string{"Nuage " + APP_VERSION, BUILD_NUMBER + "_" + hashFile(filepath.Join(GetCurrentDir(), "/nuage"))},
|
||||||
Plugins: func () [][]string {
|
Plugins: func () [][]string {
|
||||||
pPath := filepath.Join(GetCurrentDir(), PLUGIN_PATH)
|
pPath := filepath.Join(GetCurrentDir(), PLUGIN_PATH)
|
||||||
file, err := os.Open(pPath)
|
file, err := os.Open(pPath)
|
||||||
@ -122,12 +126,12 @@ func AboutHandler(ctx App) http.Handler {
|
|||||||
plugins := make([][]string, 0)
|
plugins := make([][]string, 0)
|
||||||
plugins = append(plugins, []string {
|
plugins = append(plugins, []string {
|
||||||
"config.json",
|
"config.json",
|
||||||
hash(filepath.Join(GetCurrentDir(), "/data/config/config.json")),
|
hashFile(filepath.Join(GetCurrentDir(), "/data/config/config.json")),
|
||||||
})
|
})
|
||||||
for i:=0; i < len(files); i++ {
|
for i:=0; i < len(files); i++ {
|
||||||
plugins = append(plugins, []string{
|
plugins = append(plugins, []string{
|
||||||
files[i].Name(),
|
files[i].Name(),
|
||||||
hash(pPath + "/" + files[i].Name()),
|
hashFile(pPath + "/" + files[i].Name()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return plugins
|
return plugins
|
||||||
@ -136,8 +140,16 @@ func AboutHandler(ctx App) http.Handler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func SecureHeader(header *http.Header) {
|
|
||||||
header.Set("X-XSS-Protection", "1; mode=block")
|
func hashFile (path string) string {
|
||||||
header.Set("X-Content-Type-Options", "nosniff")
|
f, err := os.OpenFile(path, os.O_RDONLY, os.ModePerm)
|
||||||
header.Set("X-Frame-Options", "DENY")
|
if err != nil {
|
||||||
|
return "__"
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
h := md5.New()
|
||||||
|
if _, err := io.Copy(h, f); err != nil {
|
||||||
|
return "__"
|
||||||
|
}
|
||||||
|
return base32.HexEncoding.EncodeToString(h.Sum(nil))[:6]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,7 +41,7 @@ func APIHandler(fn func(App, http.ResponseWriter, *http.Request), ctx App) http.
|
|||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if Config.Get("log.telemetry").Bool() {
|
if Config.Get("log.telemetry").Bool() {
|
||||||
go telemetry(req, &resw, start, ctx.Backend.Info())
|
go telemetry(req, &resw, start, ctx.Session["type"])
|
||||||
}
|
}
|
||||||
if Config.Get("log.enable").Bool() {
|
if Config.Get("log.enable").Bool() {
|
||||||
go logger(req, &resw, start)
|
go logger(req, &resw, start)
|
||||||
@ -121,7 +121,11 @@ func ExtractSession(req *http.Request, ctx *App) (map[string]string, error) {
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
str = cookie.Value
|
str = cookie.Value
|
||||||
str, _ = DecryptString(SECRET_KEY, str)
|
str, err = DecryptString(SECRET_KEY, str)
|
||||||
|
if err != nil {
|
||||||
|
// This typically happen when changing the secret key
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
err = json.Unmarshal([]byte(str), &res)
|
err = json.Unmarshal([]byte(str), &res)
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,7 +31,7 @@ func (d Dropbox) Init(params map[string]string, app *App) (IBackend, error) {
|
|||||||
backend.ClientId = Config.Get("auth.dropbox.client_id").Default("").String()
|
backend.ClientId = Config.Get("auth.dropbox.client_id").Default("").String()
|
||||||
}
|
}
|
||||||
backend.Hostname = Config.Get("general.host").String()
|
backend.Hostname = Config.Get("general.host").String()
|
||||||
backend.Bearer = params["bearer"]
|
backend.Bearer = params["access_token"]
|
||||||
|
|
||||||
if backend.ClientId == "" {
|
if backend.ClientId == "" {
|
||||||
return backend, NewError("Missing ClientID: Contact your admin", 502)
|
return backend, NewError("Missing ClientID: Contact your admin", 502)
|
||||||
@ -41,10 +41,6 @@ func (d Dropbox) Init(params map[string]string, app *App) (IBackend, error) {
|
|||||||
return backend, nil
|
return backend, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d Dropbox) Info() string {
|
|
||||||
return "dropbox"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d Dropbox) LoginForm() Form {
|
func (d Dropbox) LoginForm() Form {
|
||||||
return Form{
|
return Form{
|
||||||
Elmnts: []FormElement{
|
Elmnts: []FormElement{
|
||||||
@ -54,9 +50,16 @@ func (d Dropbox) LoginForm() Form {
|
|||||||
Value: "dropbox",
|
Value: "dropbox",
|
||||||
},
|
},
|
||||||
FormElement{
|
FormElement{
|
||||||
|
ReadOnly: true,
|
||||||
|
Name: "oauth2",
|
||||||
|
Type: "text",
|
||||||
|
Value: "/api/session/auth/dropbox",
|
||||||
|
},
|
||||||
|
FormElement{
|
||||||
|
ReadOnly: true,
|
||||||
Name: "image",
|
Name: "image",
|
||||||
Type: "image",
|
Type: "image",
|
||||||
Value: "/assets/img/dropbox.png",
|
Value: "data:image/svg+xml;utf8;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNDIuNCAzOS41IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4KICA8cG9seWdvbiBmaWxsPSIjMDA3RUU1IiBwb2ludHM9IjEyLjUsMCAwLDguMSA4LjcsMTUuMSAyMS4yLDcuMyIvPgo8cG9seWdvbiBmaWxsPSIjMDA3RUU1IiBwb2ludHM9IjAsMjEuOSAxMi41LDMwLjEgMjEuMiwyMi44IDguNywxNS4xIi8+Cjxwb2x5Z29uIGZpbGw9IiMwMDdFRTUiIHBvaW50cz0iMjEuMiwyMi44IDMwLDMwLjEgNDIuNCwyMiAzMy44LDE1LjEiLz4KPHBvbHlnb24gZmlsbD0iIzAwN0VFNSIgcG9pbnRzPSI0Mi40LDguMSAzMCwwIDIxLjIsNy4zIDMzLjgsMTUuMSIvPgo8cG9seWdvbiBmaWxsPSIjMDA3RUU1IiBwb2ludHM9IjIxLjMsMjQuNCAxMi41LDMxLjcgOC44LDI5LjIgOC44LDMyIDIxLjMsMzkuNSAzMy44LDMyIDMzLjgsMjkuMiAzMCwzMS43Ii8+Cjwvc3ZnPgo=",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,10 +70,6 @@ func (f Ftp) Init(params map[string]string, app *App) (IBackend, error) {
|
|||||||
return backend, nil
|
return backend, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Ftp) Info() string {
|
|
||||||
return "ftp"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f Ftp) LoginForm() Form {
|
func (f Ftp) LoginForm() Form {
|
||||||
return Form{
|
return Form{
|
||||||
Elmnts: []FormElement{
|
Elmnts: []FormElement{
|
||||||
|
|||||||
@ -67,10 +67,6 @@ func (g GDrive) Init(params map[string]string, app *App) (IBackend, error) {
|
|||||||
return backend, nil
|
return backend, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g GDrive) Info() string {
|
|
||||||
return "googledrive"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g GDrive) LoginForm() Form {
|
func (g GDrive) LoginForm() Form {
|
||||||
return Form{
|
return Form{
|
||||||
Elmnts: []FormElement{
|
Elmnts: []FormElement{
|
||||||
@ -80,16 +76,23 @@ func (g GDrive) LoginForm() Form {
|
|||||||
Value: "gdrive",
|
Value: "gdrive",
|
||||||
},
|
},
|
||||||
FormElement{
|
FormElement{
|
||||||
|
ReadOnly: true,
|
||||||
|
Name: "oauth2",
|
||||||
|
Type: "text",
|
||||||
|
Value: "/api/session/auth/gdrive",
|
||||||
|
},
|
||||||
|
FormElement{
|
||||||
|
ReadOnly: true,
|
||||||
Name: "image",
|
Name: "image",
|
||||||
Type: "image",
|
Type: "image",
|
||||||
Value: "/assets/img/google-drive.png",
|
Value: "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTM5IDEyMC40IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4KICA8cGF0aCBkPSJtMjQuMiAxMjAuNC0yNC4yLTQxLjkgNDUuMy03OC41IDI0LjIgNDEuOXoiIGZpbGw9IiMwZGE5NjAiLz4KICA8cGF0aCBkPSJtNTguOSA2MC4yIDEwLjYtMTguMy0yNC4yLTQxLjl6IiBmaWxsPSIjMGRhOTYwIi8+CiAgPHBhdGggZD0ibTI0LjIgMTIwLjQgMjQuMi00MS45aDkwLjZsLTI0LjIgNDEuOXoiIGZpbGw9IiMyZDZmZGQiLz4KICA8cGF0aCBkPSJtNjkuNSA3OC41aC0yMS4xbDEwLjUtMTguMy0zNC43IDYwLjJ6IiBmaWxsPSIjMmQ2ZmRkIi8+ICAKICA8cGF0aCBkPSJtMTM5IDc4LjVoLTQ4LjRsLTQ1LjMtNzguNWg0OC40eiIgZmlsbD0iI2ZmZDI0ZCIvPgogIDxwYXRoIGQ9Im05MC42IDc4LjVoNDguNGwtNTguOS0xOC4zeiIgZmlsbD0iI2ZmZDI0ZCIvPgo8L3N2Zz4K",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g GDrive) OAuthURL() string {
|
func (g GDrive) OAuthURL() string {
|
||||||
return g.Config.AuthCodeURL("googledrive", oauth2.AccessTypeOnline)
|
return g.Config.AuthCodeURL("gdrive", oauth2.AccessTypeOnline)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g GDrive) OAuthToken(ctx *map[string]interface{}) error {
|
func (g GDrive) OAuthToken(ctx *map[string]interface{}) error {
|
||||||
|
|||||||
@ -108,10 +108,6 @@ func (git Git) Init(params map[string]string, app *App) (IBackend, error) {
|
|||||||
return g, nil
|
return g, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g Git) Info() string {
|
|
||||||
return "git"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g Git) LoginForm() Form {
|
func (g Git) LoginForm() Form {
|
||||||
return Form{
|
return Form{
|
||||||
Elmnts: []FormElement{
|
Elmnts: []FormElement{
|
||||||
@ -132,7 +128,7 @@ func (g Git) LoginForm() Form {
|
|||||||
},
|
},
|
||||||
FormElement{
|
FormElement{
|
||||||
Name: "password",
|
Name: "password",
|
||||||
Type: "password",
|
Type: "long_password",
|
||||||
Placeholder: "Password",
|
Placeholder: "Password",
|
||||||
},
|
},
|
||||||
FormElement{
|
FormElement{
|
||||||
|
|||||||
@ -53,10 +53,6 @@ func (s S3Backend) Init(params map[string]string, app *App) (IBackend, error) {
|
|||||||
return backend, nil
|
return backend, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s S3Backend) Info() string {
|
|
||||||
return "s3"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s S3Backend) LoginForm() Form {
|
func (s S3Backend) LoginForm() Form {
|
||||||
return Form{
|
return Form{
|
||||||
Elmnts: []FormElement{
|
Elmnts: []FormElement{
|
||||||
|
|||||||
@ -41,7 +41,6 @@ func (s Sftp) Init(params map[string]string, app *App) (IBackend, error) {
|
|||||||
params["password"],
|
params["password"],
|
||||||
params["passphrase"],
|
params["passphrase"],
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.port == "" {
|
if p.port == "" {
|
||||||
p.port = "22"
|
p.port = "22"
|
||||||
}
|
}
|
||||||
@ -91,10 +90,6 @@ func (s Sftp) Init(params map[string]string, app *App) (IBackend, error) {
|
|||||||
return &s, nil
|
return &s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b Sftp) Info() string {
|
|
||||||
return "sftp"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b Sftp) LoginForm() Form {
|
func (b Sftp) LoginForm() Form {
|
||||||
return Form{
|
return Form{
|
||||||
Elmnts: []FormElement{
|
Elmnts: []FormElement{
|
||||||
@ -115,7 +110,7 @@ func (b Sftp) LoginForm() Form {
|
|||||||
},
|
},
|
||||||
FormElement{
|
FormElement{
|
||||||
Name: "password",
|
Name: "password",
|
||||||
Type: "password",
|
Type: "long_password",
|
||||||
Placeholder: "Password",
|
Placeholder: "Password",
|
||||||
},
|
},
|
||||||
FormElement{
|
FormElement{
|
||||||
|
|||||||
@ -41,10 +41,6 @@ func (w WebDav) Init(params map[string]string, app *App) (IBackend, error) {
|
|||||||
return backend, nil
|
return backend, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w WebDav) Info() string {
|
|
||||||
return "webdav"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w WebDav) LoginForm() Form {
|
func (w WebDav) LoginForm() Form {
|
||||||
return Form{
|
return Form{
|
||||||
Elmnts: []FormElement{
|
Elmnts: []FormElement{
|
||||||
|
|||||||
@ -9,32 +9,39 @@ import (
|
|||||||
|
|
||||||
func NewBackend(ctx *App, conn map[string]string) (IBackend, error) {
|
func NewBackend(ctx *App, conn map[string]string) (IBackend, error) {
|
||||||
isAllowed := func() bool {
|
isAllowed := func() bool {
|
||||||
|
// by default, a hacker could use filestash to establish connections outside of what's
|
||||||
|
// define in the config file. We need to prevent this
|
||||||
|
possibilities := make([]map[string]interface{}, 0)
|
||||||
|
for i:=0; i< len(Config.Conn); i++ {
|
||||||
|
d := Config.Conn[i]
|
||||||
|
if d["type"] != conn["type"] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if val, ok := d["hostname"]; ok == true {
|
||||||
|
if val != conn["hostname"] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if val, ok := d["path"]; ok == true {
|
||||||
|
if val != conn["path"] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if val, ok := d["url"]; ok == true {
|
||||||
|
if val != conn["url"] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
possibilities = append(possibilities, Config.Conn[i])
|
||||||
|
}
|
||||||
|
if len(possibilities) > 0 {
|
||||||
return true
|
return true
|
||||||
// ret := false
|
}
|
||||||
// var conns [] struct {
|
return false
|
||||||
// Type string `json:"type"`
|
}
|
||||||
// Hostname string `json:"hostname"`
|
|
||||||
// Path string `json:"path"`
|
|
||||||
// }
|
|
||||||
// Config.Get("connections").Interface()
|
|
||||||
// Config.Get("connections").Scan(&conns)
|
|
||||||
// for i := range conns {
|
|
||||||
// if conns[i].Type == conn["type"] {
|
|
||||||
// if conns[i].Hostname != "" && conns[i].Hostname != conn["hostname"] {
|
|
||||||
// continue
|
|
||||||
// } else if conns[i].Path != "" && conns[i].Path != conn["path"] {
|
|
||||||
// continue
|
|
||||||
// } else {
|
|
||||||
// ret = true
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return ret
|
|
||||||
}()
|
|
||||||
|
|
||||||
if isAllowed == false {
|
if isAllowed() == false {
|
||||||
return Backend.Get(BACKEND_NIL).Init(conn, ctx)
|
return Backend.Get(BACKEND_NIL), ErrNotAllowed
|
||||||
}
|
}
|
||||||
return Backend.Get(conn["type"]).Init(conn, ctx)
|
return Backend.Get(conn["type"]).Init(conn, ctx)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user