diff --git a/client/model/share.js b/client/model/share.js
index 469989f7..b6aca7c9 100644
--- a/client/model/share.js
+++ b/client/model/share.js
@@ -4,8 +4,13 @@ class ShareModel {
constructor(){}
all(path = "/"){
- const url = `api/share?path=${path}`;
- return http_get(url);
+ const url = `/api/share?path=${path}`;
+ return http_get(url).then((res) => res.results);
+ }
+
+ get(id){
+ const url = `/api/share/${id}`;
+ return http_get(url).then((res) => res.result);
}
upsert(obj){
diff --git a/client/pages/filespage/share.js b/client/pages/filespage/share.js
index 56e29722..35b5e105 100644
--- a/client/pages/filespage/share.js
+++ b/client/pages/filespage/share.js
@@ -13,42 +13,33 @@ export class ShareComponent extends React.Component {
show_advanced: false,
role: null,
id: randomString(7),
- existings: [
- {id: "dflkjse", role: "UPLOADER", path: "./test/test"},
- {id: "dflkjse", role: "VIEWER", path: "./test/test", password: "xxxx"},
- {id: "dflkjse", role: "EDITOR", path: "./test/test"},
- {id: "dflkjse", role: "VIEWER", path: "./test/test", password: "xxxx"},
- {id: "dflkjse", role: "EDITOR", path: "./test/test"},
- {id: "dflkjse", role: "UPLOADER", path: "./test/test"},
- ]
+ existings: []
};
}
+ componentDidMount(){
+ Share.all(this.props.path)
+ .then((existings) => {
+ this.refreshModal();
+ this.setState({existings: existings});
+ });
+ }
+
updateState(key, value){
if(this.state[key] === value){
this.setState({[key]: null});
}else{
this.setState({[key]: value});
}
-
- if(key === "role" && value && window.innerHeight < 500){
- window.dispatchEvent(new Event('resize'));
+ if(key === "role" && value){
+ this.refreshModal();
}
}
- registerLink(e){
- e.target.setSelectionRange(0, e.target.value.length);
- let st = Object.assign({}, this.state);
- delete st.existings;
- delete st.show_advanced;
- this.setState({existing: [st].concat(this.state.existings)});
- return Share.upsert(st)
- .catch((err) => {
- notify.send(err, "error");
- this.setState({
- existings: this.state.existings.slice(0, this.state.existings.length)
- });
- });
+ refreshModal(){
+ if(window.innerHeight < 500){
+ window.dispatchEvent(new Event('resize'));
+ }
}
onLoad(link){
@@ -59,14 +50,44 @@ export class ShareComponent extends React.Component {
this.setState(st);
}
- onDelete(link_id){
+ onDeleteLink(link_id){
+ let removed = null,
+ i = 0;
+
+ for(i=0; i < this.state.existings.length; i++){
+ if(this.state.existings[i].id === link_id){
+ removed = Object.assign({}, this.state.existings[i]);
+ break;
+ }
+ }
+ if(removed !== null){
+ this.state.existings.splice(i, 1);
+ this.setState({existings: this.state.existings});
+ }
+
return Share.remove(link_id)
- .then(() => {
- console.log("HERE");
- })
- .catch((err) => notify.send(err, "error"));
+ .catch((err) => {
+ this.setState({existings: [removed].concat(this.state.existings)});
+ notify.send(err, "error");
+ });
}
+ onRegisterLink(e){
+ e.target.setSelectionRange(0, e.target.value.length);
+ let st = Object.assign({}, this.state);
+ delete st.existings;
+ delete st.show_advanced;
+ this.setState({existings: [st].concat(this.state.existings)});
+ return Share.upsert(st)
+ .catch((err) => {
+ notify.send(err, "error");
+ this.setState({
+ existings: this.state.existings.slice(0, this.state.existings.length)
+ });
+ });
+ }
+
+
render(){
return (
@@ -90,10 +111,10 @@ export class ShareComponent extends React.Component {
{
this.state.existings && this.state.existings.map((link, i) => {
return (
-
+
{link.role}
{link.path}
-
+
);
@@ -105,7 +126,7 @@ export class ShareComponent extends React.Component {
Restrictions
-
+
@@ -124,7 +145,7 @@ export class ShareComponent extends React.Component {
- {}}/>
+ {}}/>
diff --git a/client/pages/filespage/share.scss b/client/pages/filespage/share.scss
index d1f346db..033f4cb9 100644
--- a/client/pages/filespage/share.scss
+++ b/client/pages/filespage/share.scss
@@ -48,6 +48,7 @@
color: var(--light);
display: inline-block;
min-width: 75px;
+ text-transform: uppercase;
}
.component_icon{
width: 20px;
diff --git a/client/pages/filespage/thing-existing.js b/client/pages/filespage/thing-existing.js
index db6fca51..3568d079 100644
--- a/client/pages/filespage/thing-existing.js
+++ b/client/pages/filespage/thing-existing.js
@@ -185,7 +185,7 @@ export class ExistingThing extends React.Component {
onShareRequest(filename){
alert.now(
- ,
+ ,
(ok) => {}
);
}
@@ -313,7 +313,7 @@ const ActionButton = (props) => {
-
+
diff --git a/client/pages/filespage/thing.scss b/client/pages/filespage/thing.scss
index 6d4869e4..32b91acc 100644
--- a/client/pages/filespage/thing.scss
+++ b/client/pages/filespage/thing.scss
@@ -111,6 +111,7 @@
margin: 2px;
padding: 0;
position: relative;
+ border: 3px solid transparent;
> span > img{
padding: 0;
margin: 0;
diff --git a/client/pages/index.js b/client/pages/index.js
index aa1e8783..2b78c18d 100644
--- a/client/pages/index.js
+++ b/client/pages/index.js
@@ -1,4 +1,5 @@
export { HomePage } from './homepage';
+export { SharePage } from './sharepage';
export { ConnectPage } from './connectpage';
export { LogoutPage } from './logout';
export { NotFoundPage } from './notfoundpage';
diff --git a/client/pages/sharepage.js b/client/pages/sharepage.js
new file mode 100644
index 00000000..267b82f5
--- /dev/null
+++ b/client/pages/sharepage.js
@@ -0,0 +1,75 @@
+import React from 'react';
+import { Redirect } from 'react-router';
+
+import { Share } from '../model/';
+import { notify } from '../helpers/';
+import { Loader, Input, Button, Container } from '../components/';
+import './error.scss';
+
+export class SharePage extends React.Component {
+ constructor(props){
+ super(props);
+ this.state = {
+ redirection: null,
+ loading: true,
+ request_password: false,
+ request_username: false
+ };
+ }
+
+ componentDidMount(){
+ Share.get(this.props.match.params.id)
+ .then((res) => {
+ this.setState({
+ loading: false,
+ redirection: res
+ });
+ })
+ .catch((res) => {
+ console.log(">> COMPONENT DID MOUNT:: ", res);
+ this.setState({
+ loading: false
+ });
+ });
+ }
+ render() {
+ if(this.state.loading === true){
+ return (
);
+ }
+
+ if(this.state.request_password === true){
+ return (
+
+
+
+ );
+ }else if(this.state.request_username === true){
+ return (
+
+
+
+ );
+ }
+
+ if(this.state.redirection !== null){
+ if(this.state.redirection.slice(-1) === "/"){
+ return ( );
+ }else{
+ return ( );
+ }
+ }else{
+ return (
+
+
Oops!
+ There's nothing in here
+
+ );
+ }
+ }
+}
diff --git a/client/pages/viewerpage/imageviewer.scss b/client/pages/viewerpage/imageviewer.scss
index 0c726574..92421727 100644
--- a/client/pages/viewerpage/imageviewer.scss
+++ b/client/pages/viewerpage/imageviewer.scss
@@ -135,6 +135,7 @@
margin: 5px 0;
border-top: 1px dashed var(--color);
padding-top: 5px;
+ clear: both;
.title{ float: left; margin-right: 5px; }
.value{ color: var(--bg-color); }
}
diff --git a/client/router.js b/client/router.js
index 093123b3..cf95d642 100644
--- a/client/router.js
+++ b/client/router.js
@@ -1,6 +1,6 @@
import React from 'react';
import { BrowserRouter, Route, IndexRoute, Switch } from 'react-router-dom';
-import { NotFoundPage, ConnectPage, HomePage, LogoutPage, FilesPage, ViewerPage } from './pages/';
+import { NotFoundPage, ConnectPage, HomePage, SharePage, LogoutPage, FilesPage, ViewerPage } from './pages/';
import { Bundle, URL_HOME, URL_FILES, URL_VIEWER, URL_LOGIN, URL_LOGOUT } from './helpers/';
import { ModalPrompt, ModalAlert, ModalConfirm, Notification, Audio, Video } from './components/';
@@ -11,6 +11,7 @@ export default class AppRouter extends React.Component {
+
diff --git a/server/common/crypto.go b/server/common/crypto.go
index 412897b9..d8eb8a32 100644
--- a/server/common/crypto.go
+++ b/server/common/crypto.go
@@ -4,6 +4,8 @@ import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
+ "crypto/sha1"
+ "encoding/base32"
"encoding/base64"
"encoding/json"
"io"
@@ -49,3 +51,18 @@ func Decrypt(keystr string, cryptoText string) (map[string]string, error) {
json.Unmarshal(ciphertext, &raw)
return raw, nil
}
+
+func GenerateID(params map[string]string) string {
+ p := "type =>" + params["type"]
+ p += "host =>" + params["host"]
+ p += "hostname =>" + params["hostname"]
+ p += "username =>" + params["username"]
+ p += "repo =>" + params["repo"]
+ p += "access_key_id =>" + params["access_key_id"]
+ p += "endpoint =>" + params["endpoint"]
+ p += "bearer =>" + params["bearer"]
+ p += "token =>" + params["token"]
+ hasher := sha1.New()
+ hasher.Write([]byte(p))
+ return base32.HexEncoding.EncodeToString(hasher.Sum(nil))
+}
diff --git a/server/common/utils.go b/server/common/utils.go
index bdeab37b..cf9322d3 100644
--- a/server/common/utils.go
+++ b/server/common/utils.go
@@ -17,3 +17,7 @@ func RandomString(n int) string {
func NewBool(t bool) *bool {
return &t
}
+
+func NewString(t string) *string {
+ return &t
+}
diff --git a/server/ctrl/share.go b/server/ctrl/share.go
index 8c423b1f..96342003 100644
--- a/server/ctrl/share.go
+++ b/server/ctrl/share.go
@@ -8,27 +8,35 @@ import (
)
type ShareAPI struct {
- Id string `json:"id"`
- Path string `json:"path"`
- Role string `json:"role"`
- Password string `json:"password"`
- Users []string `json:"users"`
- CanManageOwn bool `json:"can_manage_own"`
- CanShare bool `json:"can_share"`
- Expire int `json:"expire"`
- Link string `json:"link"`
+ Id string `json:"id"`
+ Path string `json:"path"`
+ Role string `json:"role"`
+ Password *string `json:"password"`
+ Users *[]string `json:"users"`
+ CanManageOwn *bool `json:"can_manage_own"`
+ CanShare *bool `json:"can_share"`
+ Expire *int `json:"expire"`
+ CustomURI *string `json:"uri"`
}
func ShareList(ctx App, res http.ResponseWriter, req *http.Request) {
- p := extractParams(req)
- listOfSharedLinks := model.ShareList(p)
+ s := extractParams(req, &ctx)
+ listOfSharedLinks := model.ShareList(s)
SendSuccessResults(res, listOfSharedLinks)
}
+func ShareGet(ctx App, res http.ResponseWriter, req *http.Request) {
+ s := extractParams(req, &ctx)
+ if err := model.ShareGet(&s); err != nil {
+ SendErrorResult(res, err)
+ return
+ }
+ SendSuccessResult(res, s)
+}
+
func ShareUpsert(ctx App, res http.ResponseWriter, req *http.Request) {
- p := extractParams(req)
- err := model.ShareUpsert(p, model.ShareParams{})
- if err != nil {
+ s := extractParams(req, &ctx)
+ if err := model.ShareUpsert(s); err != nil {
SendErrorResult(res, err)
return
}
@@ -36,16 +44,18 @@ func ShareUpsert(ctx App, res http.ResponseWriter, req *http.Request) {
}
func ShareDelete(ctx App, res http.ResponseWriter, req *http.Request) {
- p := extractParams(req)
- err := model.ShareDelete(p)
- if err != nil {
+ s := extractParams(req, &ctx)
+ if err := model.ShareDelete(s); err != nil {
SendErrorResult(res, err)
return
}
SendSuccessResult(res, nil)
}
-func extractParams(req *http.Request) model.ShareKey {
- vars := mux.Vars(req)
- return model.ShareKey{vars["id"], "", ""}
+func extractParams(req *http.Request, ctx *App) model.Share {
+ return model.Share{
+ Id: NewString(mux.Vars(req)["id"]),
+ Backend: NewString(GenerateID(ctx.Session)),
+ Path: NewString(req.URL.Query().Get("path")),
+ }
}
diff --git a/server/model/backend/git.go b/server/model/backend/git.go
index df4a7ddc..2b3061a5 100644
--- a/server/model/backend/git.go
+++ b/server/model/backend/git.go
@@ -3,7 +3,6 @@ package backend
import (
"fmt"
. "github.com/mickael-kerjean/nuage/server/common"
- "github.com/mitchellh/hashstructure"
"golang.org/x/crypto/ssh"
"gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/plumbing"
@@ -95,10 +94,7 @@ func NewGit(params map[string]string, app *App) (*Git, error) {
return nil, NewError("Your password doesn't fit in a cookie :/", 500)
}
- hash, err := hashstructure.Hash(params, nil)
- if err != nil {
- return nil, NewError("Internal error", 500)
- }
+ hash := GenerateID(params)
p.basePath = app.Helpers.AbsolutePath(GitCachePath + "repo_" + fmt.Sprint(hash) + "/")
repo, err := g.git.open(p, p.basePath)
diff --git a/server/model/permissions.go b/server/model/permissions.go
new file mode 100644
index 00000000..c5967088
--- /dev/null
+++ b/server/model/permissions.go
@@ -0,0 +1,9 @@
+package model
+
+func CanRemoveShare() bool {
+ return false
+}
+
+func CanEditShare() bool {
+ return false
+}
diff --git a/server/model/share.go b/server/model/share.go
new file mode 100644
index 00000000..25057cfb
--- /dev/null
+++ b/server/model/share.go
@@ -0,0 +1,135 @@
+package model
+
+import (
+ "database/sql"
+ _ "github.com/mattn/go-sqlite3"
+ . "github.com/mickael-kerjean/nuage/server/common"
+ "log"
+ "os"
+ "path/filepath"
+)
+
+const DBCachePath = "data/"
+
+type Share struct {
+ Id *string `json:"id"`
+ Backend *string `json:"-"`
+ Path *string `json:"path"`
+ Params struct {
+ } `json:"-"`
+ Role *string `json:"role"`
+ Password *string `json:"password,omitempty"`
+ Users *[]string `json:"-"`
+ Expire *int `json:"expire,omitempty"`
+ CanRead *bool `json:"-"`
+ CanWrite *bool `json:"-"`
+ CanUpload *bool `json:"-"`
+ CanShare *bool `json:"can_share,omitempty"`
+ CanManageOwn *bool `json:"can_manage_own,omitempty"`
+}
+
+func init() {
+ cachePath := filepath.Join(GetCurrentDir(), DBCachePath)
+ os.MkdirAll(cachePath, os.ModePerm)
+
+ db, err := sql.Open("sqlite3", cachePath+"/db.sql")
+ if err != nil {
+ return
+ }
+ stmt, err := db.Prepare("CREATE TABLE IF NOT EXISTS Location(backend VARCHAR(16), path VARCHAR(512), CONSTRAINT pk_location PRIMARY KEY(backend, path))")
+ if err != nil {
+ return
+ }
+ stmt.Exec()
+
+ stmt, err = db.Prepare("CREATE TABLE IF NOT EXISTS Share(id VARCHAR(64) PRIMARY KEY, related_backend VARCHAR(16), related_path VARCHAR(512), params JSON, FOREIGN KEY (related_backend, related_path) REFERENCES Location(backend, path) ON UPDATE CASCADE ON DELETE CASCADE)")
+ if err != nil {
+ return
+ }
+ stmt.Exec()
+}
+
+func ShareList(p Share) []Share {
+ db, err := getDb()
+ if err != nil {
+ return nil
+ }
+ log.Println("- backend: ", p.Backend)
+ stmt, err := db.Prepare("SELECT s.id, l.path, s.params FROM Share as s LEFT JOIN Location as l ON l.backend = s.related_backend")
+ log.Println("err1:", err)
+ if err != nil {
+ return nil
+ }
+ rows, err := stmt.Query()
+ log.Println(">> ROWS::", rows)
+ log.Println("err2:", err)
+ if err != nil {
+ return nil
+ }
+ defer rows.Close()
+
+ sharedFiles := []Share{}
+ for rows.Next() {
+ var a Share
+ //var params string
+ rows.Scan(&a.Id, &a.Path, &a.Role)
+ a.Role = NewString("viewer")
+ sharedFiles = append(sharedFiles, a)
+ }
+ return sharedFiles
+}
+
+func ShareGet(p *Share) error {
+ db, err := getDb()
+ if err != nil {
+ return err
+ }
+ stmt, err := db.Prepare("SELECT id, related_path, params FROM share WHERE id = ?")
+ if err != nil {
+ return err
+ }
+ defer stmt.Close()
+ row := stmt.QueryRow(p.Id)
+ row.Scan(&p.Id, &p.Path)
+ return nil
+}
+
+func ShareUpsert(p Share) error {
+ db, err := getDb()
+ if err != nil {
+ return err
+ }
+
+ stmt, err := db.Prepare("INSERT INTO Share(id, related_backend, related_path, params) VALUES($1, $2, $3, $4) ON CONFLICT(id) DO UPDATE SET related_backend = $,2s related_path = $3, params = $4")
+ if err != nil {
+ return err
+ }
+ _, err = stmt.Exec(p.Id, p.Backend, p.Path, "{}")
+ return err
+}
+
+func ShareDelete(p Share) error {
+ db, err := getDb()
+ if err != nil {
+ return err
+ }
+ stmt, err := db.Prepare("DELETE FROM Share WHERE id = ?")
+ if err != nil {
+ return err
+ }
+ _, err = stmt.Exec(p.Id)
+ return err
+}
+
+func getDb() (*sql.DB, error) {
+ path := filepath.Join(GetCurrentDir(), DBCachePath) + "/db.sql"
+ return sql.Open("sqlite3", path)
+}
+
+func shareToDBParams(s Share) string {
+ return ""
+}
+
+func DPParamstoShare() Share {
+ return Share{}
+}
diff --git a/server/router/index.go b/server/router/index.go
index 2995334e..631071d4 100644
--- a/server/router/index.go
+++ b/server/router/index.go
@@ -29,6 +29,7 @@ func Init(a *App) *http.Server {
share := r.PathPrefix("/api/share").Subrouter()
share.HandleFunc("", APIHandler(ShareList, *a)).Methods("GET")
+ share.HandleFunc("/{id}", APIHandler(ShareGet, *a)).Methods("GET")
share.HandleFunc("/{id}", APIHandler(ShareUpsert, *a)).Methods("POST")
share.HandleFunc("/{id}", APIHandler(ShareDelete, *a)).Methods("DELETE")