mirror of
https://github.com/casdoor/casdoor.git
synced 2025-05-23 02:35:49 +08:00
feat: add local file system storage provider (#224)
Signed-off-by: sh1luo <690898835@qq.com>
This commit is contained in:
parent
44150a6781
commit
c55fa4f452
1
.gitignore
vendored
1
.gitignore
vendored
@ -22,5 +22,6 @@ tmp/
|
|||||||
tmpFiles/
|
tmpFiles/
|
||||||
*.tmp
|
*.tmp
|
||||||
logs/
|
logs/
|
||||||
|
files/
|
||||||
lastupdate.tmp
|
lastupdate.tmp
|
||||||
commentsRouter*.go
|
commentsRouter*.go
|
||||||
|
@ -256,13 +256,13 @@ func (c *ApiController) UploadAvatar() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dist, _ := base64.StdEncoding.DecodeString(avatarBase64[index+1:])
|
dist, _ := base64.StdEncoding.DecodeString(avatarBase64[index+1:])
|
||||||
err := object.UploadAvatar(provider, user.GetId(), dist)
|
fileUrl, err := object.UploadAvatar(provider, user.GetId(), dist)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Avatar = fmt.Sprintf("%s/%s.png?time=%s", util.UrlJoin(provider.Domain, "/avatar"), user.GetId(), util.GetCurrentUnixTime())
|
user.Avatar = fileUrl
|
||||||
object.UpdateUser(user.GetId(), user)
|
object.UpdateUser(user.GetId(), user)
|
||||||
|
|
||||||
resp = Response{Status: "ok", Msg: ""}
|
resp = Response{Status: "ok", Msg: ""}
|
||||||
|
1
main.go
1
main.go
@ -45,6 +45,7 @@ func main() {
|
|||||||
beego.SetStaticPath("/static", "web/build/static")
|
beego.SetStaticPath("/static", "web/build/static")
|
||||||
beego.BConfig.WebConfig.DirectoryIndex = true
|
beego.BConfig.WebConfig.DirectoryIndex = true
|
||||||
beego.SetStaticPath("/swagger", "swagger")
|
beego.SetStaticPath("/swagger", "swagger")
|
||||||
|
beego.SetStaticPath("/files", "files")
|
||||||
// https://studygolang.com/articles/2303
|
// https://studygolang.com/articles/2303
|
||||||
beego.InsertFilter("*", beego.BeforeRouter, routers.StaticFilter)
|
beego.InsertFilter("*", beego.BeforeRouter, routers.StaticFilter)
|
||||||
beego.InsertFilter("*", beego.BeforeRouter, routers.AutoSigninFilter)
|
beego.InsertFilter("*", beego.BeforeRouter, routers.AutoSigninFilter)
|
||||||
|
@ -17,14 +17,16 @@ package object
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/casbin/casdoor/storage"
|
"github.com/casbin/casdoor/storage"
|
||||||
"github.com/casbin/casdoor/util"
|
"github.com/casbin/casdoor/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func UploadAvatar(provider *Provider, username string, avatar []byte) error {
|
func UploadAvatar(provider *Provider, username string, avatar []byte) (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)
|
||||||
}
|
}
|
||||||
|
|
||||||
if provider.Domain == "" {
|
if provider.Domain == "" {
|
||||||
@ -34,5 +36,22 @@ func UploadAvatar(provider *Provider, username string, avatar []byte) error {
|
|||||||
|
|
||||||
path := fmt.Sprintf("%s/%s.png", util.UrlJoin(util.GetUrlPath(provider.Domain), "/avatar"), username)
|
path := fmt.Sprintf("%s/%s.png", util.UrlJoin(util.GetUrlPath(provider.Domain), "/avatar"), username)
|
||||||
_, err := storageProvider.Put(path, bytes.NewReader(avatar))
|
_, err := storageProvider.Put(path, bytes.NewReader(avatar))
|
||||||
return err
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
host := ""
|
||||||
|
if provider.Type != "Local File System" {
|
||||||
|
// provider.Domain = "https://cdn.casbin.com/casdoor/"
|
||||||
|
host = util.GetUrlHost(provider.Domain)
|
||||||
|
if !strings.HasPrefix(host, "http://") && !strings.HasPrefix(host, "https://") {
|
||||||
|
host = fmt.Sprintf("https://%s", host)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// provider.Domain = "http://localhost:8000" or "https://door.casbin.com"
|
||||||
|
host = util.UrlJoin(provider.Domain, "/files")
|
||||||
|
}
|
||||||
|
|
||||||
|
fileUrl := fmt.Sprintf("%s?time=%s", util.UrlJoin(host, path), util.GetCurrentUnixTime())
|
||||||
|
return fileUrl, nil
|
||||||
}
|
}
|
||||||
|
129
storage/local_file_system.go
Normal file
129
storage/local_file_system.go
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qor/oss"
|
||||||
|
)
|
||||||
|
|
||||||
|
var baseFolder = "files"
|
||||||
|
|
||||||
|
// FileSystem file system storage
|
||||||
|
type FileSystem struct {
|
||||||
|
Base string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFileSystem initialize the local file system storage
|
||||||
|
func NewFileSystem(base string) *FileSystem {
|
||||||
|
absBase, err := filepath.Abs(base)
|
||||||
|
if err != nil {
|
||||||
|
panic("local file system storage's base folder is not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &FileSystem{Base: absBase}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFullPath get full path from absolute/relative path
|
||||||
|
func (fileSystem FileSystem) GetFullPath(path string) string {
|
||||||
|
fullPath := path
|
||||||
|
if !strings.HasPrefix(path, fileSystem.Base) {
|
||||||
|
fullPath, _ = filepath.Abs(filepath.Join(fileSystem.Base, path))
|
||||||
|
}
|
||||||
|
return fullPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get receive file with given path
|
||||||
|
func (fileSystem FileSystem) Get(path string) (*os.File, error) {
|
||||||
|
return os.Open(fileSystem.GetFullPath(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStream get file as stream
|
||||||
|
func (fileSystem FileSystem) GetStream(path string) (io.ReadCloser, error) {
|
||||||
|
return os.Open(fileSystem.GetFullPath(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put store a reader into given path
|
||||||
|
func (fileSystem FileSystem) Put(path string, reader io.Reader) (*oss.Object, error) {
|
||||||
|
var (
|
||||||
|
fullPath = fileSystem.GetFullPath(path)
|
||||||
|
err = os.MkdirAll(filepath.Dir(fullPath), os.ModePerm)
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dst, err := os.Create(fullPath)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
if seeker, ok := reader.(io.ReadSeeker); ok {
|
||||||
|
seeker.Seek(0, 0)
|
||||||
|
}
|
||||||
|
_, err = io.Copy(dst, reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &oss.Object{Path: path, Name: filepath.Base(path), StorageInterface: fileSystem}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete delete file
|
||||||
|
func (fileSystem FileSystem) Delete(path string) error {
|
||||||
|
return os.Remove(fileSystem.GetFullPath(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
// List list all objects under current path
|
||||||
|
func (fileSystem FileSystem) List(path string) ([]*oss.Object, error) {
|
||||||
|
var (
|
||||||
|
objects []*oss.Object
|
||||||
|
fullPath = fileSystem.GetFullPath(path)
|
||||||
|
)
|
||||||
|
|
||||||
|
filepath.Walk(fullPath, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if path == fullPath {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil && !info.IsDir() {
|
||||||
|
modTime := info.ModTime()
|
||||||
|
objects = append(objects, &oss.Object{
|
||||||
|
Path: strings.TrimPrefix(path, fileSystem.Base),
|
||||||
|
Name: info.Name(),
|
||||||
|
LastModified: &modTime,
|
||||||
|
StorageInterface: fileSystem,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return objects, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEndpoint get endpoint, FileSystem's endpoint is /
|
||||||
|
func (fileSystem FileSystem) GetEndpoint() string {
|
||||||
|
return "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetURL get public accessible URL
|
||||||
|
func (fileSystem FileSystem) GetURL(path string) (url string, err error) {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLocalFileSystemStorageProvider(clientId string, clientSecret string, region string, bucket string, endpoint string) oss.StorageInterface {
|
||||||
|
return NewFileSystem(baseFolder)
|
||||||
|
}
|
@ -18,6 +18,8 @@ import "github.com/qor/oss"
|
|||||||
|
|
||||||
func GetStorageProvider(providerType string, clientId string, clientSecret string, region string, bucket string, endpoint string) oss.StorageInterface {
|
func GetStorageProvider(providerType string, clientId string, clientSecret string, region string, bucket string, endpoint string) oss.StorageInterface {
|
||||||
switch providerType {
|
switch providerType {
|
||||||
|
case "Local File System":
|
||||||
|
return NewLocalFileSystemStorageProvider(clientId, clientSecret, region, bucket, endpoint)
|
||||||
case "AWS S3":
|
case "AWS S3":
|
||||||
return NewAwsS3StorageProvider(clientId, clientSecret, region, bucket, endpoint)
|
return NewAwsS3StorageProvider(clientId, clientSecret, region, bucket, endpoint)
|
||||||
case "Aliyun OSS":
|
case "Aliyun OSS":
|
||||||
|
@ -29,10 +29,6 @@ func FileExist(path string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func UrlJoin(base string, path string) string {
|
func UrlJoin(base string, path string) string {
|
||||||
if !strings.HasPrefix(base, "http://") && !strings.HasPrefix(base, "https://") {
|
|
||||||
base = fmt.Sprintf("https://%s", base)
|
|
||||||
}
|
|
||||||
|
|
||||||
res := fmt.Sprintf("%s/%s", strings.TrimRight(base, "/"), strings.TrimLeft(path, "/"))
|
res := fmt.Sprintf("%s/%s", strings.TrimRight(base, "/"), strings.TrimLeft(path, "/"))
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
@ -41,3 +37,8 @@ func GetUrlPath(urlString string) string {
|
|||||||
u, _ := url.Parse(urlString)
|
u, _ := url.Parse(urlString)
|
||||||
return u.Path
|
return u.Path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetUrlHost(urlString string) string {
|
||||||
|
u, _ := url.Parse(urlString)
|
||||||
|
return fmt.Sprintf("%s://%s", u.Scheme, u.Host)
|
||||||
|
}
|
||||||
|
@ -148,7 +148,8 @@ class ProviderEditPage extends React.Component {
|
|||||||
} else if (value === "SMS") {
|
} else if (value === "SMS") {
|
||||||
this.updateProviderField('type', 'Aliyun SMS');
|
this.updateProviderField('type', 'Aliyun SMS');
|
||||||
} else if (value === "Storage") {
|
} else if (value === "Storage") {
|
||||||
this.updateProviderField('type', 'Aliyun OSS');
|
this.updateProviderField('type', 'Local File System');
|
||||||
|
this.updateProviderField('domain', Setting.getFullServerUrl());
|
||||||
}
|
}
|
||||||
})}>
|
})}>
|
||||||
{
|
{
|
||||||
@ -167,7 +168,12 @@ class ProviderEditPage extends React.Component {
|
|||||||
{Setting.getLabel(i18next.t("provider:Type"), i18next.t("provider:Type - Tooltip"))} :
|
{Setting.getLabel(i18next.t("provider:Type"), i18next.t("provider:Type - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Select virtual={false} style={{width: '100%'}} value={this.state.provider.type} onChange={(value => {this.updateProviderField('type', value);})}>
|
<Select virtual={false} style={{width: '100%'}} value={this.state.provider.type} onChange={(value => {
|
||||||
|
this.updateProviderField('type', value);
|
||||||
|
if (value === "Local File System") {
|
||||||
|
this.updateProviderField('domain', Setting.getFullServerUrl());
|
||||||
|
}
|
||||||
|
})}>
|
||||||
{
|
{
|
||||||
this.getProviderTypeOptions(this.state.provider).map((providerType, index) => <Option key={index} value={providerType.id}>{providerType.name}</Option>)
|
this.getProviderTypeOptions(this.state.provider).map((providerType, index) => <Option key={index} value={providerType.id}>{providerType.name}</Option>)
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,14 @@ function isLocalhost() {
|
|||||||
return hostname === "localhost";
|
return hostname === "localhost";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getFullServerUrl() {
|
||||||
|
let fullServerUrl = window.location.origin;
|
||||||
|
if (fullServerUrl === "http://localhost:7001") {
|
||||||
|
fullServerUrl = "http://localhost:8000";
|
||||||
|
}
|
||||||
|
return fullServerUrl;
|
||||||
|
}
|
||||||
|
|
||||||
export function isProviderVisible(providerItem) {
|
export function isProviderVisible(providerItem) {
|
||||||
if (providerItem.provider === undefined || providerItem.provider === null) {
|
if (providerItem.provider === undefined || providerItem.provider === null) {
|
||||||
return false;
|
return false;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user