| @@ -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()); | ||||
| } | } | ||||