feat: support storage provider to terms of use file (#221)

Signed-off-by: WindSpiritSR <simon343riley@gmail.com>
This commit is contained in:
WindSpiritSR 2021-08-10 10:43:33 +08:00 committed by Yang Luo
parent 718fc4df74
commit 8c6f0a31b6
9 changed files with 113 additions and 55 deletions

View File

@ -15,7 +15,6 @@
package controllers package controllers
import ( import (
"encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"strconv" "strconv"
@ -236,26 +235,38 @@ func (c *ApiController) UploadFile() {
c.ResponseError("No storage provider is found") c.ResponseError("No storage provider is found")
return return
} }
file, header, err := c.GetFile("file")
fileString := c.Ctx.Request.Form.Get("file") defer file.Close()
if err != nil {
var fileBytes []byte c.ResponseError("Missing parameter")
pngHeader := "data:image/png;base64," return
if strings.HasPrefix(fileString, pngHeader) {
fileBytes, _ = base64.StdEncoding.DecodeString(fileString[len(pngHeader):])
} else {
fileBytes = []byte(fileString)
} }
fileUrl, err := object.UploadFile(provider, folder, subFolder, fileBytes) fileType := header.Header.Get("Content-Type")
fileSuffix := ""
switch fileType {
case "image/png":
fileSuffix = "png"
case "text/html":
fileSuffix = "html"
}
fileUrl, err := object.UploadFile(provider, folder, subFolder, file, fileSuffix)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
if folder == "avatar" { switch folder {
case "avatar":
user.Avatar = fileUrl user.Avatar = fileUrl
object.UpdateUser(user.GetId(), user) object.UpdateUser(user.GetId(), user)
case "termsofuse":
appId := fmt.Sprintf("admin/%s", strings.Split(subFolder, "/")[0])
app := object.GetApplication(appId)
app.TermsOfUse = fileUrl
object.UpdateApplication(appId, app)
} }
c.ResponseOk(fileUrl) c.ResponseOk(fileUrl)

View File

@ -16,7 +16,6 @@ package controllers
import ( import (
"encoding/json" "encoding/json"
"github.com/casbin/casdoor/object" "github.com/casbin/casdoor/object"
) )

View File

@ -17,13 +17,15 @@ package object
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io"
"mime/multipart"
"strings" "strings"
"github.com/casbin/casdoor/storage" "github.com/casbin/casdoor/storage"
"github.com/casbin/casdoor/util" "github.com/casbin/casdoor/util"
) )
func UploadFile(provider *Provider, folder string, subFolder string, fileBytes []byte) (string, error) { func UploadFile(provider *Provider, folder string, subFolder string, file multipart.File, suffix string) (string, error) {
storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, provider.Endpoint) storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, provider.Endpoint)
if storageProvider == nil { if storageProvider == nil {
return "", fmt.Errorf("the provider type: %s is not supported", provider.Type) return "", fmt.Errorf("the provider type: %s is not supported", provider.Type)
@ -34,8 +36,12 @@ func UploadFile(provider *Provider, folder string, subFolder string, fileBytes [
UpdateProvider(provider.GetId(), provider) UpdateProvider(provider.GetId(), provider)
} }
path := fmt.Sprintf("%s/%s.png", util.UrlJoin(util.GetUrlPath(provider.Domain), folder), subFolder) path := fmt.Sprintf("%s/%s.%s", util.UrlJoin(util.GetUrlPath(provider.Domain), folder), subFolder, suffix)
_, err := storageProvider.Put(path, bytes.NewReader(fileBytes)) fileBuf := bytes.NewBuffer(nil)
if _, err := io.Copy(fileBuf, file); err != nil {
return "", err
}
_, err := storageProvider.Put(path, fileBuf)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -13,12 +13,13 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import {Button, Card, Col, Input, Row, Select, Switch} from 'antd'; import {Button, Card, Col, Input, Row, Select, Switch, Upload} from 'antd';
import {LinkOutlined} from "@ant-design/icons"; import {LinkOutlined, UploadOutlined} from "@ant-design/icons";
import * as ApplicationBackend from "./backend/ApplicationBackend"; import * as ApplicationBackend from "./backend/ApplicationBackend";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import * as ProviderBackend from "./backend/ProviderBackend"; import * as ProviderBackend from "./backend/ProviderBackend";
import * as OrganizationBackend from "./backend/OrganizationBackend"; import * as OrganizationBackend from "./backend/OrganizationBackend";
import * as UserBackend from "./backend/UserBackend";
import SignupPage from "./auth/SignupPage"; import SignupPage from "./auth/SignupPage";
import LoginPage from "./auth/LoginPage"; import LoginPage from "./auth/LoginPage";
import i18next from "i18next"; import i18next from "i18next";
@ -26,8 +27,6 @@ import UrlTable from "./UrlTable";
import ProviderTable from "./ProviderTable"; import ProviderTable from "./ProviderTable";
import SignupTable from "./SignupTable"; import SignupTable from "./SignupTable";
import PromptPage from "./auth/PromptPage"; import PromptPage from "./auth/PromptPage";
const { TextArea } = Input;
const { Option } = Select; const { Option } = Select;
class ApplicationEditPage extends React.Component { class ApplicationEditPage extends React.Component {
@ -39,6 +38,7 @@ class ApplicationEditPage extends React.Component {
application: null, application: null,
organizations: [], organizations: [],
providers: [], providers: [],
uploading: false,
}; };
} }
@ -92,6 +92,25 @@ class ApplicationEditPage extends React.Component {
}); });
} }
handleUpload(info) {
if (info.file.type !== "text/html") {
Setting.showMessage("error", i18next.t("provider:Please select a HTML file"))
return
}
this.setState({uploading: true})
UserBackend.uploadFile("termsofuse", `${this.state.applicationName}/termsofuse`, info.file)
.then(res => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Upload success"))
this.updateApplicationField("termsOfUse", res.data)
} else {
Setting.showMessage("error", res.msg)
}
}).finally(() => {
this.setState({uploading: false})
})
}
renderApplication() { renderApplication() {
return ( return (
<Card size="small" title={ <Card size="small" title={
@ -286,9 +305,13 @@ class ApplicationEditPage extends React.Component {
{Setting.getLabel(i18next.t("provider:Terms of Use"), i18next.t("provider:Terms of Use - Tooltip"))} : {Setting.getLabel(i18next.t("provider:Terms of Use"), i18next.t("provider:Terms of Use - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<TextArea autoSize={{minRows: 1, maxRows: 6}} value={this.state.application.termsOfUse} onChange={e => { <Input value={this.state.application.termsOfUse} style={{marginBottom: "10px"}} onChange={e => {
this.updateApplicationField('termsOfUse', e.target.value); this.updateApplicationField("termsOfUse", e.target.value)
}} /> }}/>
<Upload maxCount={1} accept=".html" showUploadList={false}
beforeUpload={file => {return false}} onChange={info => {this.handleUpload(info)}}>
<Button icon={<UploadOutlined />} loading={this.state.uploading}>Click to Upload</Button>
</Upload>
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: '20px'}} > <Row style={{marginTop: '20px'}} >

View File

@ -49,15 +49,23 @@ export const CropperDiv = (props) => {
}; };
const uploadAvatar = () => { const uploadAvatar = () => {
let canvas = cropper.getCroppedCanvas(); cropper.getCroppedCanvas().toBlob(blob => {
if (canvas === null) { if (blob === null) {
Setting.showMessage("error", "You must select a picture first!"); Setting.showMessage("error", "You must select a picture first!");
return false; return false;
} }
// Setting.showMessage("success", "uploading..."); // Setting.showMessage("success", "uploading...");
const userId = `${user.owner}/${user.name}`; const userId = `${user.owner}/${user.name}`;
UserBackend.uploadFile("avatar", userId, canvas.toDataURL()); UserBackend.uploadFile("avatar", userId, blob)
return true; .then((res) => {
if (res.status === "ok") {
window.location.href = "/account";
} else {
Setting.showMessage("error", res.msg);
}
});
return true;
});
} }
const showModal = () => { const showModal = () => {

View File

@ -72,6 +72,7 @@ class SignupPage extends React.Component {
validPhone: false, validPhone: false,
region: "", region: "",
isTermsOfUseVisible: false, isTermsOfUseVisible: false,
termsOfUseContent: "",
}; };
this.form = React.createRef(); this.form = React.createRef();
@ -95,6 +96,7 @@ class SignupPage extends React.Component {
this.setState({ this.setState({
application: application, application: application,
}); });
this.getTermsofuseContent(application.termsOfUse);
}); });
} }
@ -118,6 +120,16 @@ class SignupPage extends React.Component {
} }
} }
getTermsofuseContent(url) {
fetch(url, {
method: "GET",
}).then(r => {
r.text().then(res => {
this.setState({termsOfUseContent: res})
})
})
}
onUpdateAccount(account) { onUpdateAccount(account) {
this.props.onUpdateAccount(account); this.props.onUpdateAccount(account);
} }
@ -407,23 +419,25 @@ class SignupPage extends React.Component {
<Modal <Modal
title={i18next.t("signup:Terms of Use")} title={i18next.t("signup:Terms of Use")}
visible={this.state.isTermsOfUseVisible} visible={this.state.isTermsOfUseVisible}
width={"55vw"}
closable={false} closable={false}
footer={[ okText={i18next.t("signup:Accept")}
<Button key="agree" type="primary" onClick={() => { cancelText={i18next.t("signup:Decline")}
this.setState({ onOk={() => {
isTermsOfUseVisible: false, this.form.current.setFieldsValue({agreement: true})
}); this.setState({
}}> isTermsOfUseVisible: false,
{i18next.t("user:OK")} });
</Button>, }}
// <Button key="decline" onClick={() => { onCancel={() => {
// this.props.history.goBack(); this.form.current.setFieldsValue({agreement: false})
// }}> this.setState({
// {i18next.t("signup:Decline")} isTermsOfUseVisible: false,
// </Button>, });
]} this.props.history.goBack();
}}
> >
<div dangerouslySetInnerHTML={{__html: this.state.application?.termsOfUse}} /> <iframe style={{border: 0, width: "100%", height: "60vh"}} srcDoc={this.state.termsOfUseContent}/>
</Modal> </Modal>
) )
} }

View File

@ -13,6 +13,7 @@
// limitations under the License. // limitations under the License.
import * as Setting from "../Setting"; import * as Setting from "../Setting";
import * as AuthBackend from "../auth/AuthBackend";
export function getApplications(owner) { export function getApplications(owner) {
return fetch(`${Setting.ServerUrl}/api/get-applications?owner=${owner}`, { return fetch(`${Setting.ServerUrl}/api/get-applications?owner=${owner}`, {

View File

@ -79,18 +79,11 @@ export function getAffiliationOptions(url, code) {
export function uploadFile(folder, subFolder, file) { export function uploadFile(folder, subFolder, file) {
let formData = new FormData(); let formData = new FormData();
formData.append("file", file); formData.append("file", file);
fetch(`${Setting.ServerUrl}/api/upload-file?folder=${encodeURIComponent(folder)}&subFolder=${encodeURIComponent(subFolder)}`, { return fetch(`${Setting.ServerUrl}/api/upload-file?folder=${encodeURIComponent(folder)}&subFolder=${encodeURIComponent(subFolder)}`, {
body: formData, body: formData,
method: 'POST', method: 'POST',
credentials: 'include', credentials: 'include',
}).then(res => res.json()) }).then(res => res.json())
.then((res) => {
if (res.status === "ok") {
window.location.href = "/account";
} else {
Setting.showMessage("error", res.msg);
}
});
} }
export function setPassword(userOwner, userName, oldPassword, newPassword) { export function setPassword(userOwner, userName, oldPassword, newPassword) {

View File

@ -81,6 +81,9 @@
"Signin URL - Tooltip": "sign in url", "Signin URL - Tooltip": "sign in url",
"ID - Tooltip": "random string", "ID - Tooltip": "random string",
"Favicon - Tooltip": "Application icon", "Favicon - Tooltip": "Application icon",
"Uploading": "Uploading",
"Start Upload": "Start Upload",
"Upload success": "Upload success",
"Back Home": "Back Home", "Back Home": "Back Home",
"Sorry, the page you visited does not exist": "Sorry, the page you visited does not exist" "Sorry, the page you visited does not exist": "Sorry, the page you visited does not exist"
}, },