mirror of
https://github.com/mickael-kerjean/filestash
synced 2026-01-06 07:50:40 +01:00
maintenance (code): incremental improvement
This commit is contained in:
parent
32f7bb8875
commit
bde4079fb9
16 changed files with 151 additions and 76 deletions
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,7 +6,7 @@ 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';
|
||||
|
|
@ -14,6 +14,8 @@ import InfiniteScroll from 'react-infinite-scroller';
|
|||
const PAGE_NUMBER_INIT = 3;
|
||||
const LOAD_PER_SCROLL = 24;
|
||||
|
||||
@ErrorPage
|
||||
@LoggedInOnly
|
||||
@EventReceiver
|
||||
@DragDropContext(('ontouchstart' in window)? HTML5Backend : HTML5Backend)
|
||||
export class FilesPage extends React.Component {
|
||||
|
|
@ -30,8 +32,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 +51,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() {
|
||||
|
|
@ -76,10 +76,6 @@ export class FilesPage extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
hideError(){
|
||||
this.setState({error: null});
|
||||
}
|
||||
|
||||
toggleHiddenFilesVisibilityonCtrlK(e){
|
||||
if(e.keyCode === 72 && e.ctrlKey === true){
|
||||
e.preventDefault();
|
||||
|
|
@ -118,10 +114,9 @@ export class FilesPage extends React.Component {
|
|||
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}));
|
||||
}
|
||||
|
|
@ -213,7 +208,7 @@ export class FilesPage extends React.Component {
|
|||
<div 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 +220,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>
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Reference in a new issue