Skip to content

Commit adb58bb

Browse files
authored
feat: move and copy objects across buckets (#197)
1 parent 68a6197 commit adb58bb

File tree

11 files changed

+82
-20
lines changed

11 files changed

+82
-20
lines changed

infra/docker-compose.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ services:
4343
- assets-volume:/tmp/storage
4444
healthcheck:
4545
test: ['CMD-SHELL', 'curl -f -LI http://localhost:5000/status']
46+
interval: 2s
4647
db:
4748
build:
4849
context: ./postgres
@@ -63,6 +64,20 @@ services:
6364
timeout: 5s
6465
retries: 5
6566

67+
dummy_data:
68+
build:
69+
context: ./postgres
70+
depends_on:
71+
storage:
72+
condition: service_healthy
73+
volumes:
74+
- ./postgres:/sql
75+
command:
76+
- psql
77+
- "postgresql://postgres:postgres@db:5432/postgres"
78+
- -f
79+
- /sql/dummy-data.sql
80+
6681
imgproxy:
6782
image: darthsim/imgproxy
6883
ports:

infra/postgres/Dockerfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ FROM supabase/postgres:0.13.0
33
COPY 00-initial-schema.sql /docker-entrypoint-initdb.d/00-initial-schema.sql
44
COPY auth-schema.sql /docker-entrypoint-initdb.d/01-auth-schema.sql
55
COPY storage-schema.sql /docker-entrypoint-initdb.d/02-storage-schema.sql
6-
COPY dummy-data.sql /docker-entrypoint-initdb.d/03-dummy-data.sql
76

87
# Build time defaults
98
ARG build_POSTGRES_DB=postgres

infra/postgres/dummy-data.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ INSERT INTO "storage"."buckets" ("id", "name", "owner", "created_at", "updated_a
99
('bucket2', 'bucket2', '4d56e902-f0a0-4662-8448-a4d9e643c142', '2021-02-17 04:43:32.770206+00', '2021-02-17 04:43:32.770206+00'),
1010
('bucket3', 'bucket3', '4d56e902-f0a0-4662-8448-a4d9e643c142', '2021-02-17 04:43:32.770206+00', '2021-02-17 04:43:32.770206+00'),
1111
('bucket4', 'bucket4', '317eadce-631a-4429-a0bb-f19a7a517b4a', '2021-02-25 09:23:01.58385+00', '2021-02-25 09:23:01.58385+00'),
12-
('bucket5', 'bucket5', '317eadce-631a-4429-a0bb-f19a7a517b4a', '2021-02-27 03:04:25.6386+00', '2021-02-27 03:04:25.6386+00');
12+
('bucket5', 'bucket5', '317eadce-631a-4429-a0bb-f19a7a517b4a', '2021-02-27 03:04:25.6386+00', '2021-02-27 03:04:25.6386+00'),
13+
('bucket-move', 'bucket-move', '317eadce-631a-4429-a0bb-f19a7a517b4a', '2021-02-27 03:04:25.6386+00', '2021-02-27 03:04:25.6386+00');
1314

1415

1516
-- insert objects

infra/postgres/storage-schema.sql

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ CREATE TABLE "storage"."objects" (
2828
"last_accessed_at" timestamptz DEFAULT now(),
2929
"metadata" jsonb,
3030
CONSTRAINT "objects_bucketId_fkey" FOREIGN KEY ("bucket_id") REFERENCES "storage"."buckets"("id"),
31-
CONSTRAINT "objects_owner_fkey" FOREIGN KEY ("owner") REFERENCES "auth"."users"("id"),
3231
PRIMARY KEY ("id")
3332
);
3433
CREATE UNIQUE INDEX "bucketid_objname" ON "storage"."objects" USING BTREE ("bucket_id","name");
@@ -86,7 +85,7 @@ CREATE OR REPLACE FUNCTION storage.search(prefix text, bucketname text, limits i
8685
LANGUAGE plpgsql
8786
AS $function$
8887
BEGIN
89-
return query
88+
return query
9089
with files_folders as (
9190
select ((string_to_array(objects.name, '/'))[levels]) as folder
9291
from objects
@@ -95,8 +94,8 @@ BEGIN
9594
GROUP by folder
9695
limit limits
9796
offset offsets
98-
)
99-
select files_folders.folder as name, objects.id, objects.updated_at, objects.created_at, objects.last_accessed_at, objects.metadata from files_folders
97+
)
98+
select files_folders.folder as name, objects.id, objects.updated_at, objects.created_at, objects.last_accessed_at, objects.metadata from files_folders
10099
left join objects
101100
on prefix || files_folders.folder = objects.name and objects.bucket_id=bucketname;
102101
END

infra/storage/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
FROM supabase/storage-api:v0.35.1
1+
FROM supabase/storage-api:v1.0.10
22

33
RUN apk add curl --no-cache

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"types-generate": "dts-gen -m '@supabase/storage-js' -s",
3131
"test": "run-s test:clean test:infra test:suite test:clean",
3232
"test:suite": "jest --runInBand",
33-
"test:infra": "cd infra && docker-compose down && docker-compose up -d && sleep 10",
33+
"test:infra": "cd infra && docker-compose down && docker-compose up -d --build && sleep 10",
3434
"test:clean": "cd infra && docker-compose down --remove-orphans",
3535
"docs": "typedoc --entryPoints src/index.ts --out docs/v2 --entryPoints src/packages/* --excludePrivate --excludeProtected",
3636
"docs:json": "typedoc --json docs/v2/spec.json --entryPoints src/index.ts --entryPoints src/packages/* --excludePrivate --excludeExternals --excludeProtected"

src/lib/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ export interface FileOptions {
4545
duplex?: string
4646
}
4747

48+
export interface DestinationOptions {
49+
destinationBucket?: string
50+
}
51+
4852
export interface SearchOptions {
4953
/**
5054
* The number of files you want to be returned.

src/packages/StorageFileApi.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
SearchOptions,
88
FetchParameters,
99
TransformOptions,
10+
DestinationOptions,
1011
} from '../lib/types'
1112

1213
const DEFAULT_SEARCH_OPTIONS = {
@@ -68,7 +69,7 @@ export default class StorageFileApi {
6869
fileOptions?: FileOptions
6970
): Promise<
7071
| {
71-
data: { id: string, path: string, fullPath: string }
72+
data: { id: string; path: string; fullPath: string }
7273
error: null
7374
}
7475
| {
@@ -138,7 +139,7 @@ export default class StorageFileApi {
138139
fileOptions?: FileOptions
139140
): Promise<
140141
| {
141-
data: { id: string, path: string, fullPath: string }
142+
data: { id: string; path: string; fullPath: string }
142143
error: null
143144
}
144145
| {
@@ -282,7 +283,7 @@ export default class StorageFileApi {
282283
fileOptions?: FileOptions
283284
): Promise<
284285
| {
285-
data: { id: string, path: string, fullPath: string }
286+
data: { id: string; path: string; fullPath: string }
286287
error: null
287288
}
288289
| {
@@ -298,10 +299,12 @@ export default class StorageFileApi {
298299
*
299300
* @param fromPath The original file path, including the current file name. For example `folder/image.png`.
300301
* @param toPath The new file path, including the new file name. For example `folder/image-new.png`.
302+
* @param options The destination options.
301303
*/
302304
async move(
303305
fromPath: string,
304-
toPath: string
306+
toPath: string,
307+
options?: DestinationOptions
305308
): Promise<
306309
| {
307310
data: { message: string }
@@ -316,7 +319,12 @@ export default class StorageFileApi {
316319
const data = await post(
317320
this.fetch,
318321
`${this.url}/object/move`,
319-
{ bucketId: this.bucketId, sourceKey: fromPath, destinationKey: toPath },
322+
{
323+
bucketId: this.bucketId,
324+
sourceKey: fromPath,
325+
destinationKey: toPath,
326+
destinationBucket: options?.destinationBucket,
327+
},
320328
{ headers: this.headers }
321329
)
322330
return { data, error: null }
@@ -334,10 +342,12 @@ export default class StorageFileApi {
334342
*
335343
* @param fromPath The original file path, including the current file name. For example `folder/image.png`.
336344
* @param toPath The new file path, including the new file name. For example `folder/image-copy.png`.
345+
* @param options The destination options.
337346
*/
338347
async copy(
339348
fromPath: string,
340-
toPath: string
349+
toPath: string,
350+
options?: DestinationOptions
341351
): Promise<
342352
| {
343353
data: { path: string }
@@ -352,7 +362,12 @@ export default class StorageFileApi {
352362
const data = await post(
353363
this.fetch,
354364
`${this.url}/object/copy`,
355-
{ bucketId: this.bucketId, sourceKey: fromPath, destinationKey: toPath },
365+
{
366+
bucketId: this.bucketId,
367+
sourceKey: fromPath,
368+
destinationKey: toPath,
369+
destinationBucket: options?.destinationBucket,
370+
},
356371
{ headers: this.headers }
357372
)
358373
return { data: { path: data.Key }, error: null }

test/__snapshots__/storageApi.test.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ exports[`bucket api Get bucket by id 1`] = `
44
{
55
"allowed_mime_types": null,
66
"created_at": "2021-02-17T04:43:32.770Z",
7-
"file_size_limit": 0,
7+
"file_size_limit": null,
88
"id": "bucket2",
99
"name": "bucket2",
1010
"owner": "4d56e902-f0a0-4662-8448-a4d9e643c142",

test/storageApi.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { StorageClient } from '../src/index'
33
// TODO: need to setup storage-api server for this test
44
const URL = 'http://localhost:8000/storage/v1'
55
const KEY =
6-
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2ODA5NjcxMTUsImV4cCI6MTcxMjUwMzI1MywiYXVkIjoiIiwic3ViIjoiMzE3ZWFkY2UtNjMxYS00NDI5LWEwYmItZjE5YTdhNTE3YjRhIiwicm9sZSI6ImF1dGhlbnRpY2F0ZWQifQ.NNzc54y9cZ2QLUHVSrCPOcGE2E0i8ouldc-AaWLsI08'
6+
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYXV0aGVudGljYXRlZCIsInN1YiI6IjMxN2VhZGNlLTYzMWEtNDQyOS1hMGJiLWYxOWE3YTUxN2I0YSIsImlhdCI6MTcxMzQzMzgwMCwiZXhwIjoyMDI5MDA5ODAwfQ.jVFIR-MB7rNfUuJaUH-_CyDFZEHezzXiqcRcdrGd29o'
77

88
const storage = new StorageClient(URL, { Authorization: `Bearer ${KEY}` })
99
const newBucketName = `my-new-bucket-${Date.now()}`

0 commit comments

Comments
 (0)