This commit is contained in:
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"git.aiterp.net/rpdata/api/space"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
@@ -19,12 +20,27 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
db, err := database.Init(config.Global().Database)
|
cfg := config.Global()
|
||||||
|
|
||||||
|
db, err := database.Init(cfg.Database)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("Failed to init db:", err)
|
log.Fatalln("Failed to init db:", err)
|
||||||
}
|
}
|
||||||
defer db.Close(context.Background())
|
defer db.Close(context.Background())
|
||||||
serviceBundle := services.NewBundle(db)
|
|
||||||
|
var spaceClient *space.Client
|
||||||
|
if cfg.Space.Enabled {
|
||||||
|
spaceClient, err = space.Connect(cfg.Space)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to init space:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Space loaded")
|
||||||
|
} else {
|
||||||
|
log.Println("Space is disabled, file upload will not work!")
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceBundle := services.NewBundle(db, spaceClient)
|
||||||
|
|
||||||
instrumentation.Register()
|
instrumentation.Register()
|
||||||
|
|
||||||
@@ -83,6 +99,8 @@ func logListedChanges(changes *services.ChangeService) {
|
|||||||
func queryHandler(services *services.Bundle) http.HandlerFunc {
|
func queryHandler(services *services.Bundle) http.HandlerFunc {
|
||||||
handler := handler.GraphQL(
|
handler := handler.GraphQL(
|
||||||
graph2.New(services),
|
graph2.New(services),
|
||||||
|
handler.UploadMaxSize(10485760),
|
||||||
|
handler.UploadMaxMemory(1048576),
|
||||||
handler.RecoverFunc(func(ctx context.Context, err interface{}) error {
|
handler.RecoverFunc(func(ctx context.Context, err interface{}) error {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
log.Println(string(debug.Stack()))
|
log.Println(string(debug.Stack()))
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ type Database interface {
|
|||||||
Comments() repositories.CommentRepository
|
Comments() repositories.CommentRepository
|
||||||
Keys() repositories.KeyRepository
|
Keys() repositories.KeyRepository
|
||||||
Users() repositories.UserRepository
|
Users() repositories.UserRepository
|
||||||
|
Files() repositories.FileRepository
|
||||||
|
|
||||||
Close(ctx context.Context) error
|
Close(ctx context.Context) error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,6 +77,37 @@ func (r *characterRepository) FindNick(ctx context.Context, nick string) (*model
|
|||||||
return character, nil
|
return character, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *characterRepository) FindName(ctx context.Context, name string) (*models.Character, error) {
|
||||||
|
query := bson.M{
|
||||||
|
"$or": []bson.M{
|
||||||
|
{"shortName": name},
|
||||||
|
{"name": name},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for all characters matching query
|
||||||
|
characters := make([]*models.Character, 0, 8)
|
||||||
|
err := r.characters.Find(query).All(&characters)
|
||||||
|
if err != nil {
|
||||||
|
if err == mgo.ErrNotFound {
|
||||||
|
return nil, repositories.ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
} else if len(characters) == 0 {
|
||||||
|
return nil, repositories.ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prioritize exact match
|
||||||
|
for _, character := range characters {
|
||||||
|
if character.Name == name {
|
||||||
|
return character, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return characters[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *characterRepository) List(ctx context.Context, filter models.CharacterFilter) ([]*models.Character, error) {
|
func (r *characterRepository) List(ctx context.Context, filter models.CharacterFilter) ([]*models.Character, error) {
|
||||||
query := bson.M{}
|
query := bson.M{}
|
||||||
if filter.Author != nil {
|
if filter.Author != nil {
|
||||||
|
|||||||
@@ -2,9 +2,12 @@ package resolvers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"git.aiterp.net/rpdata/api/graph2/graphcore"
|
||||||
"git.aiterp.net/rpdata/api/models"
|
"git.aiterp.net/rpdata/api/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Queries
|
||||||
|
|
||||||
func (r *queryResolver) File(ctx context.Context, id string) (*models.File, error) {
|
func (r *queryResolver) File(ctx context.Context, id string) (*models.File, error) {
|
||||||
return r.s.Files.Find(ctx, id)
|
return r.s.Files.Find(ctx, id)
|
||||||
}
|
}
|
||||||
@@ -20,3 +23,9 @@ func (r *queryResolver) Files(ctx context.Context, filter *models.FileFilter) ([
|
|||||||
|
|
||||||
return r.s.Files.List(ctx, *filter)
|
return r.s.Files.List(ctx, *filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mutations
|
||||||
|
|
||||||
|
func (r *mutationResolver) UploadFile(ctx context.Context, input *graphcore.FileUploadInput) (*models.File, error) {
|
||||||
|
return r.s.Files.Upload(ctx, input.File.File, input.Name, input.Public, input.File.Size)
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
type queryResolver struct{ s *services.Bundle }
|
type queryResolver struct{ s *services.Bundle }
|
||||||
type mutationResolver struct{ s *services.Bundle }
|
type mutationResolver struct{ s *services.Bundle }
|
||||||
|
|
||||||
type subscriptionResolver struct{ s *services.Bundle }
|
type subscriptionResolver struct{ s *services.Bundle }
|
||||||
|
|
||||||
// QueryResolver has all the queries
|
// QueryResolver has all the queries
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ type Mutation {
|
|||||||
"Edit a comment in a chapter."
|
"Edit a comment in a chapter."
|
||||||
editComment(input: CommentEditInput!): Comment!
|
editComment(input: CommentEditInput!): Comment!
|
||||||
|
|
||||||
"Remove a comemnt in a chapter."
|
"Remove a comment in a chapter."
|
||||||
removeComment(input: CommentRemoveInput!): Comment!
|
removeComment(input: CommentRemoveInput!): Comment!
|
||||||
|
|
||||||
|
|
||||||
@@ -164,6 +164,10 @@ type Mutation {
|
|||||||
|
|
||||||
# Edit a channel
|
# Edit a channel
|
||||||
editChannel(input: ChannelEditInput!): Channel!
|
editChannel(input: ChannelEditInput!): Channel!
|
||||||
|
|
||||||
|
|
||||||
|
"Upload a file"
|
||||||
|
uploadFile(input: FileUploadInput): File!
|
||||||
}
|
}
|
||||||
|
|
||||||
type Subscription {
|
type Subscription {
|
||||||
@@ -176,3 +180,5 @@ type Subscription {
|
|||||||
# A Time represents a RFC3339 encoded date with up to millisecond precision.
|
# A Time represents a RFC3339 encoded date with up to millisecond precision.
|
||||||
scalar Time
|
scalar Time
|
||||||
|
|
||||||
|
"""A GraphQL file upload."""
|
||||||
|
scalar Upload
|
||||||
@@ -64,6 +64,8 @@ enum ChangeModel {
|
|||||||
Story
|
Story
|
||||||
Tag
|
Tag
|
||||||
Chapter
|
Chapter
|
||||||
|
Comment
|
||||||
|
File
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -38,9 +38,21 @@ input FilesFilter {
|
|||||||
mimeTypes: [String!]
|
mimeTypes: [String!]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Input for uploadFile mutation
|
||||||
|
input FileUploadInput {
|
||||||
|
"The file name"
|
||||||
|
name: String!
|
||||||
|
|
||||||
|
# Whether the file should be public
|
||||||
|
public: Boolean!
|
||||||
|
|
||||||
|
# The actual file upload
|
||||||
|
file: Upload!
|
||||||
|
}
|
||||||
|
|
||||||
# Input for editFile mutation
|
# Input for editFile mutation
|
||||||
input EditFileInput {
|
input EditFileInput {
|
||||||
# The file's unique ID
|
# The file's unique ID
|
||||||
id: String!
|
id: String!
|
||||||
|
|
||||||
# Whether the file is publicly listable. Someone with knowledge of the ID
|
# Whether the file is publicly listable. Someone with knowledge of the ID
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ type Space struct {
|
|||||||
Bucket string `json:"bucket" yaml:"bucket"`
|
Bucket string `json:"bucket" yaml:"bucket"`
|
||||||
MaxSize int64 `json:"maxSize" yaml:"maxSize"`
|
MaxSize int64 `json:"maxSize" yaml:"maxSize"`
|
||||||
Root string `json:"root" yaml:"root"`
|
Root string `json:"root" yaml:"root"`
|
||||||
|
URLRoot string `json:"urlRoot" yaml:"urlRoot"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Database is configuration for spaces.
|
// Database is configuration for spaces.
|
||||||
|
|||||||
@@ -63,6 +63,11 @@ func FileID() string {
|
|||||||
return ID("F", 16)
|
return ID("F", 16)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FileID generates a file ID.
|
||||||
|
func FileUploadID() string {
|
||||||
|
return ID("U", 24)
|
||||||
|
}
|
||||||
|
|
||||||
func KeyID() string {
|
func KeyID() string {
|
||||||
return ID("K", 32)
|
return ID("K", 32)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ const (
|
|||||||
ChangeModelChapter ChangeModel = "Chapter"
|
ChangeModelChapter ChangeModel = "Chapter"
|
||||||
// ChangeModelComment is a value of ChangeModel
|
// ChangeModelComment is a value of ChangeModel
|
||||||
ChangeModelComment ChangeModel = "Comment"
|
ChangeModelComment ChangeModel = "Comment"
|
||||||
|
// ChangeModelFile is a value of ChangeModel
|
||||||
|
ChangeModelFile ChangeModel = "File"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsValid returns true if the underlying string is one of the correct values.
|
// IsValid returns true if the underlying string is one of the correct values.
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ type Change struct {
|
|||||||
Tags []*Tag `bson:"tags"`
|
Tags []*Tag `bson:"tags"`
|
||||||
Chapters []*Chapter `bson:"chapters"`
|
Chapters []*Chapter `bson:"chapters"`
|
||||||
Comments []*Comment `bson:"comments"`
|
Comments []*Comment `bson:"comments"`
|
||||||
|
Files []*File `bson:"files"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddObject adds the model into the appropriate array.
|
// AddObject adds the model into the appropriate array.
|
||||||
@@ -68,6 +69,10 @@ func (change *Change) AddObject(object interface{}) bool {
|
|||||||
change.Comments = append(change.Comments, object)
|
change.Comments = append(change.Comments, object)
|
||||||
case []*Comment:
|
case []*Comment:
|
||||||
change.Comments = append(change.Comments, object...)
|
change.Comments = append(change.Comments, object...)
|
||||||
|
case *File:
|
||||||
|
change.Files = append(change.Files, object)
|
||||||
|
case []*File:
|
||||||
|
change.Files = append(change.Files, object...)
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -103,6 +108,9 @@ func (change *Change) Objects() []interface{} {
|
|||||||
for _, comment := range change.Comments {
|
for _, comment := range change.Comments {
|
||||||
data = append(data, comment)
|
data = append(data, comment)
|
||||||
}
|
}
|
||||||
|
for _, file := range change.Files {
|
||||||
|
data = append(data, file)
|
||||||
|
}
|
||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
type CharacterRepository interface {
|
type CharacterRepository interface {
|
||||||
Find(ctx context.Context, id string) (*models.Character, error)
|
Find(ctx context.Context, id string) (*models.Character, error)
|
||||||
FindNick(ctx context.Context, nick string) (*models.Character, error)
|
FindNick(ctx context.Context, nick string) (*models.Character, error)
|
||||||
|
FindName(ctx context.Context, name string) (*models.Character, error)
|
||||||
List(ctx context.Context, filter models.CharacterFilter) ([]*models.Character, error)
|
List(ctx context.Context, filter models.CharacterFilter) ([]*models.Character, error)
|
||||||
Insert(ctx context.Context, character models.Character) (*models.Character, error)
|
Insert(ctx context.Context, character models.Character) (*models.Character, error)
|
||||||
Update(ctx context.Context, character models.Character, update models.CharacterUpdate) (*models.Character, error)
|
Update(ctx context.Context, character models.Character, update models.CharacterUpdate) (*models.Character, error)
|
||||||
|
|||||||
@@ -71,7 +71,12 @@ func (s *CharacterService) Create(ctx context.Context, nick, name, shortName, au
|
|||||||
}
|
}
|
||||||
|
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return nil, errors.New("Name cannot be empty")
|
return nil, errors.New("name cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert nick into existing if character already exists.
|
||||||
|
if character, err := s.characters.FindName(ctx, name); err == nil && character.Name == name {
|
||||||
|
return s.AddNick(ctx, character.ID, nick)
|
||||||
}
|
}
|
||||||
|
|
||||||
if author == "" {
|
if author == "" {
|
||||||
|
|||||||
+45
-5
@@ -3,10 +3,15 @@ package services
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"git.aiterp.net/rpdata/api/internal/generate"
|
||||||
"git.aiterp.net/rpdata/api/models"
|
"git.aiterp.net/rpdata/api/models"
|
||||||
|
"git.aiterp.net/rpdata/api/models/changekeys"
|
||||||
"git.aiterp.net/rpdata/api/repositories"
|
"git.aiterp.net/rpdata/api/repositories"
|
||||||
|
"git.aiterp.net/rpdata/api/space"
|
||||||
"github.com/h2non/filetype"
|
"github.com/h2non/filetype"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrPrivateNoAuthor = errors.New("cannot search for private files without an author")
|
var ErrPrivateNoAuthor = errors.New("cannot search for private files without an author")
|
||||||
@@ -18,8 +23,10 @@ var ErrCouldNotUploadFile = errors.New("could not upload file")
|
|||||||
|
|
||||||
// FileService is a service for files.
|
// FileService is a service for files.
|
||||||
type FileService struct {
|
type FileService struct {
|
||||||
files repositories.FileRepository
|
files repositories.FileRepository
|
||||||
authService *AuthService
|
authService *AuthService
|
||||||
|
changeService *ChangeService
|
||||||
|
space *space.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FileService) Find(ctx context.Context, id string) (*models.File, error) {
|
func (s *FileService) Find(ctx context.Context, id string) (*models.File, error) {
|
||||||
@@ -56,13 +63,18 @@ func (s *FileService) List(ctx context.Context, filter models.FileFilter) ([]*mo
|
|||||||
return s.files.List(ctx, filter)
|
return s.files.List(ctx, filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FileService) Upload(ctx context.Context, reader io.Reader, name string, size int64) (*models.File, error) {
|
func (s *FileService) Upload(ctx context.Context, reader io.Reader, name string, public bool, size int64) (*models.File, error) {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return nil, ErrInvalidName
|
return nil, ErrInvalidName
|
||||||
} else if size < 320 || size > 16777216 {
|
} else if size < 320 || size > 16777216 {
|
||||||
return nil, ErrInvalidFileSize
|
return nil, ErrInvalidFileSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
token := s.authService.TokenFromContext(ctx)
|
||||||
|
if token == nil || !token.Permitted("file.upload", "member") {
|
||||||
|
return nil, ErrUnauthorized
|
||||||
|
}
|
||||||
|
|
||||||
head := make([]byte, 320)
|
head := make([]byte, 320)
|
||||||
n, err := reader.Read(head)
|
n, err := reader.Read(head)
|
||||||
if err != nil || n < 320 {
|
if err != nil || n < 320 {
|
||||||
@@ -74,7 +86,35 @@ func (s *FileService) Upload(ctx context.Context, reader io.Reader, name string,
|
|||||||
return nil, ErrInvalidFileType
|
return nil, ErrInvalidFileType
|
||||||
}
|
}
|
||||||
|
|
||||||
panic("implement rest of me")
|
reader2 := &concatReader{head: head, body: reader}
|
||||||
|
|
||||||
|
path := generate.FileUploadID() + "." + fileType.Extension
|
||||||
|
err = s.space.UploadFile(ctx, path, fileType.MIME.Value, reader2, size)
|
||||||
|
if err != nil || !allowedMimeTypes[fileType.MIME.Value] {
|
||||||
|
log.Println("File upload failed:", err)
|
||||||
|
|
||||||
|
return nil, ErrCouldNotUploadFile
|
||||||
|
}
|
||||||
|
|
||||||
|
file := &models.File{
|
||||||
|
Size: size,
|
||||||
|
Name: name,
|
||||||
|
URL: s.space.URLFromPath(path),
|
||||||
|
Time: time.Now(),
|
||||||
|
Author: token.UserID,
|
||||||
|
Kind: "upload",
|
||||||
|
MimeType: fileType.MIME.Value,
|
||||||
|
Public: public,
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err = s.files.Insert(ctx, *file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.changeService.Submit(ctx, models.ChangeModelFile, "upload", file.Public, changekeys.Listed(file), file)
|
||||||
|
|
||||||
|
return file, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FileService) Edit(ctx context.Context, id string, name *string, public *bool) (*models.File, error) {
|
func (s *FileService) Edit(ctx context.Context, id string, name *string, public *bool) (*models.File, error) {
|
||||||
@@ -122,7 +162,7 @@ func (r *concatReader) Read(p []byte) (n int, err error) {
|
|||||||
r.headPos = len(r.head)
|
r.headPos = len(r.head)
|
||||||
copy(p, remainder)
|
copy(p, remainder)
|
||||||
|
|
||||||
return len(p), nil
|
return len(remainder), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.body.Read(p)
|
return r.body.Read(p)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package services
|
|||||||
import (
|
import (
|
||||||
"git.aiterp.net/rpdata/api/database"
|
"git.aiterp.net/rpdata/api/database"
|
||||||
"git.aiterp.net/rpdata/api/services/loaders"
|
"git.aiterp.net/rpdata/api/services/loaders"
|
||||||
|
"git.aiterp.net/rpdata/api/space"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Bundle contains all services.
|
// A Bundle contains all services.
|
||||||
@@ -18,20 +19,22 @@ type Bundle struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewBundle creates a new bundle.
|
// NewBundle creates a new bundle.
|
||||||
func NewBundle(db database.Database) *Bundle {
|
func NewBundle(db database.Database, spaceClient *space.Client) *Bundle {
|
||||||
bundle := &Bundle{}
|
bundle := &Bundle{}
|
||||||
bundle.Auth = &AuthService{
|
bundle.Auth = &AuthService{
|
||||||
keys: db.Keys(),
|
keys: db.Keys(),
|
||||||
users: db.Users(),
|
users: db.Users(),
|
||||||
}
|
}
|
||||||
bundle.Files = &FileService{
|
|
||||||
files: nil,
|
|
||||||
authService: bundle.Auth,
|
|
||||||
}
|
|
||||||
bundle.Changes = &ChangeService{
|
bundle.Changes = &ChangeService{
|
||||||
changes: db.Changes(),
|
changes: db.Changes(),
|
||||||
authService: bundle.Auth,
|
authService: bundle.Auth,
|
||||||
}
|
}
|
||||||
|
bundle.Files = &FileService{
|
||||||
|
files: db.Files(),
|
||||||
|
authService: bundle.Auth,
|
||||||
|
changeService: bundle.Changes,
|
||||||
|
space: spaceClient,
|
||||||
|
}
|
||||||
bundle.Tags = &TagService{tags: db.Tags()}
|
bundle.Tags = &TagService{tags: db.Tags()}
|
||||||
bundle.Characters = &CharacterService{
|
bundle.Characters = &CharacterService{
|
||||||
characters: db.Characters(),
|
characters: db.Characters(),
|
||||||
|
|||||||
+15
-10
@@ -25,29 +25,27 @@ func (client *Client) MaxSize() int64 {
|
|||||||
|
|
||||||
// UploadFile uploads the file to the space. This does not do any checks on it, so the endpoints should
|
// UploadFile uploads the file to the space. This does not do any checks on it, so the endpoints should
|
||||||
// ensure that's all okay.
|
// ensure that's all okay.
|
||||||
func (client *Client) UploadFile(ctx context.Context, folder string, name string, mimeType string, reader io.Reader, size int64) (string, error) {
|
func (client *Client) UploadFile(ctx context.Context, name string, mimeType string, reader io.Reader, size int64) error {
|
||||||
path := folder + "/" + name
|
|
||||||
|
|
||||||
if size > client.maxSize {
|
if size > client.maxSize {
|
||||||
return "", errors.New("file is too big")
|
return errors.New("file is too big")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := client.s3.PutObjectWithContext(ctx, client.bucket, client.spaceRoot+"/"+path, reader, size, minio.PutObjectOptions{
|
_, err := client.s3.PutObjectWithContext(ctx, client.bucket, client.spaceRoot+"/"+name, reader, size, minio.PutObjectOptions{
|
||||||
ContentType: mimeType,
|
ContentType: mimeType,
|
||||||
UserMetadata: map[string]string{
|
UserMetadata: map[string]string{
|
||||||
"x-amz-acl": "public-read",
|
"x-amz-acl": "public-read",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = client.s3.StatObject(client.bucket, client.spaceRoot+"/"+path, minio.StatObjectOptions{})
|
_, err = client.s3.StatObject(client.bucket, client.spaceRoot+"/"+name, minio.StatObjectOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return path, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveFile removes a file from the space
|
// RemoveFile removes a file from the space
|
||||||
@@ -83,9 +81,16 @@ func Connect(cfg config.Space) (*Client, error) {
|
|||||||
return nil, errors.New("bucket not found")
|
return nil, errors.New("bucket not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
urlRoot := cfg.URLRoot
|
||||||
|
if urlRoot == "" {
|
||||||
|
urlRoot = fmt.Sprintf("https://%s.%s/%s/", cfg.Bucket, cfg.Host, cfg.Root)
|
||||||
|
} else {
|
||||||
|
urlRoot += "/" + cfg.Root + "/"
|
||||||
|
}
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
bucket: cfg.Bucket,
|
bucket: cfg.Bucket,
|
||||||
urlRoot: fmt.Sprintf("https://%s.%s/%s/", cfg.Bucket, cfg.Host, cfg.Root),
|
urlRoot: urlRoot,
|
||||||
spaceRoot: cfg.Root,
|
spaceRoot: cfg.Root,
|
||||||
maxSize: cfg.MaxSize,
|
maxSize: cfg.MaxSize,
|
||||||
s3: client,
|
s3: client,
|
||||||
|
|||||||
Reference in New Issue
Block a user