@@ -2,6 +2,7 @@ package controllers | |||||
import ( | import ( | ||||
"encoding/json" | "encoding/json" | ||||
"mime/multipart" | |||||
"github.com/casbin/casbase/object" | "github.com/casbin/casbase/object" | ||||
) | ) | ||||
@@ -23,9 +24,21 @@ func (c *ApiController) UpdateFile() { | |||||
func (c *ApiController) AddFile() { | func (c *ApiController) AddFile() { | ||||
storeId := c.Input().Get("store") | storeId := c.Input().Get("store") | ||||
key := c.Input().Get("key") | key := c.Input().Get("key") | ||||
newFolder := c.Input().Get("newFolder") | |||||
isLeaf := c.Input().Get("isLeaf") == "1" | |||||
filename := c.Input().Get("filename") | |||||
var file multipart.File | |||||
if isLeaf { | |||||
var err error | |||||
file, _, err = c.GetFile("file") | |||||
if err != nil { | |||||
c.ResponseError(err.Error()) | |||||
return | |||||
} | |||||
defer file.Close() | |||||
} | |||||
c.Data["json"] = object.AddFile(storeId, key, newFolder) | |||||
c.Data["json"] = object.AddFile(storeId, key, isLeaf, filename, file) | |||||
c.ServeJSON() | c.ServeJSON() | ||||
} | } | ||||
@@ -1,7 +1,10 @@ | |||||
package object | package object | ||||
import ( | import ( | ||||
"bytes" | |||||
"fmt" | "fmt" | ||||
"io" | |||||
"mime/multipart" | |||||
"github.com/casbin/casbase/storage" | "github.com/casbin/casbase/storage" | ||||
) | ) | ||||
@@ -10,14 +13,27 @@ func UpdateFile(storeId string, key string, file *File) bool { | |||||
return true | return true | ||||
} | } | ||||
func AddFile(storeId string, key string, newFolder string) bool { | |||||
func AddFile(storeId string, key string, isLeaf bool, filename string, file multipart.File) bool { | |||||
store := GetStore(storeId) | store := GetStore(storeId) | ||||
if store == nil { | if store == nil { | ||||
return false | return false | ||||
} | } | ||||
objectKey := fmt.Sprintf("%s/%s/_hidden.ini", key, newFolder) | |||||
storage.PutObject(store.Bucket, objectKey) | |||||
var objectKey string | |||||
var fileBuffer *bytes.Buffer | |||||
if isLeaf { | |||||
objectKey = fmt.Sprintf("%s/%s", key, filename) | |||||
fileBuffer = bytes.NewBuffer(nil) | |||||
if _, err := io.Copy(fileBuffer, file); err != nil { | |||||
panic(err) | |||||
} | |||||
} else { | |||||
objectKey = fmt.Sprintf("%s/%s/_hidden.ini", key, filename) | |||||
fileBuffer = bytes.NewBuffer(nil) | |||||
} | |||||
storage.PutObject(store.Bucket, objectKey, fileBuffer) | |||||
return true | return true | ||||
} | } | ||||
@@ -29,12 +45,11 @@ func DeleteFile(storeId string, key string, isLeaf bool) bool { | |||||
if isLeaf { | if isLeaf { | ||||
storage.DeleteObject(store.Bucket, key) | storage.DeleteObject(store.Bucket, key) | ||||
return true | |||||
} else { | } else { | ||||
objects := storage.ListObjects(store.Bucket, key) | objects := storage.ListObjects(store.Bucket, key) | ||||
for _, object := range objects { | for _, object := range objects { | ||||
storage.DeleteObject(store.Bucket, object.Key) | storage.DeleteObject(store.Bucket, object.Key) | ||||
} | } | ||||
return true | |||||
} | } | ||||
return true | |||||
} | } |
@@ -48,11 +48,9 @@ func ListObjects(bucketName string, prefix string) []oss.ObjectProperties { | |||||
return res | return res | ||||
} | } | ||||
func PutObject(bucketName string, key string) { | |||||
func PutObject(bucketName string, key string, fileBuffer *bytes.Buffer) { | |||||
bucket := getBucket(bucketName) | bucket := getBucket(bucketName) | ||||
fileBuffer := bytes.NewBuffer(nil) | |||||
err := bucket.PutObject(key, fileBuffer) | err := bucket.PutObject(key, fileBuffer) | ||||
if err != nil { | if err != nil { | ||||
panic(err) | panic(err) | ||||
@@ -1,5 +1,5 @@ | |||||
import React from "react"; | import React from "react"; | ||||
import {Button, Col, Empty, Input, Popconfirm, Row, Spin, Tooltip, Tree} from 'antd'; | |||||
import {Button, Col, Empty, Input, Popconfirm, Row, Spin, Tooltip, Tree, Upload} from 'antd'; | |||||
import {CloudUploadOutlined, createFromIconfontCN, DeleteOutlined, EditOutlined, FolderAddOutlined, RadiusSettingOutlined} from "@ant-design/icons"; | import {CloudUploadOutlined, createFromIconfontCN, DeleteOutlined, EditOutlined, FolderAddOutlined, RadiusSettingOutlined} from "@ant-design/icons"; | ||||
import * as Setting from "./Setting"; | import * as Setting from "./Setting"; | ||||
import * as FileBackend from "./backend/FileBackend"; | import * as FileBackend from "./backend/FileBackend"; | ||||
@@ -86,9 +86,24 @@ class FileTree extends React.Component { | |||||
}; | }; | ||||
} | } | ||||
uploadFile(file, info) { | |||||
const storeId = `${this.props.store.owner}/${this.props.store.name}`; | |||||
// this.setState({uploading: true}); | |||||
const filename = info.fileList[0].name; | |||||
FileBackend.addFile(storeId, file.key, true, filename, info.file) | |||||
.then(res => { | |||||
Setting.showMessage("success", `File uploaded successfully`); | |||||
window.location.reload(); | |||||
}) | |||||
.catch(error => { | |||||
Setting.showMessage("error", `File failed to upload: ${error}`); | |||||
}); | |||||
}; | |||||
addFile(file, newFolder) { | addFile(file, newFolder) { | ||||
const storeId = `${this.props.store.owner}/${this.props.store.name}`; | const storeId = `${this.props.store.owner}/${this.props.store.name}`; | ||||
FileBackend.addFile(storeId, file.key, newFolder) | |||||
FileBackend.addFile(storeId, file.key, false, newFolder, null) | |||||
.then((res) => { | .then((res) => { | ||||
Setting.showMessage("success", `File added successfully`); | Setting.showMessage("success", `File added successfully`); | ||||
window.location.reload(); | window.location.reload(); | ||||
@@ -303,10 +318,16 @@ class FileTree extends React.Component { | |||||
}} /> | }} /> | ||||
</Tooltip> | </Tooltip> | ||||
<Tooltip title={i18next.t("store:Upload file")}> | <Tooltip title={i18next.t("store:Upload file")}> | ||||
<Button style={{marginRight: "5px"}} icon={<CloudUploadOutlined />} size="small" onClick={(e) => { | |||||
Setting.showMessage("error", "Upload file"); | |||||
e.stopPropagation(); | |||||
}} /> | |||||
<Upload maxCount={1} accept="*" showUploadList={false} beforeUpload={file => {return false;}} onChange={info => { | |||||
this.uploadFile(file, info); | |||||
}} | |||||
> | |||||
<Button style={{marginRight: "5px"}} icon={<CloudUploadOutlined />} size="small" /> | |||||
</Upload> | |||||
{/*<Button style={{marginRight: "5px"}} icon={<CloudUploadOutlined />} size="small" onClick={(e) => {*/} | |||||
{/* Setting.showMessage("error", "Upload file");*/} | |||||
{/* e.stopPropagation();*/} | |||||
{/*}} />*/} | |||||
</Tooltip> | </Tooltip> | ||||
<Tooltip title={i18next.t("store:Delete")}> | <Tooltip title={i18next.t("store:Delete")}> | ||||
<span onClick={(e) => e.stopPropagation()}> | <span onClick={(e) => e.stopPropagation()}> | ||||
@@ -404,7 +425,7 @@ class FileTree extends React.Component { | |||||
); | ); | ||||
} | } | ||||
if (["txt", "html", "js", "css", "md"].includes(ext)) { | |||||
if (["txt", "htm", "html", "js", "css", "md"].includes(ext)) { | |||||
if (this.state.loading) { | if (this.state.loading) { | ||||
return ( | return ( | ||||
<div className="App"> | <div className="App"> | ||||
@@ -9,10 +9,13 @@ export function updateFile(storeId, name, file) { | |||||
}).then(res => res.json()); | }).then(res => res.json()); | ||||
} | } | ||||
export function addFile(storeId, key, newFolder) { | |||||
return fetch(`${Setting.ServerUrl}/api/add-file?store=${storeId}&key=${key}&newFolder=${newFolder}`, { | |||||
export function addFile(storeId, key, isLeaf, filename, file) { | |||||
let formData = new FormData(); | |||||
formData.append("file", file); | |||||
return fetch(`${Setting.ServerUrl}/api/add-file?store=${storeId}&key=${key}&isLeaf=${isLeaf ? 1 : 0}&filename=${filename}`, { | |||||
method: 'POST', | method: 'POST', | ||||
credentials: 'include', | credentials: 'include', | ||||
body: formData, | |||||
}).then(res => res.json()); | }).then(res => res.json()); | ||||
} | } | ||||