mirror of
https://github.com/mickael-kerjean/filestash
synced 2025-12-15 21:04:46 +01:00
Merge branch 'master' of https://github.com/mickael-kerjean/nuage into HEAD
This commit is contained in:
commit
22d045266d
24 changed files with 313 additions and 171 deletions
20
.drone.yml
20
.drone.yml
|
|
@ -1,22 +1,4 @@
|
|||
pipeline:
|
||||
test:
|
||||
group: test
|
||||
image: machines/nuage_build
|
||||
pull: true
|
||||
environment:
|
||||
- GOPATH=/tmp/go
|
||||
- PKG_CONFIG_PATH=/usr/local/lib/pkgconfig/
|
||||
- LD_LIBRARY_PATH=/usr/local/lib
|
||||
- CGO_LDFLAGS_ALLOW='-fopenmp'
|
||||
commands:
|
||||
# Prepare
|
||||
- mkdir -p $GOPATH/src/github.com/mickael-kerjean/nuage
|
||||
- mv * $GOPATH/src/github.com/mickael-kerjean/nuage
|
||||
- cd $GOPATH/src/github.com/mickael-kerjean/nuage
|
||||
- cd server
|
||||
- go get
|
||||
- go test -v ./...
|
||||
|
||||
publish_docker:
|
||||
group: release
|
||||
image: docker
|
||||
|
|
@ -28,5 +10,5 @@ pipeline:
|
|||
commands:
|
||||
- echo $DOCKER_PASSWORD | docker login -u=$DOCKER_USERNAME --password-stdin
|
||||
- docker pull alpine:latest
|
||||
- docker build --no-cache -t machines/nuage:master docker/prod
|
||||
- docker build --no-cache -t machines/nuage docker/prod
|
||||
- docker push machines/nuage
|
||||
|
|
@ -1,56 +1,118 @@
|
|||
@import url('https://fonts.googleapis.com/css?family=Source+Code+Pro:400,600');
|
||||
/* latin-ext */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Source Code Pro';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Source Code Pro'), local('SourceCodePro-Regular'), url(/assets/fonts/SourceCodePro-Regular-400-latin-ext.woff2) format('woff2');
|
||||
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 */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Source Code Pro';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Source Code Pro'), local('SourceCodePro-Regular'), url(/assets/fonts/SourceCodePro-Regular-400-latin.woff2) format('woff2');
|
||||
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 */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Source Code Pro';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Source Code Pro Semibold'), local('SourceCodePro-Semibold'), url(/assets/fonts/SourceCodePro-Semibold-600-latin-ext.woff2) format('woff2');
|
||||
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 */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Source Code Pro';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Source Code Pro Semibold'), local('SourceCodePro-Semibold'), url(/assets/fonts/SourceCodePro-Semibold-600-latin.woff2) format('woff2');
|
||||
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;
|
||||
}
|
||||
|
||||
:root {
|
||||
--bg-color: #f1f2f2;
|
||||
--color: #626469;
|
||||
--emphasis: #375160;
|
||||
|
||||
--primary: #9AD1ED;
|
||||
--emphasis-primary: #c5e2f1;
|
||||
|
||||
--secondary: #466372;
|
||||
--emphasis-secondary: #466372;
|
||||
|
||||
--light: #909090;
|
||||
--super-light: #f4f4f4;
|
||||
--super-light: #f4f4f4;
|
||||
--error: #f26d6d;
|
||||
--success: #63d9b1;
|
||||
|
||||
--dark: #313538;
|
||||
}
|
||||
|
||||
html {
|
||||
font-family:"San Francisco","Roboto","Arial",sans-serif;
|
||||
-webkit-text-size-adjust:100%;
|
||||
font-family: "San Francisco", "Roboto", "Arial", sans-serif;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
background: var(--bg-color);
|
||||
color: var(--color);
|
||||
}
|
||||
body {overflow: hidden;}
|
||||
body, html{
|
||||
|
||||
body {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body,
|
||||
html {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
#main{height: 100%;}
|
||||
a{color: inherit; text-decoration: none;}
|
||||
|
||||
select{-moz-appearance: none;}
|
||||
#main {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
select {
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
||||
select:-moz-focusring {
|
||||
color: transparent;
|
||||
outline: none;
|
||||
border: none;
|
||||
}
|
||||
select::-ms-expand { display: none; }
|
||||
|
||||
|
||||
button::-moz-focus-inner {
|
||||
border: 0;
|
||||
select::-ms-expand {
|
||||
display: none;
|
||||
}
|
||||
|
||||
input, textarea{
|
||||
button::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
transition: border 0.2s;
|
||||
outline: none;
|
||||
}
|
||||
input[type="checkbox"]{position: relative; top: 1px; margin: 0; padding: 0; vertical-align: top;}
|
||||
|
||||
input[type="checkbox"] {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.no-select {
|
||||
-webkit-touch-callout: none;
|
||||
|
|
@ -61,74 +123,73 @@ input[type="checkbox"]{position: relative; top: 1px; margin: 0; padding: 0; vert
|
|||
user-select: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
button:focus,
|
||||
a:focus, a:active,
|
||||
a:focus,
|
||||
a:active,
|
||||
button::-moz-focus-inner,
|
||||
input[type="reset"]::-moz-focus-inner,
|
||||
input[type="button"]::-moz-focus-inner,
|
||||
input[type="submit"]::-moz-focus-inner,
|
||||
select::-moz-focus-inner,
|
||||
input[type="file"] > input[type="button"]::-moz-focus-inner {
|
||||
outline: none !important;
|
||||
input[type="file"]>input[type="button"]::-moz-focus-inner {
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
select:-moz-focusring {
|
||||
color: transparent;
|
||||
text-shadow: 0 0 0 #000;
|
||||
color: transparent;
|
||||
text-shadow: 0 0 0 #000;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.connect-form input:hover, .connect-form textarea:hover,
|
||||
.connect-form input:focus, .connect-form textarea:focus{
|
||||
.connect-form input:hover,
|
||||
.connect-form textarea:hover,
|
||||
.connect-form input:focus,
|
||||
.connect-form textarea:focus {
|
||||
border-color: rgb(154, 209, 237)!important;
|
||||
}
|
||||
|
||||
.drag-drop{
|
||||
.drag-drop {
|
||||
z-index: 2;
|
||||
}
|
||||
.drag-drop.dragging > div{
|
||||
background: rgba(0,0,0,0.1);
|
||||
|
||||
.drag-drop.dragging>div {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* CONNECTION FORM */
|
||||
.login-form button.active{
|
||||
box-shadow: 0px 1px 5px rgba(0,0,0,0.20);
|
||||
|
||||
.login-form button.active {
|
||||
box-shadow: 0px 1px 5px rgba(0, 0, 0, 0.20);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
width: 4px
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgba(0, 0, 0, .1)
|
||||
}
|
||||
|
||||
::-webkit-scrollbar{
|
||||
height:4px;
|
||||
width:4px
|
||||
::-webkit-scrollbar-thumb {
|
||||
-webkit-border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
-ms-border-radius: 2px;
|
||||
-o-border-radius: 2px;
|
||||
border-radius: 2px;
|
||||
background: rgba(0, 0, 0, .2);
|
||||
}
|
||||
::-webkit-scrollbar-track{
|
||||
background:rgba(0,0,0,.1)
|
||||
|
||||
.scroll-y {
|
||||
scrollbar-3dlight-color: #7d7e94;
|
||||
scrollbar-arrow-color: #c1c1d1;
|
||||
scrollbar-darkshadow-color: #2d2c4d;
|
||||
scrollbar-face-color: rgba(0, 0, 0, .1);
|
||||
scrollbar-highlight-color: #7d7e94;
|
||||
scrollbar-shadow-color: #2d2c4d;
|
||||
scrollbar-track-color: rgba(0, 0, 0, .1);
|
||||
}
|
||||
::-webkit-scrollbar-thumb{
|
||||
-webkit-border-radius:2px;
|
||||
-moz-border-radius:2px;
|
||||
-ms-border-radius:2px;
|
||||
-o-border-radius:2px;
|
||||
border-radius:2px;
|
||||
background:rgba(0,0,0,.2);
|
||||
}
|
||||
.scroll-y{
|
||||
scrollbar-3dlight-color:#7d7e94;
|
||||
scrollbar-arrow-color:#c1c1d1;
|
||||
scrollbar-darkshadow-color:#2d2c4d;
|
||||
scrollbar-face-color:rgba(0,0,0,.1);
|
||||
scrollbar-highlight-color:#7d7e94;
|
||||
scrollbar-shadow-color:#2d2c4d;
|
||||
scrollbar-track-color:rgba(0,0,0,.1);
|
||||
}
|
||||
.pointer{cursor: pointer;}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
BIN
client/assets/fonts/SourceCodePro-Regular-400-latin-ext.woff2
Normal file
BIN
client/assets/fonts/SourceCodePro-Regular-400-latin-ext.woff2
Normal file
Binary file not shown.
BIN
client/assets/fonts/SourceCodePro-Regular-400-latin.woff2
Normal file
BIN
client/assets/fonts/SourceCodePro-Regular-400-latin.woff2
Normal file
Binary file not shown.
BIN
client/assets/fonts/SourceCodePro-Semibold-600-latin-ext.woff2
Normal file
BIN
client/assets/fonts/SourceCodePro-Semibold-600-latin-ext.woff2
Normal file
Binary file not shown.
BIN
client/assets/fonts/SourceCodePro-Semibold-600-latin.woff2
Normal file
BIN
client/assets/fonts/SourceCodePro-Semibold-600-latin.woff2
Normal file
Binary file not shown.
80
client/components/decorator.js
Normal file
80
client/components/decorator.js
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import React from 'react';
|
||||
|
||||
import { Session } from '../model/';
|
||||
import { Container, Loader } from '../components/';
|
||||
import { memory } from '../helpers/';
|
||||
|
||||
import '../pages/error.scss';
|
||||
|
||||
export function LoggedInOnly(WrappedComponent){
|
||||
memory.set('user::authenticated', false);
|
||||
|
||||
return class extends React.Component {
|
||||
constructor(props){
|
||||
super(props);
|
||||
this.state = {
|
||||
is_logged_in: memory.get('user::authenticated')
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
if(this.state.is_logged_in === false){
|
||||
Session.currentUser().then((res) => {
|
||||
if(res.is_authenticated === false){
|
||||
this.props.error({message: "Authentication Required"});
|
||||
return;
|
||||
}
|
||||
memory.set('user::authenticated', true);
|
||||
this.setState({is_logged_in: true});
|
||||
}).catch((err) => {
|
||||
if(err.code === "NO_INTERNET"){
|
||||
this.setState({is_logged_in: true});
|
||||
return;
|
||||
}
|
||||
this.props.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
if(this.state.is_logged_in === true){
|
||||
return <WrappedComponent {...this.props} />;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function ErrorPage(WrappedComponent){
|
||||
return class extends React.Component {
|
||||
constructor(props){
|
||||
super(props);
|
||||
this.state = {
|
||||
error: null
|
||||
};
|
||||
}
|
||||
|
||||
update(obj){
|
||||
this.setState({error: obj});
|
||||
}
|
||||
|
||||
render(){
|
||||
if(this.state.error !== null){
|
||||
const message = this.state.error.message || "There is nothing in here";
|
||||
return (
|
||||
<Container>
|
||||
<div className="error-page">
|
||||
<h1>Oops!</h1>
|
||||
<h2>{message}</h2>
|
||||
<p>{JSON.stringify(this.state.error)}</p>
|
||||
</div>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<WrappedComponent error={this.update.bind(this)} {...this.props} />
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ export { Audio } from './audio';
|
|||
export { Video } from './video';
|
||||
export { Dropdown, DropdownButton, DropdownList, DropdownItem } from './dropdown';
|
||||
export { MapShot } from './mapshot';
|
||||
export { LoggedInOnly, ErrorPage } from './decorator';
|
||||
//export { Connect } from './connect';
|
||||
// Those are commented because they will delivered as a separate chunk
|
||||
// export { Editor } from './editor';
|
||||
|
|
|
|||
|
|
@ -108,7 +108,6 @@ function handle_error_response(xhr, err){
|
|||
}else if(xhr.status === 500){
|
||||
err({message: message || "Oups something went wrong with our servers", code: "INTERNAL_SERVER_ERROR"});
|
||||
}else if(xhr.status === 401){
|
||||
if(location.pathname !== '/login'){ location.pathname = "/login"; }
|
||||
err({message: message || "Authentication error", code: "Unauthorized"});
|
||||
}else if(xhr.status === 403){
|
||||
err({message: message || "You can\'t do that", code: "Forbidden"});
|
||||
|
|
|
|||
|
|
@ -3,11 +3,10 @@ function Memory(){
|
|||
|
||||
return {
|
||||
get: function(key){
|
||||
if(!data[key]) return null;
|
||||
if(data[key] === undefined) return null;
|
||||
return data[key];
|
||||
},
|
||||
set: function(key, value){
|
||||
if(!data[key]) data[key] = {};
|
||||
data[key] = value;
|
||||
},
|
||||
all: function(){
|
||||
|
|
|
|||
|
|
@ -4,19 +4,15 @@ import Router from './router';
|
|||
|
||||
import './assets/css/reset.scss';
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register('/assets/worker/cache.js').catch(function(error) {
|
||||
console.log('ServiceWorker registration failed:', error);
|
||||
});
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
ReactDOM.render(<Router/>, document.getElementById('main'));
|
||||
};
|
||||
|
||||
window.log = function(){console.log.apply(this, arguments)};
|
||||
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
const className = 'ontouchstart' in window ? 'touch-yes' : 'touch-no';
|
||||
document.body.classList.add(className);
|
||||
|
||||
ReactDOM.render(<Router/>, document.getElementById('main'));
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register('/assets/worker/cache.js').catch(function(error) {
|
||||
console.log('ServiceWorker registration failed:', error);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { http_get, http_post, http_delete } from '../helpers/';
|
||||
|
||||
class SessionManager{
|
||||
isLoggedIn(){
|
||||
currentUser(){
|
||||
let url = '/api/session'
|
||||
return http_get(url)
|
||||
.then(data => data.result);
|
||||
|
|
|
|||
|
|
@ -45,8 +45,10 @@ export class ConnectPage extends React.Component {
|
|||
authenticate(params){
|
||||
this.setState({loading: true});
|
||||
Session.authenticate(params)
|
||||
.then((path) => {
|
||||
.then(Session.currentUser)
|
||||
.then((user) => {
|
||||
let url = '/files/';
|
||||
let path = user.home
|
||||
if(path){
|
||||
path = path.replace(/^\/?(.*?)\/?$/, "$1");
|
||||
if(path !== ""){
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
width: 80%;
|
||||
max-width: 600px;
|
||||
margin: 50px auto 0 auto;
|
||||
flex-direction: column;
|
||||
|
||||
h1{margin: 5px 0; font-size: 3.1em;}
|
||||
h2{margin: 10px 0; font-weight: normal;}
|
||||
|
|
|
|||
|
|
@ -6,14 +6,23 @@ import './filespage.scss';
|
|||
import './error.scss';
|
||||
import { Files } from '../model/';
|
||||
import { sort, onCreate, onRename, onDelete, onUpload, onSearch } from './filespage.helper';
|
||||
import { NgIf, Loader, EventReceiver } from '../components/';
|
||||
import { NgIf, Loader, EventReceiver, LoggedInOnly, ErrorPage } from '../components/';
|
||||
import { notify, debounce, goToFiles, goToViewer, event, settings_get, settings_put } from '../helpers/';
|
||||
import { BreadCrumb, FileSystem, FrequentlyAccess, Submenu } from './filespage/';
|
||||
import InfiniteScroll from 'react-infinite-scroller';
|
||||
|
||||
const PAGE_NUMBER_INIT = 3;
|
||||
const LOAD_PER_SCROLL = 24;
|
||||
const PAGE_NUMBER_INIT = 2;
|
||||
const LOAD_PER_SCROLL = 48;
|
||||
|
||||
// usefull when user press the back button while keeping the current context
|
||||
let LAST_PAGE_PARAMS = {
|
||||
path: null,
|
||||
scroll: 0,
|
||||
page_number: PAGE_NUMBER_INIT
|
||||
};
|
||||
|
||||
@ErrorPage
|
||||
@LoggedInOnly
|
||||
@EventReceiver
|
||||
@DragDropContext(('ontouchstart' in window)? HTML5Backend : HTML5Backend)
|
||||
export class FilesPage extends React.Component {
|
||||
|
|
@ -30,8 +39,7 @@ export class FilesPage extends React.Component {
|
|||
metadata: null,
|
||||
frequents: [],
|
||||
page_number: PAGE_NUMBER_INIT,
|
||||
loading: true,
|
||||
error: null
|
||||
loading: true
|
||||
};
|
||||
|
||||
this.goToFiles = goToFiles.bind(null, this.props.history);
|
||||
|
|
@ -50,7 +58,6 @@ export class FilesPage extends React.Component {
|
|||
this.props.subscribe('file.delete', onDelete.bind(this));
|
||||
this.props.subscribe('file.refresh', this.onRefresh.bind(this));
|
||||
window.addEventListener('keydown', this.toggleHiddenFilesVisibilityonCtrlK);
|
||||
this.hideError();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
|
@ -61,6 +68,10 @@ export class FilesPage extends React.Component {
|
|||
this.props.unsubscribe('file.refresh');
|
||||
window.removeEventListener('keydown', this.toggleHiddenFilesVisibilityonCtrlK);
|
||||
this._cleanupListeners();
|
||||
|
||||
LAST_PAGE_PARAMS.path = this.state.path;
|
||||
LAST_PAGE_PARAMS.scroll = this.refs.$scroll.scrollTop;
|
||||
LAST_PAGE_PARAMS.page_number = this.state.page_number;
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps){
|
||||
|
|
@ -76,10 +87,6 @@ export class FilesPage extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
hideError(){
|
||||
this.setState({error: null});
|
||||
}
|
||||
|
||||
toggleHiddenFilesVisibilityonCtrlK(e){
|
||||
if(e.keyCode === 72 && e.ctrlKey === true){
|
||||
e.preventDefault();
|
||||
|
|
@ -112,16 +119,24 @@ export class FilesPage extends React.Component {
|
|||
metadata: res.metadata,
|
||||
files: sort(files, this.state.sort),
|
||||
loading: false,
|
||||
page_number: PAGE_NUMBER_INIT
|
||||
page_number: function(){
|
||||
if(this.state.path === LAST_PAGE_PARAMS.path){
|
||||
return LAST_PAGE_PARAMS.page_number;
|
||||
}
|
||||
return PAGE_NUMBER_INIT;
|
||||
}.bind(this)()
|
||||
}, () => {
|
||||
if(this.state.path === LAST_PAGE_PARAMS.path){
|
||||
this.refs.$scroll.scrollTop = LAST_PAGE_PARAMS.scroll;
|
||||
}
|
||||
});
|
||||
}else{
|
||||
notify.send(res, 'error');
|
||||
}
|
||||
}, (error) => {
|
||||
this.setState({error: error});
|
||||
this.props.error(error);
|
||||
});
|
||||
this.observers.push(observer);
|
||||
this.setState({error: null});
|
||||
if(path === "/"){
|
||||
Files.frequents().then((s) => this.setState({frequents: s}));
|
||||
}
|
||||
|
|
@ -210,10 +225,10 @@ export class FilesPage extends React.Component {
|
|||
<div className="component_page_filespage">
|
||||
<BreadCrumb className="breadcrumb" path={this.state.path} />
|
||||
<div className="page_container">
|
||||
<div className="scroll-y">
|
||||
<div ref="$scroll" className="scroll-y">
|
||||
<InfiniteScroll pageStart={0} loader={$moreLoading} hasMore={this.state.files.length > 70}
|
||||
initialLoad={false} useWindow={false} loadMore={this.loadMore.bind(this)} threshold={100}>
|
||||
<NgIf className="container" cond={this.state.loading === false && this.state.error === null}>
|
||||
<NgIf className="container" cond={!this.state.loading}>
|
||||
<NgIf cond={this.state.path === '/'}>
|
||||
<FrequentlyAccess files={this.state.frequents}/>
|
||||
</NgIf>
|
||||
|
|
@ -225,14 +240,9 @@ export class FilesPage extends React.Component {
|
|||
</NgIf>
|
||||
</NgIf>
|
||||
</InfiniteScroll>
|
||||
<NgIf cond={this.state.loading && this.state.error === null}>
|
||||
<NgIf cond={!!this.state.loading}>
|
||||
<Loader/>
|
||||
</NgIf>
|
||||
<NgIf cond={this.state.error !== null} className="error-page">
|
||||
<h1>Oops!</h1>
|
||||
<h2>It seems this directory doesn't exist</h2>
|
||||
<p>{JSON.stringify(this.state.error)}</p>
|
||||
</NgIf>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -408,7 +408,7 @@ class LazyLoadImage extends React.Component {
|
|||
error: false
|
||||
};
|
||||
this.$scroll = document.querySelector(props.scroller);
|
||||
this.onScroll = debounce(this.onScroll.bind(this), 100);
|
||||
this.onScroll = debounce(this.onScroll.bind(this), 250);
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
|
|
|
|||
|
|
@ -13,10 +13,14 @@ export class HomePage extends React.Component {
|
|||
}
|
||||
|
||||
componentDidMount(){
|
||||
Session.isLoggedIn()
|
||||
Session.currentUser()
|
||||
.then((res) => {
|
||||
if(res === true){
|
||||
this.setState({redirection: "/files"});
|
||||
if(res && res.is_authenticated === true){
|
||||
let url = "/files"
|
||||
if(res.home){
|
||||
url += res.home
|
||||
}
|
||||
this.setState({redirection: url});
|
||||
}else{
|
||||
this.setState({redirection: "/login"});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import Path from 'path';
|
|||
import './viewerpage.scss';
|
||||
import './error.scss';
|
||||
import { Files } from '../model/';
|
||||
import { BreadCrumb, Bundle, NgIf, Loader, Container, EventReceiver, EventEmitter } from '../components/';
|
||||
import { BreadCrumb, Bundle, NgIf, Loader, Container, EventReceiver, EventEmitter, LoggedInOnly , ErrorPage } from '../components/';
|
||||
import { debounce, opener, notify } from '../helpers/';
|
||||
import { AudioPlayer, FileDownloader, ImageViewer, PDFViewer } from './viewerpage/';
|
||||
|
||||
|
|
@ -19,6 +19,8 @@ const IDE = (props) => (
|
|||
</Bundle>
|
||||
);
|
||||
|
||||
@ErrorPage
|
||||
@LoggedInOnly
|
||||
@EventReceiver
|
||||
export class ViewerPage extends React.Component {
|
||||
constructor(props){
|
||||
|
|
@ -31,8 +33,7 @@ export class ViewerPage extends React.Component {
|
|||
content: null,
|
||||
needSaving: false,
|
||||
isSaving: false,
|
||||
loading: true,
|
||||
error: null
|
||||
loading: true
|
||||
};
|
||||
this.props.subscribe('file.select', this.onPathUpdate.bind(this));
|
||||
}
|
||||
|
|
@ -41,10 +42,10 @@ export class ViewerPage extends React.Component {
|
|||
this.setState({
|
||||
path: props.match.url.replace('/view', '') + (location.hash || ""),
|
||||
filename: Path.basename(props.match.url.replace('/view', '')) || 'untitled.dat'
|
||||
}, () => { this.componentWillMount(); });
|
||||
}, () => { this.componentDidMount(); });
|
||||
}
|
||||
|
||||
componentWillMount(){
|
||||
componentDidMount(){
|
||||
const metadata = () => {
|
||||
return new Promise((done, err) => {
|
||||
let app_opener = opener(this.state.path);
|
||||
|
|
@ -54,7 +55,7 @@ export class ViewerPage extends React.Component {
|
|||
opener: app_opener
|
||||
}, () => done(app_opener));
|
||||
}).catch(error => {
|
||||
notify.send(err, 'error');
|
||||
this.props.error(error);
|
||||
err(error);
|
||||
});
|
||||
});
|
||||
|
|
@ -67,15 +68,14 @@ export class ViewerPage extends React.Component {
|
|||
if(err && err.code === 'BINARY_FILE'){
|
||||
this.setState({opener: 'download', loading: false});
|
||||
}else{
|
||||
this.setState({error: err});
|
||||
this.props.error(err);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
this.setState({loading: false});
|
||||
return;
|
||||
}
|
||||
this.setState({loading: false});
|
||||
};
|
||||
return metadata()
|
||||
.then(data_fetch);
|
||||
return metadata().then(data_fetch);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
|
@ -106,7 +106,7 @@ export class ViewerPage extends React.Component {
|
|||
if(err && err.code === 'CANCELLED'){ return; }
|
||||
this.setState({isSaving: false});
|
||||
notify.send(err, 'error');
|
||||
return Promise.reject();
|
||||
return Promise.reject(err);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -125,7 +125,7 @@ export class ViewerPage extends React.Component {
|
|||
<div className="component_page_viewerpage">
|
||||
<BreadCrumb needSaving={this.state.needSaving} className="breadcrumb" path={this.state.path} />
|
||||
<div className="page_container">
|
||||
<NgIf cond={this.state.loading === false && this.state.error === null}>
|
||||
<NgIf cond={this.state.loading === false}>
|
||||
<NgIf cond={this.state.opener === 'editor'}>
|
||||
<IDE needSavingUpdate={this.needSaving.bind(this)}
|
||||
needSaving={this.state.needSaving}
|
||||
|
|
@ -151,14 +151,9 @@ export class ViewerPage extends React.Component {
|
|||
<FileDownloader data={this.state.url} filename={this.state.filename} />
|
||||
</NgIf>
|
||||
</NgIf>
|
||||
<NgIf cond={this.state.loading === true && this.state.error === null}>
|
||||
<NgIf cond={this.state.loading === true}>
|
||||
<Loader/>
|
||||
</NgIf>
|
||||
<NgIf cond={this.state.error !== null} className="error-page">
|
||||
<h1>Oops!</h1>
|
||||
<h2>There is nothing in here</h2>
|
||||
<p>{JSON.stringify(this.state.error)}</p>
|
||||
</NgIf>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ func NewBool(t bool) *bool {
|
|||
}
|
||||
|
||||
func NewString(t string) *string {
|
||||
if t == "" {
|
||||
return nil
|
||||
}
|
||||
return &t
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,6 +56,13 @@ func FileLs(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
|
||||
func FileCat(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
http.SetCookie(res, &http.Cookie{
|
||||
Name: "download",
|
||||
Value: "",
|
||||
MaxAge: -1,
|
||||
Path: "/",
|
||||
})
|
||||
|
||||
path, err := pathBuilder(ctx, req.URL.Query().Get("path"))
|
||||
if err != nil {
|
||||
SendErrorResult(res, err)
|
||||
|
|
@ -71,13 +78,6 @@ func FileCat(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
http.SetCookie(res, &http.Cookie{
|
||||
Name: "download",
|
||||
Value: "",
|
||||
MaxAge: -1,
|
||||
Path: "/",
|
||||
})
|
||||
|
||||
file, err = services.ProcessFileBeforeSend(file, &ctx, req, &res)
|
||||
if err != nil {
|
||||
SendErrorResult(res, err)
|
||||
|
|
|
|||
|
|
@ -8,21 +8,28 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
func SessionIsValid(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
type Session struct {
|
||||
Home *string `json:"home,omitempty"`
|
||||
IsAuth bool `json:"is_authenticated"`
|
||||
}
|
||||
|
||||
func SessionGet(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
r := Session {
|
||||
IsAuth: false,
|
||||
}
|
||||
|
||||
if ctx.Backend == nil {
|
||||
SendSuccessResult(res, false)
|
||||
SendSuccessResult(res, r)
|
||||
return
|
||||
}
|
||||
if _, err := ctx.Backend.Ls("/"); err != nil {
|
||||
SendSuccessResult(res, false)
|
||||
home, err := model.GetHome(ctx.Backend)
|
||||
if err != nil {
|
||||
SendSuccessResult(res, r)
|
||||
return
|
||||
}
|
||||
home, _ := model.GetHome(ctx.Backend)
|
||||
if home == "" {
|
||||
SendSuccessResult(res, true)
|
||||
return
|
||||
}
|
||||
SendSuccessResult(res, true)
|
||||
r.IsAuth = true
|
||||
r.Home = NewString(home)
|
||||
SendSuccessResult(res, r)
|
||||
}
|
||||
|
||||
func SessionAuthenticate(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import (
|
|||
. "github.com/mickael-kerjean/nuage/server/common"
|
||||
"github.com/mickael-kerjean/nuage/server/model"
|
||||
"net/http"
|
||||
"log"
|
||||
)
|
||||
|
||||
func ShareList(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
|
|
@ -29,7 +28,6 @@ func ShareGet(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||
|
||||
func ShareUpsert(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
s := extractParams(req, &ctx)
|
||||
log.Println("EXPIRE::", s.Expire, ctx.Body["expire"])
|
||||
s.Path = NewStringFromInterface(ctx.Body["path"])
|
||||
|
||||
if err := model.ShareUpsert(&s); err != nil {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ func Init(a *App) *http.Server {
|
|||
|
||||
// API
|
||||
session := r.PathPrefix("/api/session").Subrouter()
|
||||
session.HandleFunc("", APIHandler(SessionIsValid, *a)).Methods("GET")
|
||||
session.HandleFunc("", APIHandler(SessionGet, *a)).Methods("GET")
|
||||
session.HandleFunc("", APIHandler(SessionAuthenticate, *a)).Methods("POST")
|
||||
session.HandleFunc("", APIHandler(SessionLogout, *a)).Methods("DELETE")
|
||||
session.Handle("/auth/{service}", APIHandler(SessionOAuthBackend, *a)).Methods("GET")
|
||||
|
|
|
|||
|
|
@ -19,8 +19,7 @@ let config = {
|
|||
chunkFilename: "assets/js/chunk_[name]_[id]_[chunkhash].js"
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
rules: [{
|
||||
test: path.join(__dirname, 'client'),
|
||||
use: ['babel-loader'],
|
||||
exclude: /node_modules/
|
||||
|
|
@ -29,6 +28,10 @@ let config = {
|
|||
test: /\.html$/,
|
||||
loader: 'html-loader'
|
||||
},
|
||||
{
|
||||
test: /\.woff2$/,
|
||||
loader: 'woff-loader'
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
loaders: ['style-loader', 'css-loader', 'sass-loader']
|
||||
|
|
@ -55,20 +58,21 @@ let config = {
|
|||
new webpack.optimize.OccurrenceOrderPlugin(),
|
||||
new HtmlWebpackPlugin({
|
||||
template: path.join(__dirname, 'client', 'index.html'),
|
||||
inject:true
|
||||
inject: true
|
||||
}),
|
||||
new CopyWebpackPlugin([
|
||||
{ from: 'manifest.json', to: "assets/" },
|
||||
{ from: 'worker/*.js', to: "assets/" },
|
||||
{ from: 'assets/logo/*' },
|
||||
{ from: 'assets/icons/*' }
|
||||
{ from: 'assets/icons/*' },
|
||||
{ from: 'assets/fonts/*' }
|
||||
], { context: path.join(__dirname, 'client') }),
|
||||
//new BundleAnalyzerPlugin()
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
if(process.env.NODE_ENV === 'production'){
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
config.plugins.push(new UglifyJSPlugin({
|
||||
sourceMap: false
|
||||
}));
|
||||
|
|
@ -79,8 +83,8 @@ if(process.env.NODE_ENV === 'production'){
|
|||
threshold: 0,
|
||||
minRatio: 0.8
|
||||
}));
|
||||
}else{
|
||||
} else {
|
||||
config.devtool = '#inline-source-map';
|
||||
}
|
||||
|
||||
module.exports = config;
|
||||
module.exports = config;
|
||||
Loading…
Reference in a new issue