@headless-lms/adapter-storage-minio 0.2.4 → 0.3.0
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/README.md +11 -12
- package/dist/index.d.ts +0 -2
- package/dist/index.js +2 -5
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -20,15 +20,14 @@ const container = await createContainer(config, {
|
|
|
20
20
|
|
|
21
21
|
## Environment variables (reference installation)
|
|
22
22
|
|
|
23
|
-
| Variable
|
|
24
|
-
|
|
|
25
|
-
| `STORAGE_ENDPOINT`
|
|
26
|
-
| `STORAGE_PORT`
|
|
27
|
-
| `STORAGE_USE_SSL`
|
|
28
|
-
| `STORAGE_ACCESS_KEY`
|
|
29
|
-
| `STORAGE_SECRET_KEY`
|
|
30
|
-
| `STORAGE_REGION`
|
|
31
|
-
| `STORAGE_BUCKET`
|
|
32
|
-
| `STORAGE_UPLOAD_EXPIRY`
|
|
33
|
-
| `
|
|
34
|
-
| `STORAGE_PUBLIC_URL` | no | `publicUrl` | Origin serving `public/*`, such as a CDN. Default `http(s)://<endpoint>:<port>/<bucket>`. |
|
|
23
|
+
| Variable | Required | Maps to | Notes |
|
|
24
|
+
| ----------------------- | -------- | --------------------- | ----------------------------------------------------------------------------------------- |
|
|
25
|
+
| `STORAGE_ENDPOINT` | yes | `endPoint` | Host only, no scheme. |
|
|
26
|
+
| `STORAGE_PORT` | yes | `port` | |
|
|
27
|
+
| `STORAGE_USE_SSL` | yes | `useSSL` | `"true"` / `"false"`. |
|
|
28
|
+
| `STORAGE_ACCESS_KEY` | yes | `accessKey` | |
|
|
29
|
+
| `STORAGE_SECRET_KEY` | yes | `secretKey` | |
|
|
30
|
+
| `STORAGE_REGION` | yes | `region` | |
|
|
31
|
+
| `STORAGE_BUCKET` | yes | `bucket` | Created private if missing. |
|
|
32
|
+
| `STORAGE_UPLOAD_EXPIRY` | no | `uploadExpirySeconds` | Default 300. Stray member uploads older than this are swept. |
|
|
33
|
+
| `STORAGE_PUBLIC_URL` | no | `publicUrl` | Origin serving `public/*`, such as a CDN. Default `http(s)://<endpoint>:<port>/<bucket>`. |
|
package/dist/index.d.ts
CHANGED
|
@@ -10,7 +10,6 @@ interface MinioStorageConfig {
|
|
|
10
10
|
bucket: string;
|
|
11
11
|
publicUrl?: string;
|
|
12
12
|
uploadExpirySeconds: number;
|
|
13
|
-
downloadExpirySeconds: number;
|
|
14
13
|
}
|
|
15
14
|
declare class MinioStorageAdapter implements ObjectStorage {
|
|
16
15
|
private readonly logger?;
|
|
@@ -19,7 +18,6 @@ declare class MinioStorageAdapter implements ObjectStorage {
|
|
|
19
18
|
private readonly region;
|
|
20
19
|
private readonly publicBase;
|
|
21
20
|
readonly uploadExpirySeconds: number;
|
|
22
|
-
private readonly downloadExpiry;
|
|
23
21
|
private ensured;
|
|
24
22
|
constructor(config: MinioStorageConfig, logger?: Logger | undefined);
|
|
25
23
|
/** Create the (private) bucket if it does not exist. Call once at startup. */
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,6 @@ var MinioStorageAdapter = class {
|
|
|
8
8
|
region;
|
|
9
9
|
publicBase;
|
|
10
10
|
uploadExpirySeconds;
|
|
11
|
-
downloadExpiry;
|
|
12
11
|
ensured = false;
|
|
13
12
|
constructor(config, logger) {
|
|
14
13
|
this.logger = logger;
|
|
@@ -24,7 +23,6 @@ var MinioStorageAdapter = class {
|
|
|
24
23
|
this.region = config.region;
|
|
25
24
|
this.publicBase = (config.publicUrl ?? `${config.useSSL ? "https" : "http"}://${config.endPoint}:${config.port}/${config.bucket}`).replace(/\/+$/, "");
|
|
26
25
|
this.uploadExpirySeconds = config.uploadExpirySeconds;
|
|
27
|
-
this.downloadExpiry = config.downloadExpirySeconds;
|
|
28
26
|
}
|
|
29
27
|
/** Create the (private) bucket if it does not exist. Call once at startup. */
|
|
30
28
|
async ensureBucket() {
|
|
@@ -46,10 +44,9 @@ var MinioStorageAdapter = class {
|
|
|
46
44
|
}
|
|
47
45
|
async presignDownload(input) {
|
|
48
46
|
await this.ensureBucket();
|
|
49
|
-
const
|
|
50
|
-
const respHeaders = {};
|
|
47
|
+
const respHeaders = { "response-cache-control": PUBLIC_CACHE_CONTROL };
|
|
51
48
|
if (input.downloadFilename) respHeaders["response-content-disposition"] = `attachment; filename="${input.downloadFilename.replace(/"/g, "")}"`;
|
|
52
|
-
return this.client.presignedGetObject(this.bucket, input.key,
|
|
49
|
+
return this.client.presignedGetObject(this.bucket, input.key, Math.round((input.expiresAt.getTime() - input.signedAt.getTime()) / 1e3), respHeaders, input.signedAt);
|
|
53
50
|
}
|
|
54
51
|
async stat(key) {
|
|
55
52
|
try {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["// MinIO (S3-compatible) implementation of the ObjectStorage port. Private\n// buckets, except the public/ prefix, which anyone may read.\nimport { Client, CopyDestinationOptions, CopySourceOptions } from \"minio\";\nimport {\n PUBLIC_CACHE_CONTROL,\n PUBLIC_PREFIX,\n type Logger,\n type ObjectStorage,\n type PresignDownloadInput,\n type PresignedUpload,\n type StoredObjectInfo,\n} from \"@headless-lms/core/types\";\n\nexport interface MinioStorageConfig {\n endPoint: string;\n port: number;\n useSSL: boolean;\n accessKey: string;\n secretKey: string;\n region: string;\n bucket: string;\n publicUrl?: string;\n uploadExpirySeconds: number;\n
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["// MinIO (S3-compatible) implementation of the ObjectStorage port. Private\n// buckets, except the public/ prefix, which anyone may read.\nimport { Client, CopyDestinationOptions, CopySourceOptions } from \"minio\";\nimport {\n PUBLIC_CACHE_CONTROL,\n PUBLIC_PREFIX,\n type Logger,\n type ObjectStorage,\n type PresignDownloadInput,\n type PresignedUpload,\n type StoredObjectInfo,\n} from \"@headless-lms/core/types\";\n\nexport interface MinioStorageConfig {\n endPoint: string;\n port: number;\n useSSL: boolean;\n accessKey: string;\n secretKey: string;\n region: string;\n bucket: string;\n publicUrl?: string;\n uploadExpirySeconds: number;\n}\n\nexport class MinioStorageAdapter implements ObjectStorage {\n private readonly client: Client;\n private readonly bucket: string;\n private readonly region: string;\n private readonly publicBase: string;\n readonly uploadExpirySeconds: number;\n private ensured = false;\n\n constructor(\n config: MinioStorageConfig,\n private readonly logger?: Logger,\n ) {\n this.client = new Client({\n endPoint: config.endPoint,\n port: config.port,\n useSSL: config.useSSL,\n accessKey: config.accessKey,\n secretKey: config.secretKey,\n region: config.region,\n });\n this.bucket = config.bucket;\n this.region = config.region;\n this.publicBase = (\n config.publicUrl ??\n `${config.useSSL ? \"https\" : \"http\"}://${config.endPoint}:${config.port}/${config.bucket}`\n ).replace(/\\/+$/, \"\");\n this.uploadExpirySeconds = config.uploadExpirySeconds;\n }\n\n /** Create the (private) bucket if it does not exist. Call once at startup. */\n async ensureBucket(): Promise<void> {\n if (this.ensured) {\n return;\n }\n const exists = await this.client.bucketExists(this.bucket).catch(() => false);\n if (!exists) {\n await this.client.makeBucket(this.bucket, this.region);\n }\n await this.client.setBucketPolicy(this.bucket, publicReadPolicy(this.bucket));\n this.ensured = true;\n }\n\n async presignUpload(input: {\n key: string;\n contentType?: string;\n expiresInSeconds?: number;\n }): Promise<PresignedUpload> {\n await this.ensureBucket();\n const expiresInSeconds = input.expiresInSeconds ?? this.uploadExpirySeconds;\n const url = await this.client.presignedPutObject(this.bucket, input.key, expiresInSeconds);\n return {\n url,\n method: \"PUT\",\n key: input.key,\n expiresInSeconds,\n headers: input.contentType ? { \"Content-Type\": input.contentType } : {},\n };\n }\n\n async presignDownload(input: PresignDownloadInput): Promise<string> {\n await this.ensureBucket();\n const respHeaders: Record<string, string> = {\n \"response-cache-control\": PUBLIC_CACHE_CONTROL,\n };\n if (input.downloadFilename) {\n respHeaders[\"response-content-disposition\"] =\n `attachment; filename=\"${input.downloadFilename.replace(/\"/g, \"\")}\"`;\n }\n return this.client.presignedGetObject(\n this.bucket,\n input.key,\n Math.round((input.expiresAt.getTime() - input.signedAt.getTime()) / 1000),\n respHeaders,\n input.signedAt,\n );\n }\n\n async stat(key: string): Promise<StoredObjectInfo | null> {\n try {\n const s = await this.client.statObject(this.bucket, key);\n const contentType = s.metaData?.[\"content-type\"];\n return {\n key,\n size: s.size,\n ...(contentType ? { contentType } : {}),\n ...(s.lastModified ? { lastModified: s.lastModified.toISOString() } : {}),\n };\n } catch {\n return null;\n }\n }\n\n async list(prefix: string): Promise<StoredObjectInfo[]> {\n await this.ensureBucket();\n const objects: StoredObjectInfo[] = [];\n for await (const item of this.client.listObjectsV2(this.bucket, prefix, true)) {\n if (!item.name) {\n continue;\n }\n objects.push({\n key: item.name,\n size: item.size,\n ...(item.lastModified ? { lastModified: item.lastModified.toISOString() } : {}),\n });\n }\n return objects;\n }\n\n async copy(input: { from: string; to: string }): Promise<void> {\n await this.ensureBucket();\n await this.client.copyObject(\n new CopySourceOptions({ Bucket: this.bucket, Object: input.from }),\n new CopyDestinationOptions({ Bucket: this.bucket, Object: input.to }),\n );\n }\n\n async publish(input: { from: string; to: string }): Promise<void> {\n await this.ensureBucket();\n const source = await this.client.statObject(this.bucket, input.from);\n const contentType = source.metaData?.[\"content-type\"];\n await this.client.copyObject(\n new CopySourceOptions({ Bucket: this.bucket, Object: input.from }),\n new CopyDestinationOptions({\n Bucket: this.bucket,\n Object: input.to,\n MetadataDirective: \"REPLACE\",\n Headers: {\n \"Cache-Control\": PUBLIC_CACHE_CONTROL,\n ...(contentType ? { \"Content-Type\": contentType } : {}),\n },\n }),\n );\n }\n\n publicUrl(key: string): string {\n return `${this.publicBase}/${key.split(\"/\").map(encodeURIComponent).join(\"/\")}`;\n }\n\n async remove(key: string): Promise<void> {\n await this.client.removeObject(this.bucket, key);\n }\n}\n\nfunction publicReadPolicy(bucket: string): string {\n return JSON.stringify({\n Version: \"2012-10-17\",\n Statement: [\n {\n Effect: \"Allow\",\n Principal: { AWS: [\"*\"] },\n Action: [\"s3:GetObject\"],\n Resource: [`arn:aws:s3:::${bucket}/${PUBLIC_PREFIX}*`],\n },\n ],\n });\n}\n"],"mappings":";;;AAyBA,IAAa,sBAAb,MAA0D;CAUrC;CATnB;CACA;CACA;CACA;CACA;CACA,UAAkB;CAElB,YACE,QACA,QACA;EADiB,KAAA,SAAA;EAEjB,KAAK,SAAS,IAAI,OAAO;GACvB,UAAU,OAAO;GACjB,MAAM,OAAO;GACb,QAAQ,OAAO;GACf,WAAW,OAAO;GAClB,WAAW,OAAO;GAClB,QAAQ,OAAO;EACjB,CAAC;EACD,KAAK,SAAS,OAAO;EACrB,KAAK,SAAS,OAAO;EACrB,KAAK,cACH,OAAO,aACP,GAAG,OAAO,SAAS,UAAU,OAAO,KAAK,OAAO,SAAS,GAAG,OAAO,KAAK,GAAG,OAAO,SAAA,CAClF,QAAQ,QAAQ,EAAE;EACpB,KAAK,sBAAsB,OAAO;CACpC;;CAGA,MAAM,eAA8B;EAClC,IAAI,KAAK,SACP;EAGF,IAAI,CAAC,MADgB,KAAK,OAAO,aAAa,KAAK,MAAM,CAAC,CAAC,YAAY,KAAK,GAE1E,MAAM,KAAK,OAAO,WAAW,KAAK,QAAQ,KAAK,MAAM;EAEvD,MAAM,KAAK,OAAO,gBAAgB,KAAK,QAAQ,iBAAiB,KAAK,MAAM,CAAC;EAC5E,KAAK,UAAU;CACjB;CAEA,MAAM,cAAc,OAIS;EAC3B,MAAM,KAAK,aAAa;EACxB,MAAM,mBAAmB,MAAM,oBAAoB,KAAK;EAExD,OAAO;GACL,KAAA,MAFgB,KAAK,OAAO,mBAAmB,KAAK,QAAQ,MAAM,KAAK,gBAAgB;GAGvF,QAAQ;GACR,KAAK,MAAM;GACX;GACA,SAAS,MAAM,cAAc,EAAE,gBAAgB,MAAM,YAAY,IAAI,CAAC;EACxE;CACF;CAEA,MAAM,gBAAgB,OAA8C;EAClE,MAAM,KAAK,aAAa;EACxB,MAAM,cAAsC,EAC1C,0BAA0B,qBAC5B;EACA,IAAI,MAAM,kBACR,YAAY,kCACV,yBAAyB,MAAM,iBAAiB,QAAQ,MAAM,EAAE,EAAE;EAEtE,OAAO,KAAK,OAAO,mBACjB,KAAK,QACL,MAAM,KACN,KAAK,OAAO,MAAM,UAAU,QAAQ,IAAI,MAAM,SAAS,QAAQ,KAAK,GAAI,GACxE,aACA,MAAM,QACR;CACF;CAEA,MAAM,KAAK,KAA+C;EACxD,IAAI;GACF,MAAM,IAAI,MAAM,KAAK,OAAO,WAAW,KAAK,QAAQ,GAAG;GACvD,MAAM,cAAc,EAAE,WAAW;GACjC,OAAO;IACL;IACA,MAAM,EAAE;IACR,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;IACrC,GAAI,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,YAAY,EAAE,IAAI,CAAC;GACzE;EACF,QAAQ;GACN,OAAO;EACT;CACF;CAEA,MAAM,KAAK,QAA6C;EACtD,MAAM,KAAK,aAAa;EACxB,MAAM,UAA8B,CAAC;EACrC,WAAW,MAAM,QAAQ,KAAK,OAAO,cAAc,KAAK,QAAQ,QAAQ,IAAI,GAAG;GAC7E,IAAI,CAAC,KAAK,MACR;GAEF,QAAQ,KAAK;IACX,KAAK,KAAK;IACV,MAAM,KAAK;IACX,GAAI,KAAK,eAAe,EAAE,cAAc,KAAK,aAAa,YAAY,EAAE,IAAI,CAAC;GAC/E,CAAC;EACH;EACA,OAAO;CACT;CAEA,MAAM,KAAK,OAAoD;EAC7D,MAAM,KAAK,aAAa;EACxB,MAAM,KAAK,OAAO,WAChB,IAAI,kBAAkB;GAAE,QAAQ,KAAK;GAAQ,QAAQ,MAAM;EAAK,CAAC,GACjE,IAAI,uBAAuB;GAAE,QAAQ,KAAK;GAAQ,QAAQ,MAAM;EAAG,CAAC,CACtE;CACF;CAEA,MAAM,QAAQ,OAAoD;EAChE,MAAM,KAAK,aAAa;EAExB,MAAM,eAAc,MADC,KAAK,OAAO,WAAW,KAAK,QAAQ,MAAM,IAAI,EAAA,CACxC,WAAW;EACtC,MAAM,KAAK,OAAO,WAChB,IAAI,kBAAkB;GAAE,QAAQ,KAAK;GAAQ,QAAQ,MAAM;EAAK,CAAC,GACjE,IAAI,uBAAuB;GACzB,QAAQ,KAAK;GACb,QAAQ,MAAM;GACd,mBAAmB;GACnB,SAAS;IACP,iBAAiB;IACjB,GAAI,cAAc,EAAE,gBAAgB,YAAY,IAAI,CAAC;GACvD;EACF,CAAC,CACH;CACF;CAEA,UAAU,KAAqB;EAC7B,OAAO,GAAG,KAAK,WAAW,GAAG,IAAI,MAAM,GAAG,CAAC,CAAC,IAAI,kBAAkB,CAAC,CAAC,KAAK,GAAG;CAC9E;CAEA,MAAM,OAAO,KAA4B;EACvC,MAAM,KAAK,OAAO,aAAa,KAAK,QAAQ,GAAG;CACjD;AACF;AAEA,SAAS,iBAAiB,QAAwB;CAChD,OAAO,KAAK,UAAU;EACpB,SAAS;EACT,WAAW,CACT;GACE,QAAQ;GACR,WAAW,EAAE,KAAK,CAAC,GAAG,EAAE;GACxB,QAAQ,CAAC,cAAc;GACvB,UAAU,CAAC,gBAAgB,OAAO,GAAG,cAAc,EAAE;EACvD,CACF;CACF,CAAC;AACH"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@headless-lms/adapter-storage-minio",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"minio": "^8.0.7",
|
|
15
|
-
"@headless-lms/core": "0.
|
|
15
|
+
"@headless-lms/core": "0.8.0"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"@types/node": "^22.10.2",
|