mirror of
https://github.com/khairul169/garage-webui.git
synced 2026-06-12 13:36:21 +07:00
Merge 80cf7fbaaab57659dbb60c7fd0bccf6a53e615d8 into ee420fbf2946e9f79977615cee5e29192d7da478
This commit is contained in:
commit
6d748c3c28
@ -24,7 +24,7 @@ type Browse struct{}
|
|||||||
|
|
||||||
func (b *Browse) GetObjects(w http.ResponseWriter, r *http.Request) {
|
func (b *Browse) GetObjects(w http.ResponseWriter, r *http.Request) {
|
||||||
query := r.URL.Query()
|
query := r.URL.Query()
|
||||||
bucket := r.PathValue("bucket")
|
bucketId := r.PathValue("bucket")
|
||||||
prefix := query.Get("prefix")
|
prefix := query.Get("prefix")
|
||||||
continuationToken := query.Get("next")
|
continuationToken := query.Get("next")
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ func (b *Browse) GetObjects(w http.ResponseWriter, r *http.Request) {
|
|||||||
limit = 100
|
limit = 100
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := getS3Client(bucket)
|
client, bucket, err := getS3Client(bucketId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.ResponseError(w, err)
|
utils.ResponseError(w, err)
|
||||||
return
|
return
|
||||||
@ -73,7 +73,7 @@ func (b *Browse) GetObjects(w http.ResponseWriter, r *http.Request) {
|
|||||||
ObjectKey: &key,
|
ObjectKey: &key,
|
||||||
LastModified: object.LastModified,
|
LastModified: object.LastModified,
|
||||||
Size: object.Size,
|
Size: object.Size,
|
||||||
Url: fmt.Sprintf("/browse/%s/%s", bucket, *object.Key),
|
Url: fmt.Sprintf("/browse/%s/%s", bucketId, *object.Key),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,14 +81,14 @@ func (b *Browse) GetObjects(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Browse) GetOneObject(w http.ResponseWriter, r *http.Request) {
|
func (b *Browse) GetOneObject(w http.ResponseWriter, r *http.Request) {
|
||||||
bucket := r.PathValue("bucket")
|
bucketId := r.PathValue("bucket")
|
||||||
key := r.PathValue("key")
|
key := r.PathValue("key")
|
||||||
queryParams := r.URL.Query()
|
queryParams := r.URL.Query()
|
||||||
view := queryParams.Get("view") == "1"
|
view := queryParams.Get("view") == "1"
|
||||||
thumbnail := queryParams.Get("thumb") == "1"
|
thumbnail := queryParams.Get("thumb") == "1"
|
||||||
download := queryParams.Get("dl") == "1"
|
download := queryParams.Get("dl") == "1"
|
||||||
|
|
||||||
client, err := getS3Client(bucket)
|
client, bucket, err := getS3Client(bucketId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.ResponseError(w, err)
|
utils.ResponseError(w, err)
|
||||||
return
|
return
|
||||||
@ -170,7 +170,7 @@ func (b *Browse) GetOneObject(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Browse) PutObject(w http.ResponseWriter, r *http.Request) {
|
func (b *Browse) PutObject(w http.ResponseWriter, r *http.Request) {
|
||||||
bucket := r.PathValue("bucket")
|
bucketId := r.PathValue("bucket")
|
||||||
key := r.PathValue("key")
|
key := r.PathValue("key")
|
||||||
isDirectory := strings.HasSuffix(key, "/")
|
isDirectory := strings.HasSuffix(key, "/")
|
||||||
|
|
||||||
@ -184,7 +184,7 @@ func (b *Browse) PutObject(w http.ResponseWriter, r *http.Request) {
|
|||||||
defer file.Close()
|
defer file.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := getS3Client(bucket)
|
client, bucket, err := getS3Client(bucketId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.ResponseError(w, err)
|
utils.ResponseError(w, err)
|
||||||
return
|
return
|
||||||
@ -215,12 +215,12 @@ func (b *Browse) PutObject(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Browse) DeleteObject(w http.ResponseWriter, r *http.Request) {
|
func (b *Browse) DeleteObject(w http.ResponseWriter, r *http.Request) {
|
||||||
bucket := r.PathValue("bucket")
|
bucketId := r.PathValue("bucket")
|
||||||
key := r.PathValue("key")
|
key := r.PathValue("key")
|
||||||
recursive := r.URL.Query().Get("recursive") == "true"
|
recursive := r.URL.Query().Get("recursive") == "true"
|
||||||
isDirectory := strings.HasSuffix(key, "/")
|
isDirectory := strings.HasSuffix(key, "/")
|
||||||
|
|
||||||
client, err := getS3Client(bucket)
|
client, bucket, err := getS3Client(bucketId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.ResponseError(w, err)
|
utils.ResponseError(w, err)
|
||||||
return
|
return
|
||||||
@ -284,15 +284,23 @@ func (b *Browse) DeleteObject(w http.ResponseWriter, r *http.Request) {
|
|||||||
utils.ResponseSuccess(w, res)
|
utils.ResponseSuccess(w, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getBucketCredentials(bucket string) (aws.CredentialsProvider, error) {
|
type bucketAccess struct {
|
||||||
cacheKey := fmt.Sprintf("key:%s", bucket)
|
Credentials aws.CredentialsProvider
|
||||||
cacheData := utils.Cache.Get(cacheKey)
|
S3Bucket string
|
||||||
|
|
||||||
if cacheData != nil {
|
|
||||||
return cacheData.(aws.CredentialsProvider), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body, err := utils.Garage.Fetch("/v2/GetBucketInfo?globalAlias="+bucket, &utils.FetchOptions{})
|
func getBucketAccess(bucketId string) (*bucketAccess, error) {
|
||||||
|
if bucketId == "" {
|
||||||
|
return nil, errors.New("bucket id is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheKey := fmt.Sprintf("bucket-access:%s", bucketId)
|
||||||
|
cacheData := utils.Cache.Get(cacheKey)
|
||||||
|
if cacheData != nil {
|
||||||
|
return cacheData.(*bucketAccess), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := utils.Garage.Fetch("/v2/GetBucketInfo?id="+bucketId, &utils.FetchOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -303,11 +311,24 @@ func getBucketCredentials(bucket string) (aws.CredentialsProvider, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var key schema.KeyElement
|
var key schema.KeyElement
|
||||||
|
bucket := ""
|
||||||
|
if len(bucketData.GlobalAliases) > 0 {
|
||||||
|
bucket = bucketData.GlobalAliases[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
found := false
|
||||||
for _, k := range bucketData.Keys {
|
for _, k := range bucketData.Keys {
|
||||||
if !k.Permissions.Read || !k.Permissions.Write {
|
if !k.Permissions.Read || !k.Permissions.Write {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// If no global alias, use local alias instead
|
||||||
|
if bucket == "" {
|
||||||
|
if len(k.BucketLocalAliases) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
bucket = k.BucketLocalAliases[0]
|
||||||
|
}
|
||||||
|
found = true
|
||||||
|
|
||||||
body, err := utils.Garage.Fetch(fmt.Sprintf("/v2/GetKeyInfo?id=%s&showSecretKey=true", k.AccessKeyID), &utils.FetchOptions{})
|
body, err := utils.Garage.Fetch(fmt.Sprintf("/v2/GetKeyInfo?id=%s&showSecretKey=true", k.AccessKeyID), &utils.FetchOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -318,17 +339,25 @@ func getBucketCredentials(bucket string) (aws.CredentialsProvider, error) {
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
if !found {
|
||||||
credential := credentials.NewStaticCredentialsProvider(key.AccessKeyID, key.SecretAccessKey, "")
|
return nil, errors.New("no read-write key for bucket")
|
||||||
utils.Cache.Set(cacheKey, credential, time.Hour)
|
|
||||||
|
|
||||||
return credential, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getS3Client(bucket string) (*s3.Client, error) {
|
if bucket == "" {
|
||||||
creds, err := getBucketCredentials(bucket)
|
return nil, errors.New("bucket has no alias")
|
||||||
|
}
|
||||||
|
access := &bucketAccess{
|
||||||
|
Credentials: credentials.NewStaticCredentialsProvider(key.AccessKeyID, key.SecretAccessKey, ""),
|
||||||
|
S3Bucket: bucket,
|
||||||
|
}
|
||||||
|
utils.Cache.Set(cacheKey, access, time.Hour)
|
||||||
|
return access, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getS3Client(bucketId string) (*s3.Client, string, error) {
|
||||||
|
access, err := getBucketAccess(bucketId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot get credentials for bucket %s: %w", bucket, err)
|
return nil, "", fmt.Errorf("cannot get credentials for bucket %s: %w", bucketId, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine endpoint and whether to disable HTTPS
|
// Determine endpoint and whether to disable HTTPS
|
||||||
@ -337,7 +366,7 @@ func getS3Client(bucket string) (*s3.Client, error) {
|
|||||||
|
|
||||||
// AWS config without BaseEndpoint
|
// AWS config without BaseEndpoint
|
||||||
awsConfig := aws.Config{
|
awsConfig := aws.Config{
|
||||||
Credentials: creds,
|
Credentials: access.Credentials,
|
||||||
Region: utils.Garage.GetS3Region(),
|
Region: utils.Garage.GetS3Region(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,5 +382,5 @@ func getS3Client(bucket string) (*s3.Client, error) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return client, nil
|
return client, access.S3Bucket, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,13 +18,13 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Actions = ({ prefix }: Props) => {
|
const Actions = ({ prefix }: Props) => {
|
||||||
const { bucketName } = useBucketContext();
|
const { bucket } = useBucketContext();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const putObject = usePutObject(bucketName, {
|
const putObject = usePutObject(bucket.id, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast.success("File uploaded!");
|
toast.success("File uploaded!");
|
||||||
queryClient.invalidateQueries({ queryKey: ["browse", bucketName] });
|
queryClient.invalidateQueries({ queryKey: ["browse", bucket.id] });
|
||||||
},
|
},
|
||||||
onError: handleError,
|
onError: handleError,
|
||||||
});
|
});
|
||||||
@ -76,7 +76,7 @@ type CreateFolderActionProps = {
|
|||||||
|
|
||||||
const CreateFolderAction = ({ prefix }: CreateFolderActionProps) => {
|
const CreateFolderAction = ({ prefix }: CreateFolderActionProps) => {
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
const { bucketName } = useBucketContext();
|
const { bucket } = useBucketContext();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const form = useForm<CreateFolderSchema>({
|
const form = useForm<CreateFolderSchema>({
|
||||||
@ -88,10 +88,10 @@ const CreateFolderAction = ({ prefix }: CreateFolderActionProps) => {
|
|||||||
if (isOpen) form.setFocus("name");
|
if (isOpen) form.setFocus("name");
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
||||||
const createFolder = usePutObject(bucketName, {
|
const createFolder = usePutObject(bucket.id, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast.success("Folder created!");
|
toast.success("Folder created!");
|
||||||
queryClient.invalidateQueries({ queryKey: ["browse", bucketName] });
|
queryClient.invalidateQueries({ queryKey: ["browse", bucket.id] });
|
||||||
onClose();
|
onClose();
|
||||||
form.reset();
|
form.reset();
|
||||||
},
|
},
|
||||||
|
|||||||
@ -11,18 +11,18 @@ import {
|
|||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
export const useBrowseObjects = (
|
export const useBrowseObjects = (
|
||||||
bucket: string,
|
bucketId: string,
|
||||||
options?: UseBrowserObjectOptions
|
options?: UseBrowserObjectOptions
|
||||||
) => {
|
) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["browse", bucket, options],
|
queryKey: ["browse", bucketId, options],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
api.get<GetObjectsResult>(`/browse/${bucket}`, { params: options }),
|
api.get<GetObjectsResult>(`/browse/${bucketId}`, { params: options }),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const usePutObject = (
|
export const usePutObject = (
|
||||||
bucket: string,
|
bucketId: string,
|
||||||
options?: UseMutationOptions<any, Error, PutObjectPayload>
|
options?: UseMutationOptions<any, Error, PutObjectPayload>
|
||||||
) => {
|
) => {
|
||||||
return useMutation({
|
return useMutation({
|
||||||
@ -32,19 +32,19 @@ export const usePutObject = (
|
|||||||
formData.append("file", body.file);
|
formData.append("file", body.file);
|
||||||
}
|
}
|
||||||
|
|
||||||
return api.put(`/browse/${bucket}/${body.key}`, { body: formData });
|
return api.put(`/browse/${bucketId}/${body.key}`, { body: formData });
|
||||||
},
|
},
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useDeleteObject = (
|
export const useDeleteObject = (
|
||||||
bucket: string,
|
bucketId: string,
|
||||||
options?: UseMutationOptions<any, Error, { key: string; recursive?: boolean }>
|
options?: UseMutationOptions<any, Error, { key: string; recursive?: boolean }>
|
||||||
) => {
|
) => {
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: (data) =>
|
mutationFn: (data) =>
|
||||||
api.delete(`/browse/${bucket}/${data.key}`, {
|
api.delete(`/browse/${bucketId}/${data.key}`, {
|
||||||
params: { recursive: data.recursive },
|
params: { recursive: data.recursive },
|
||||||
}),
|
}),
|
||||||
...options,
|
...options,
|
||||||
|
|||||||
@ -17,14 +17,14 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ObjectActions = ({ prefix = "", object, end }: Props) => {
|
const ObjectActions = ({ prefix = "", object, end }: Props) => {
|
||||||
const { bucketName } = useBucketContext();
|
const { bucket } = useBucketContext();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const isDirectory = object.objectKey.endsWith("/");
|
const isDirectory = object.objectKey.endsWith("/");
|
||||||
|
|
||||||
const deleteObject = useDeleteObject(bucketName, {
|
const deleteObject = useDeleteObject(bucket.id, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast.success("Object deleted!");
|
toast.success("Object deleted!");
|
||||||
queryClient.invalidateQueries({ queryKey: ["browse", bucketName] });
|
queryClient.invalidateQueries({ queryKey: ["browse", bucket.id] });
|
||||||
},
|
},
|
||||||
onError: handleError,
|
onError: handleError,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -21,8 +21,8 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ObjectList = ({ prefix, onPrefixChange }: Props) => {
|
const ObjectList = ({ prefix, onPrefixChange }: Props) => {
|
||||||
const { bucketName } = useBucketContext();
|
const { bucket } = useBucketContext();
|
||||||
const { data, error, isLoading } = useBrowseObjects(bucketName, {
|
const { data, error, isLoading } = useBrowseObjects(bucket.id, {
|
||||||
prefix,
|
prefix,
|
||||||
limit: 1000,
|
limit: 1000,
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user