@directus/storage-driver-supabase 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +8 -6
- package/dist/index.js +115 -111
- package/package.json +8 -7
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
1
|
+
import * as undici from 'undici';
|
|
2
|
+
import { Driver, Range } from '@directus/storage';
|
|
3
3
|
import { Readable } from 'node:stream';
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
type DriverSupabaseConfig = {
|
|
5
6
|
bucket: string;
|
|
6
7
|
serviceRole: string;
|
|
7
8
|
projectId?: string;
|
|
8
9
|
endpoint?: string;
|
|
9
10
|
root?: string;
|
|
10
11
|
};
|
|
11
|
-
|
|
12
|
+
declare class DriverSupabase implements Driver {
|
|
12
13
|
private config;
|
|
13
14
|
private client;
|
|
14
15
|
private bucket;
|
|
@@ -19,7 +20,7 @@ export declare class DriverSupabase implements Driver {
|
|
|
19
20
|
private getFullPath;
|
|
20
21
|
private getAuthenticatedUrl;
|
|
21
22
|
read(filepath: string, range?: Range): Promise<Readable>;
|
|
22
|
-
head(filepath: string): Promise<
|
|
23
|
+
head(filepath: string): Promise<undici.Headers>;
|
|
23
24
|
stat(filepath: string): Promise<{
|
|
24
25
|
size: number;
|
|
25
26
|
modified: Date;
|
|
@@ -31,4 +32,5 @@ export declare class DriverSupabase implements Driver {
|
|
|
31
32
|
delete(filepath: string): Promise<void>;
|
|
32
33
|
list(prefix?: string): AsyncGenerator<string, void, unknown>;
|
|
33
34
|
}
|
|
34
|
-
|
|
35
|
+
|
|
36
|
+
export { DriverSupabase, DriverSupabaseConfig, DriverSupabase as default };
|
package/dist/index.js
CHANGED
|
@@ -1,118 +1,122 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { normalizePath } from "@directus/utils";
|
|
3
|
+
import { StorageClient } from "@supabase/storage-js";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import { Readable } from "stream";
|
|
6
|
+
import { fetch } from "undici";
|
|
7
|
+
var DriverSupabase = class {
|
|
8
|
+
config;
|
|
9
|
+
client;
|
|
10
|
+
bucket;
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.config = config;
|
|
13
|
+
this.client = this.getClient();
|
|
14
|
+
this.bucket = this.getBucket();
|
|
15
|
+
}
|
|
16
|
+
get endpoint() {
|
|
17
|
+
return this.config.endpoint ?? `https://${this.config.projectId}.supabase.co/storage/v1`;
|
|
18
|
+
}
|
|
19
|
+
getClient() {
|
|
20
|
+
if (!this.config.projectId && !this.config.endpoint) {
|
|
21
|
+
throw new Error("`project_id` or `endpoint` is required");
|
|
14
22
|
}
|
|
15
|
-
|
|
16
|
-
|
|
23
|
+
if (!this.config.serviceRole) {
|
|
24
|
+
throw new Error("`service_role` is required");
|
|
17
25
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
apikey: this.config.serviceRole,
|
|
27
|
-
Authorization: `Bearer ${this.config.serviceRole}`,
|
|
28
|
-
});
|
|
26
|
+
return new StorageClient(this.endpoint, {
|
|
27
|
+
apikey: this.config.serviceRole,
|
|
28
|
+
Authorization: `Bearer ${this.config.serviceRole}`
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
getBucket() {
|
|
32
|
+
if (!this.config.bucket) {
|
|
33
|
+
throw new Error("`bucket` is required");
|
|
29
34
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
return this.client.from(this.config.bucket);
|
|
36
|
+
}
|
|
37
|
+
getFullPath(filepath) {
|
|
38
|
+
return this.config.root ? normalizePath(join(this.config.root, filepath)) : filepath;
|
|
39
|
+
}
|
|
40
|
+
getAuthenticatedUrl(filepath) {
|
|
41
|
+
return `${this.endpoint}/${join("object/authenticated", this.config.bucket, this.getFullPath(filepath))}`;
|
|
42
|
+
}
|
|
43
|
+
async read(filepath, range) {
|
|
44
|
+
const requestInit = { method: "GET" };
|
|
45
|
+
requestInit.headers = {
|
|
46
|
+
Authorization: `Bearer ${this.config.serviceRole}`
|
|
47
|
+
};
|
|
48
|
+
if (range) {
|
|
49
|
+
requestInit.headers["Range"] = `bytes=${range.start ?? ""}-${range.end ?? ""}`;
|
|
35
50
|
}
|
|
36
|
-
|
|
37
|
-
|
|
51
|
+
const response = await fetch(this.getAuthenticatedUrl(filepath), requestInit);
|
|
52
|
+
if (response.status >= 400 || !response.body) {
|
|
53
|
+
throw new Error(`No stream returned for file "${filepath}"`);
|
|
38
54
|
}
|
|
39
|
-
|
|
40
|
-
|
|
55
|
+
return Readable.fromWeb(response.body);
|
|
56
|
+
}
|
|
57
|
+
async head(filepath) {
|
|
58
|
+
const response = await fetch(this.getAuthenticatedUrl(filepath), {
|
|
59
|
+
method: "HEAD",
|
|
60
|
+
headers: {
|
|
61
|
+
Authorization: `Bearer ${this.config.serviceRole}`
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
if (response.status >= 400) {
|
|
65
|
+
throw new Error("File not found");
|
|
41
66
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
67
|
+
return response.headers;
|
|
68
|
+
}
|
|
69
|
+
async stat(filepath) {
|
|
70
|
+
const headers = await this.head(filepath);
|
|
71
|
+
return {
|
|
72
|
+
size: parseInt(headers.get("content-length") || ""),
|
|
73
|
+
modified: new Date(headers.get("last-modified") || "")
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
async exists(filepath) {
|
|
77
|
+
try {
|
|
78
|
+
await this.stat(filepath);
|
|
79
|
+
return true;
|
|
80
|
+
} catch {
|
|
81
|
+
return false;
|
|
55
82
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
async delete(filepath) {
|
|
99
|
-
await this.bucket.remove([this.getFullPath(filepath)]);
|
|
100
|
-
}
|
|
101
|
-
async *list(prefix = '') {
|
|
102
|
-
const limit = 1000;
|
|
103
|
-
let offset = 0;
|
|
104
|
-
let itemCount = 0;
|
|
105
|
-
do {
|
|
106
|
-
const { data, error } = await this.bucket.list(this.config.root, { limit, offset, search: prefix });
|
|
107
|
-
if (!data || error) {
|
|
108
|
-
break;
|
|
109
|
-
}
|
|
110
|
-
itemCount = data.length;
|
|
111
|
-
offset += itemCount;
|
|
112
|
-
for (const item of data) {
|
|
113
|
-
yield item.name;
|
|
114
|
-
}
|
|
115
|
-
} while (itemCount === limit);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
export default DriverSupabase;
|
|
83
|
+
}
|
|
84
|
+
async move(src, dest) {
|
|
85
|
+
await this.bucket.move(this.getFullPath(src), this.getFullPath(dest));
|
|
86
|
+
}
|
|
87
|
+
async copy(src, dest) {
|
|
88
|
+
await this.bucket.copy(this.getFullPath(src), this.getFullPath(dest));
|
|
89
|
+
}
|
|
90
|
+
async write(filepath, content, type) {
|
|
91
|
+
await this.bucket.upload(this.getFullPath(filepath), content, {
|
|
92
|
+
contentType: type ?? "",
|
|
93
|
+
cacheControl: "3600",
|
|
94
|
+
upsert: true,
|
|
95
|
+
duplex: "half"
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
async delete(filepath) {
|
|
99
|
+
await this.bucket.remove([this.getFullPath(filepath)]);
|
|
100
|
+
}
|
|
101
|
+
async *list(prefix = "") {
|
|
102
|
+
const limit = 1e3;
|
|
103
|
+
let offset = 0;
|
|
104
|
+
let itemCount = 0;
|
|
105
|
+
do {
|
|
106
|
+
const { data, error } = await this.bucket.list(this.config.root, { limit, offset, search: prefix });
|
|
107
|
+
if (!data || error) {
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
itemCount = data.length;
|
|
111
|
+
offset += itemCount;
|
|
112
|
+
for (const item of data) {
|
|
113
|
+
yield item.name;
|
|
114
|
+
}
|
|
115
|
+
} while (itemCount === limit);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
var src_default = DriverSupabase;
|
|
119
|
+
export {
|
|
120
|
+
DriverSupabase,
|
|
121
|
+
src_default as default
|
|
122
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@directus/storage-driver-supabase",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Supabase file storage abstraction for `@directus/storage`",
|
|
5
5
|
"homepage": "https://directus.io",
|
|
6
6
|
"repository": {
|
|
@@ -23,19 +23,20 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@supabase/storage-js": "2.5.1",
|
|
25
25
|
"undici": "5.22.1",
|
|
26
|
-
"@directus/storage": "10.0.
|
|
27
|
-
"@directus/utils": "
|
|
26
|
+
"@directus/storage": "10.0.6",
|
|
27
|
+
"@directus/utils": "11.0.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@ngneat/falso": "6.4.0",
|
|
31
31
|
"@vitest/coverage-c8": "0.31.1",
|
|
32
|
-
"
|
|
32
|
+
"tsup": "7.2.0",
|
|
33
|
+
"typescript": "5.2.2",
|
|
33
34
|
"vitest": "0.31.1",
|
|
34
|
-
"@directus/tsconfig": "1.0.
|
|
35
|
+
"@directus/tsconfig": "1.0.1"
|
|
35
36
|
},
|
|
36
37
|
"scripts": {
|
|
37
|
-
"build": "
|
|
38
|
-
"dev": "
|
|
38
|
+
"build": "tsup src/index.ts --format=esm --dts",
|
|
39
|
+
"dev": "tsup src/index.ts --format=esm --dts --watch",
|
|
39
40
|
"test": "vitest --watch=false"
|
|
40
41
|
}
|
|
41
42
|
}
|