picgo-plugin-s3 1.3.9 → 1.4.0-beta.2
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 +78 -38
- package/dist/config.d.ts +5 -1
- package/dist/config.js +136 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +57 -160
- package/dist/uploader.d.ts +0 -2
- package/dist/uploader.js +21 -40
- package/dist/utils.d.ts +54 -1
- package/dist/utils.js +178 -55
- package/package.json +24 -14
- package/.eslintrc +0 -24
package/README.md
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
## picgo-plugin-s3
|
|
2
3
|
|
|
3
4
|

|
|
@@ -8,6 +9,7 @@
|
|
|
8
9
|
|
|
9
10
|
- 支持 Amazon S3 与其他如 backblaze b2 等兼容 S3 API 的云存储
|
|
10
11
|
- 支持 PicGO GUI
|
|
12
|
+
- 支持 MinIO
|
|
11
13
|
|
|
12
14
|
### 安装 Installation
|
|
13
15
|
|
|
@@ -19,43 +21,81 @@ GUI 直接搜索 _S3_ 下载即可,Core 版执行 `picgo add s3` 安装。
|
|
|
19
21
|
picgo set uploader aws-s3
|
|
20
22
|
```
|
|
21
23
|
|
|
22
|
-
| Key | 说明
|
|
23
|
-
|
|
24
|
-
| `accessKeyID` | AWS 凭证 ID
|
|
25
|
-
| `secretAccessKey` | AWS 凭证密钥
|
|
26
|
-
| `bucketName` | S3 桶名称
|
|
27
|
-
| `uploadPath` |
|
|
28
|
-
| `
|
|
29
|
-
| `
|
|
30
|
-
| `
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
34
|
-
| `
|
|
35
|
-
| `
|
|
36
|
-
| `
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
|
44
|
-
|
|
|
45
|
-
| `{
|
|
46
|
-
| `{
|
|
47
|
-
| `{
|
|
48
|
-
| `{
|
|
49
|
-
| `{
|
|
50
|
-
| `{
|
|
51
|
-
| `{
|
|
52
|
-
| `{
|
|
53
|
-
| `{
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
24
|
+
| Key | 说明 | 例子 |
|
|
25
|
+
| -------------------------- | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
26
|
+
| `accessKeyID` | AWS 凭证 ID | |
|
|
27
|
+
| `secretAccessKey` | AWS 凭证密钥 | |
|
|
28
|
+
| `bucketName` | S3 桶名称 | `gallery` |
|
|
29
|
+
| `uploadPath` | 上传路径,详细配置查看以下说明 | `{year}/{month}/{fullName}` |
|
|
30
|
+
| `endpoint` | 指定自定义终端节点 | `s3.us-west-2.amazonaws.com` |
|
|
31
|
+
| `proxy` | 代理地址 | 支持 http 代理,例如 `http://127.0.0.1:1080` |
|
|
32
|
+
| `region` | 指定执行服务请求的区域 | `us-west-1` |
|
|
33
|
+
| `pathStyleAccess` | 是否启用 S3 Path style | 默认为 `false`,使用 minio 请设置为 `true` (e.g., https://s3.amazonaws.com/<bucketName>/<key> instead of https://<bucketName>.s3.amazonaws.com/<key>) |
|
|
34
|
+
| `rejectUnauthorized` | 是否拒绝无效 TLS 证书连接 | 默认为 `true`,如上传失败日志显示证书问题可设置为`false` |
|
|
35
|
+
| `acl` | 访问控制列表,上传资源的访问策略 | 默认为 `public-read`, AWS 可选 `private" | "public-read" | "public-read-write" | "authenticated-read" | "aws-exec-read" | "bucket-owner-read" | "bucket-owner-full-control` |
|
|
36
|
+
| `outputURLPattern` | 自定义输出 URL 模板,详细配置查看以下说明 | `{protocol}://{host}:{port}/{path}` |
|
|
37
|
+
| `urlPrefix` | 最终生成图片 URL 的自定义前缀(已废弃,请使用 outputURLPattern) | `https://img.example.com/my-blog/` |
|
|
38
|
+
| `urlSuffix` | 最终生成图片 URL 的自定义后缀(已废弃,请使用 outputURLPattern) | `?oxx=xxx` |
|
|
39
|
+
| `disableBucketPrefixToURL` | 开启 `pathStyleAccess` 时,是否要禁用最终生成 URL 中添加 bucket 前缀 (已废弃,请使用 outputURLPattern) | 默认为 `false` |
|
|
40
|
+
|
|
41
|
+
#### 通用占位符 Payload
|
|
42
|
+
|
|
43
|
+
_上传路径_ 和 _自定义输出 URL 模板_ 支持的通用占位符,插件将会自行用变量替换到实际使用的路径中。
|
|
44
|
+
|
|
45
|
+
| payload | 描述 |
|
|
46
|
+
| --------------- | ------------------- |
|
|
47
|
+
| `{year}` | 当前日期 - 年 |
|
|
48
|
+
| `{month}` | 当前日期 - 月 |
|
|
49
|
+
| `{day}` | 当前日期 - 日 |
|
|
50
|
+
| `{hour}` | 当前日期 - 时 |
|
|
51
|
+
| `{minute}` | 当前日期 - 分 |
|
|
52
|
+
| `{second}` | 当前日期 - 秒 |
|
|
53
|
+
| `{millisecond}` | 当前日期 - 毫秒 |
|
|
54
|
+
| `{timestamp}` | Unix 时间戳 |
|
|
55
|
+
| `{timestampMS}` | Unix 时间戳(毫秒) |
|
|
56
|
+
|
|
57
|
+
#### 上传路径(`uploadPath`)
|
|
58
|
+
|
|
59
|
+
支持占位符方式配置,如 `{year}/{month}/{md5}.{extName}`。除以下列表外,还指出上述通用占位符。
|
|
60
|
+
|
|
61
|
+
| payload | 描述 |
|
|
62
|
+
| ------------ | ---------------------- |
|
|
63
|
+
| `{fullName}` | 完整文件名(含扩展名) |
|
|
64
|
+
| `{fileName}` | 文件名(不含扩展名) |
|
|
65
|
+
| `{extName}` | 扩展名(不含`.`) |
|
|
66
|
+
| `{md5}` | 图片 MD5 计算值 |
|
|
67
|
+
| `{sha1}` | 图片 SHA1 计算值 |
|
|
68
|
+
| `{sha256}` | 图片 SHA256 计算值 |
|
|
69
|
+
|
|
70
|
+
#### 自定义输出 URL 模板(`outputURLPattern`)
|
|
71
|
+
|
|
72
|
+
支持占位符方式配置,如 `{protocol}://{host}:{port}/{path}`。除以下列表外,还指出上述通用占位符。
|
|
73
|
+
|
|
74
|
+
| payload | 描述 | 例子 |
|
|
75
|
+
| ------------ | ------------------------------------------------------------- | --------------------------------------------------------- |
|
|
76
|
+
| `{protocol}` | 原上传 URL 的协议 | `http` 或 `https` |
|
|
77
|
+
| `{host}` | 原上传 URL 的域名,可不不使用次此变量改为其他自己的反代的域名 | `example.com` |
|
|
78
|
+
| `{port}` | 原上传 URL 的端口 | `80` |
|
|
79
|
+
| `{dir}` | 原上传 URL 的目录 | `testBucket/2024/12` |
|
|
80
|
+
| `{path}` | 原上传 URL 的完整路径 | `testBucket/2024/12/4aa4f41e38817e5fd38ac870f40dbc70.jpg` |
|
|
81
|
+
| `{fileName}` | 文件名(含扩展名) | `test.jpg` |
|
|
82
|
+
| `{extName}` | 扩展名(不含`.`) | `jpg` |
|
|
83
|
+
| `{query}` | 上传 URL 的 querystirng 部分(不含 `?` ) | `height=100&width=200` |
|
|
84
|
+
| `{hash}` | 上传 URL 的 hash 部分(不含 `#` ) | `abc` |
|
|
85
|
+
| `{bucket}` | 上传桶名 | `testBucket` |
|
|
86
|
+
|
|
87
|
+
这个配置将会替代原有的 `urlPrefix` 、`urlSuffix`、`disableBucketPrefixToURL` 的配置。
|
|
88
|
+
|
|
89
|
+
另外每个变量都支持**正则替换**
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
语法:
|
|
93
|
+
{payload:/pattern/reFlag,'replacement'}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
比如配置为 `{protocol}://example.imgbed/{path:/testBucket/i,'myimage'}`,如果原URL为 `https://cluster-test-1.s3.us-east-001.example.com/testBucket/image.jpg` 则会生成 `https://example.imgbed/myimage/image.jpg`。
|
|
97
|
+
|
|
98
|
+
#### 示例 Example
|
|
59
99
|
|
|
60
100
|
```json
|
|
61
101
|
"aws-s3": {
|
|
@@ -64,7 +104,7 @@ picgo set uploader aws-s3
|
|
|
64
104
|
"bucketName": "my-bucket",
|
|
65
105
|
"uploadPath": "{year}/{md5}.{extName}",
|
|
66
106
|
"endpoint": "s3.us-west-000.backblazeb2.com",
|
|
67
|
-
"
|
|
107
|
+
"outputURLPattern": "{protocol}://{host}/{path}"
|
|
68
108
|
}
|
|
69
109
|
```
|
|
70
110
|
|
package/dist/config.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { IPicGo, IPluginConfig } from "picgo";
|
|
1
2
|
export interface IS3UserConfig {
|
|
2
3
|
accessKeyID: string;
|
|
3
4
|
secretAccessKey: string;
|
|
@@ -6,10 +7,13 @@ export interface IS3UserConfig {
|
|
|
6
7
|
region?: string;
|
|
7
8
|
endpoint?: string;
|
|
8
9
|
proxy?: string;
|
|
9
|
-
urlPrefix?: string;
|
|
10
10
|
pathStyleAccess?: boolean;
|
|
11
11
|
rejectUnauthorized?: boolean;
|
|
12
12
|
acl?: string;
|
|
13
13
|
disableBucketPrefixToURL?: boolean;
|
|
14
|
+
urlPrefix?: string;
|
|
14
15
|
urlSuffix?: string;
|
|
16
|
+
outputURLPattern?: string;
|
|
15
17
|
}
|
|
18
|
+
export declare const getPluginConfig: (ctx: IPicGo) => IPluginConfig[];
|
|
19
|
+
export declare function loadUserConfig(ctx: IPicGo): IS3UserConfig;
|
package/dist/config.js
CHANGED
|
@@ -1,2 +1,138 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getPluginConfig = void 0;
|
|
4
|
+
exports.loadUserConfig = loadUserConfig;
|
|
5
|
+
function mergePluginConfig(userConfig) {
|
|
6
|
+
return [
|
|
7
|
+
{
|
|
8
|
+
name: "accessKeyID",
|
|
9
|
+
type: "input",
|
|
10
|
+
default: userConfig.accessKeyID,
|
|
11
|
+
required: true,
|
|
12
|
+
message: "access key id",
|
|
13
|
+
alias: "应用密钥 ID",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: "secretAccessKey",
|
|
17
|
+
type: "password",
|
|
18
|
+
default: userConfig.secretAccessKey,
|
|
19
|
+
required: true,
|
|
20
|
+
message: "secret access key",
|
|
21
|
+
alias: "应用密钥",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "bucketName",
|
|
25
|
+
type: "input",
|
|
26
|
+
default: userConfig.bucketName,
|
|
27
|
+
required: true,
|
|
28
|
+
alias: "桶名",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: "uploadPath",
|
|
32
|
+
type: "input",
|
|
33
|
+
default: userConfig.uploadPath,
|
|
34
|
+
required: true,
|
|
35
|
+
alias: "上传文件路径",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: "region",
|
|
39
|
+
type: "input",
|
|
40
|
+
default: userConfig.region,
|
|
41
|
+
required: false,
|
|
42
|
+
alias: "地区",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "endpoint",
|
|
46
|
+
type: "input",
|
|
47
|
+
default: userConfig.endpoint,
|
|
48
|
+
required: false,
|
|
49
|
+
alias: "自定义节点",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: "proxy",
|
|
53
|
+
type: "input",
|
|
54
|
+
default: userConfig.proxy,
|
|
55
|
+
required: false,
|
|
56
|
+
alias: "代理",
|
|
57
|
+
message: "http://127.0.0.1:1080",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "rejectUnauthorized",
|
|
61
|
+
type: "confirm",
|
|
62
|
+
default: userConfig.rejectUnauthorized || true,
|
|
63
|
+
message: "是否拒绝无效TLS证书连接",
|
|
64
|
+
required: false,
|
|
65
|
+
alias: "拒绝无效TLS证书连接",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "acl",
|
|
69
|
+
type: "input",
|
|
70
|
+
default: userConfig.acl || "public-read",
|
|
71
|
+
message: "上传资源的访问策略",
|
|
72
|
+
required: false,
|
|
73
|
+
alias: "ACL 访问控制列表",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "pathStyleAccess",
|
|
77
|
+
type: "confirm",
|
|
78
|
+
default: userConfig.pathStyleAccess || false,
|
|
79
|
+
message: "enable s3ForcePathStyle or not",
|
|
80
|
+
required: false,
|
|
81
|
+
alias: "ForcePathStyle",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: "outputURLPattern",
|
|
85
|
+
type: "input",
|
|
86
|
+
default: userConfig.outputURLPattern || "",
|
|
87
|
+
message: "自定义输出 URL 模板",
|
|
88
|
+
required: false,
|
|
89
|
+
alias: "自定义输出 URL 模板",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: "urlPrefix",
|
|
93
|
+
type: "input",
|
|
94
|
+
default: userConfig.urlPrefix,
|
|
95
|
+
message: "https://img.example.com/bucket-name/(已废弃,请使用 outputURLPattern)",
|
|
96
|
+
required: false,
|
|
97
|
+
alias: "设置输出图片URL前缀",
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: "urlSuffix",
|
|
101
|
+
type: "input",
|
|
102
|
+
default: userConfig.urlSuffix || "",
|
|
103
|
+
message: "例如 ?x-oss-process=xxx(已废弃,请使用 outputURLPattern)",
|
|
104
|
+
required: false,
|
|
105
|
+
alias: "设定输出图片URL后缀",
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: "disableBucketPrefixToURL",
|
|
109
|
+
type: "confirm",
|
|
110
|
+
default: userConfig.disableBucketPrefixToURL || false,
|
|
111
|
+
message: "开启 `pathStyleAccess` 时,是否要禁用最终生成URL中添加 bucket 前缀(已废弃,请使用 outputURLPattern)",
|
|
112
|
+
required: false,
|
|
113
|
+
alias: "Bucket 前缀",
|
|
114
|
+
},
|
|
115
|
+
];
|
|
116
|
+
}
|
|
117
|
+
const getPluginConfig = (ctx) => {
|
|
118
|
+
const defaultConfig = {
|
|
119
|
+
accessKeyID: "",
|
|
120
|
+
secretAccessKey: "",
|
|
121
|
+
bucketName: "",
|
|
122
|
+
uploadPath: "{year}/{month}/{md5}.{extName}",
|
|
123
|
+
pathStyleAccess: false,
|
|
124
|
+
rejectUnauthorized: true,
|
|
125
|
+
acl: "public-read",
|
|
126
|
+
};
|
|
127
|
+
let userConfig = ctx.getConfig("picBed.aws-s3");
|
|
128
|
+
userConfig = Object.assign(Object.assign({}, defaultConfig), (userConfig || {}));
|
|
129
|
+
return mergePluginConfig(userConfig);
|
|
130
|
+
};
|
|
131
|
+
exports.getPluginConfig = getPluginConfig;
|
|
132
|
+
function loadUserConfig(ctx) {
|
|
133
|
+
const userConfig = ctx.getConfig("picBed.aws-s3");
|
|
134
|
+
if (!userConfig) {
|
|
135
|
+
throw new Error("Can't find amazon s3 uploader config");
|
|
136
|
+
}
|
|
137
|
+
return userConfig;
|
|
138
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -4,175 +4,72 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
const uploader_1 = __importDefault(require("./uploader"));
|
|
6
6
|
const utils_1 = require("./utils");
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
acl: "public-read",
|
|
17
|
-
urlSuffix: "",
|
|
18
|
-
};
|
|
19
|
-
let userConfig = ctx.getConfig("picBed.aws-s3");
|
|
20
|
-
userConfig = Object.assign(Object.assign({}, defaultConfig), (userConfig || {}));
|
|
21
|
-
return [
|
|
22
|
-
{
|
|
23
|
-
name: "accessKeyID",
|
|
24
|
-
type: "input",
|
|
25
|
-
default: userConfig.accessKeyID,
|
|
26
|
-
required: true,
|
|
27
|
-
message: "access key id",
|
|
28
|
-
alias: "应用密钥 ID",
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
name: "secretAccessKey",
|
|
32
|
-
type: "password",
|
|
33
|
-
default: userConfig.secretAccessKey,
|
|
34
|
-
required: true,
|
|
35
|
-
message: "secret access key",
|
|
36
|
-
alias: "应用密钥",
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
name: "bucketName",
|
|
40
|
-
type: "input",
|
|
41
|
-
default: userConfig.bucketName,
|
|
42
|
-
required: true,
|
|
43
|
-
alias: "桶名",
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
name: "uploadPath",
|
|
47
|
-
type: "input",
|
|
48
|
-
default: userConfig.uploadPath,
|
|
49
|
-
required: true,
|
|
50
|
-
alias: "文件路径",
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
name: "region",
|
|
54
|
-
type: "input",
|
|
55
|
-
default: userConfig.region,
|
|
56
|
-
required: false,
|
|
57
|
-
alias: "地区",
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
name: "endpoint",
|
|
61
|
-
type: "input",
|
|
62
|
-
default: userConfig.endpoint,
|
|
63
|
-
required: false,
|
|
64
|
-
alias: "自定义节点",
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
name: "proxy",
|
|
68
|
-
type: "input",
|
|
69
|
-
default: userConfig.proxy,
|
|
70
|
-
required: false,
|
|
71
|
-
alias: "代理",
|
|
72
|
-
message: "http://127.0.0.1:1080",
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
name: "urlPrefix",
|
|
76
|
-
type: "input",
|
|
77
|
-
default: userConfig.urlPrefix,
|
|
78
|
-
message: "https://img.example.com/bucket-name/",
|
|
79
|
-
required: false,
|
|
80
|
-
alias: "自定义域名",
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
name: "urlSuffix",
|
|
84
|
-
type: "input",
|
|
85
|
-
default: userConfig.urlSuffix || "",
|
|
86
|
-
message: "例如?x-oss-process=xxx",
|
|
87
|
-
required: false,
|
|
88
|
-
alias: "设定网址后缀",
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
name: "pathStyleAccess",
|
|
92
|
-
type: "confirm",
|
|
93
|
-
default: userConfig.pathStyleAccess || false,
|
|
94
|
-
message: "enable s3ForcePathStyle or not",
|
|
95
|
-
required: false,
|
|
96
|
-
alias: "ForcePathStyle",
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
name: "rejectUnauthorized",
|
|
100
|
-
type: "confirm",
|
|
101
|
-
default: userConfig.rejectUnauthorized || true,
|
|
102
|
-
message: "是否拒绝无效TLS证书连接",
|
|
103
|
-
required: false,
|
|
104
|
-
alias: "拒绝无效TLS证书连接",
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
name: "acl",
|
|
108
|
-
type: "input",
|
|
109
|
-
default: userConfig.acl || "public-read",
|
|
110
|
-
message: "上传资源的访问策略",
|
|
111
|
-
required: false,
|
|
112
|
-
alias: "ACL 访问控制列表",
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
name: "disableBucketPrefixToURL",
|
|
116
|
-
type: "confirm",
|
|
117
|
-
default: userConfig.disableBucketPrefixToURL || false,
|
|
118
|
-
message: "开启 `pathStyleAccess` 时,是否要禁用最终生成URL中添加 bucket 前缀",
|
|
119
|
-
required: false,
|
|
120
|
-
alias: "Bucket 前缀",
|
|
121
|
-
},
|
|
122
|
-
];
|
|
123
|
-
};
|
|
124
|
-
const handle = async (ctx) => {
|
|
125
|
-
const userConfig = ctx.getConfig("picBed.aws-s3");
|
|
126
|
-
if (!userConfig) {
|
|
127
|
-
throw new Error("Can't find amazon s3 uploader config");
|
|
128
|
-
}
|
|
129
|
-
let urlPrefix = userConfig.urlPrefix;
|
|
130
|
-
if (urlPrefix) {
|
|
131
|
-
urlPrefix = urlPrefix.replace(/\/?$/, "");
|
|
132
|
-
if (userConfig.pathStyleAccess && !userConfig.disableBucketPrefixToURL) {
|
|
133
|
-
urlPrefix += "/" + userConfig.bucketName;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
const client = uploader_1.default.createS3Client(userConfig);
|
|
137
|
-
const output = ctx.output;
|
|
138
|
-
const tasks = output.map((item, idx) => uploader_1.default.createUploadTask({
|
|
7
|
+
const config_1 = require("./config");
|
|
8
|
+
const pluginName = "aws-s3";
|
|
9
|
+
const upload = async (ctx) => {
|
|
10
|
+
const userConfig = (0, config_1.loadUserConfig)(ctx);
|
|
11
|
+
const client = uploader_1.default.createS3Client(userConfig);
|
|
12
|
+
const output = ctx.output;
|
|
13
|
+
const tasks = output.map((item, idx) => {
|
|
14
|
+
const fileNameGenerator = new utils_1.FileNameGenerator(item);
|
|
15
|
+
return uploader_1.default.createUploadTask({
|
|
139
16
|
client,
|
|
140
17
|
index: idx,
|
|
141
18
|
bucketName: userConfig.bucketName,
|
|
142
|
-
path:
|
|
19
|
+
path: fileNameGenerator.format(userConfig.uploadPath),
|
|
143
20
|
item: item,
|
|
144
|
-
acl: userConfig.acl,
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
21
|
+
acl: userConfig.acl || '',
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
let results;
|
|
25
|
+
try {
|
|
26
|
+
results = await Promise.all(tasks);
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
ctx.log.error("上传到 S3 存储发生错误,请检查网络连接和配置是否正确");
|
|
30
|
+
ctx.log.error(err);
|
|
31
|
+
ctx.emit("notification", {
|
|
32
|
+
title: "S3 存储上传错误",
|
|
33
|
+
body: "请检查配置是否正确",
|
|
34
|
+
text: "",
|
|
35
|
+
});
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
for (const result of results) {
|
|
39
|
+
const { index, url, key } = result;
|
|
40
|
+
delete output[index].buffer;
|
|
41
|
+
delete output[index].base64Image;
|
|
42
|
+
output[index].url = url;
|
|
43
|
+
output[index].imgUrl = url;
|
|
44
|
+
output[index].uploadPath = key;
|
|
45
|
+
}
|
|
46
|
+
return ctx;
|
|
47
|
+
};
|
|
48
|
+
const afterUploadPlugins = (ctx) => {
|
|
49
|
+
const userConfig = (0, config_1.loadUserConfig)(ctx);
|
|
50
|
+
ctx.output = ctx.output.map((item) => {
|
|
51
|
+
if (item.type != pluginName) {
|
|
52
|
+
return item;
|
|
167
53
|
}
|
|
168
|
-
|
|
169
|
-
|
|
54
|
+
const outputURLGenerator = new utils_1.OutputURLGenerator(userConfig, item);
|
|
55
|
+
const url = outputURLGenerator.format();
|
|
56
|
+
return Object.assign(Object.assign({}, item), { imgUrl: url, url: url });
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
const config = (ctx) => {
|
|
60
|
+
return (0, config_1.getPluginConfig)(ctx);
|
|
61
|
+
};
|
|
62
|
+
module.exports = (ctx) => {
|
|
170
63
|
const register = () => {
|
|
171
|
-
ctx.helper.uploader.register(
|
|
172
|
-
handle,
|
|
64
|
+
ctx.helper.uploader.register(pluginName, {
|
|
65
|
+
handle: upload,
|
|
173
66
|
config,
|
|
174
67
|
name: "Amazon S3",
|
|
175
68
|
});
|
|
69
|
+
ctx.helper.afterUploadPlugins.register(pluginName, {
|
|
70
|
+
handle: afterUploadPlugins,
|
|
71
|
+
config,
|
|
72
|
+
});
|
|
176
73
|
};
|
|
177
74
|
return {
|
|
178
75
|
register,
|
package/dist/uploader.d.ts
CHANGED
|
@@ -5,7 +5,6 @@ export interface IUploadResult {
|
|
|
5
5
|
index: number;
|
|
6
6
|
key: string;
|
|
7
7
|
url: string;
|
|
8
|
-
imgURL: string;
|
|
9
8
|
versionId?: string;
|
|
10
9
|
eTag?: string;
|
|
11
10
|
}
|
|
@@ -17,7 +16,6 @@ interface createUploadTaskOpts {
|
|
|
17
16
|
item: IImgInfo;
|
|
18
17
|
index: number;
|
|
19
18
|
acl: string;
|
|
20
|
-
urlPrefix?: string;
|
|
21
19
|
}
|
|
22
20
|
declare function createUploadTask(opts: createUploadTaskOpts): Promise<IUploadResult>;
|
|
23
21
|
declare function getFileURL(opts: createUploadTaskOpts, eTag: string, versionId: string): Promise<string>;
|
package/dist/uploader.js
CHANGED
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
const client_s3_1 = require("@aws-sdk/client-s3");
|
|
7
4
|
const s3_request_presigner_1 = require("@aws-sdk/s3-request-presigner");
|
|
8
5
|
const node_http_handler_1 = require("@smithy/node-http-handler");
|
|
9
|
-
const url_1 = __importDefault(require("url"));
|
|
10
6
|
const utils_1 = require("./utils");
|
|
11
7
|
function createS3Client(opts) {
|
|
8
|
+
var _a;
|
|
12
9
|
let sslEnabled = true;
|
|
13
10
|
try {
|
|
14
|
-
const u =
|
|
15
|
-
sslEnabled = u.protocol ===
|
|
11
|
+
const u = new URL(opts.endpoint || '');
|
|
12
|
+
sslEnabled = u.protocol === 'https:';
|
|
16
13
|
}
|
|
17
|
-
catch (
|
|
18
|
-
|
|
14
|
+
catch (err) {
|
|
15
|
+
console.warn('Failed to parse endpoint URL, defaulting to HTTPS:', err);
|
|
19
16
|
}
|
|
20
17
|
const httpHandlerOpts = {};
|
|
21
18
|
if (sslEnabled) {
|
|
@@ -25,34 +22,32 @@ function createS3Client(opts) {
|
|
|
25
22
|
httpHandlerOpts.httpAgent = ((0, utils_1.getProxyAgent)(opts.proxy, false, opts.rejectUnauthorized));
|
|
26
23
|
}
|
|
27
24
|
const clientOptions = {
|
|
28
|
-
region: opts.region ||
|
|
29
|
-
endpoint: opts.endpoint
|
|
25
|
+
region: opts.region || 'auto',
|
|
26
|
+
endpoint: opts.endpoint,
|
|
30
27
|
credentials: {
|
|
31
28
|
accessKeyId: opts.accessKeyID,
|
|
32
29
|
secretAccessKey: opts.secretAccessKey,
|
|
33
30
|
},
|
|
34
31
|
tls: sslEnabled,
|
|
35
|
-
forcePathStyle: opts.pathStyleAccess,
|
|
32
|
+
forcePathStyle: (_a = opts.pathStyleAccess) !== null && _a !== void 0 ? _a : false,
|
|
36
33
|
requestHandler: new node_http_handler_1.NodeHttpHandler(httpHandlerOpts),
|
|
37
34
|
};
|
|
38
|
-
|
|
39
|
-
return client;
|
|
35
|
+
return new client_s3_1.S3Client(clientOptions);
|
|
40
36
|
}
|
|
41
37
|
async function createUploadTask(opts) {
|
|
42
38
|
if (!opts.item.buffer && !opts.item.base64Image) {
|
|
43
|
-
|
|
39
|
+
throw new Error('No image data provided: buffer or base64Image is required');
|
|
44
40
|
}
|
|
45
41
|
let body;
|
|
46
42
|
let contentType;
|
|
47
43
|
let contentEncoding;
|
|
48
44
|
try {
|
|
49
|
-
;
|
|
50
45
|
({ body, contentType, contentEncoding } = await (0, utils_1.extractInfo)(opts.item));
|
|
51
46
|
}
|
|
52
47
|
catch (err) {
|
|
53
|
-
|
|
48
|
+
throw new Error(`Failed to extract image info: ${err instanceof Error ? err.message : String(err)}`);
|
|
54
49
|
}
|
|
55
|
-
const acl = opts.acl;
|
|
50
|
+
const acl = (opts.acl || 'public-read');
|
|
56
51
|
const command = new client_s3_1.PutObjectCommand({
|
|
57
52
|
Bucket: opts.bucketName,
|
|
58
53
|
Key: opts.path,
|
|
@@ -61,33 +56,19 @@ async function createUploadTask(opts) {
|
|
|
61
56
|
ContentType: contentType,
|
|
62
57
|
ContentEncoding: contentEncoding,
|
|
63
58
|
});
|
|
64
|
-
let output;
|
|
65
59
|
try {
|
|
66
|
-
output = await opts.client.send(command);
|
|
60
|
+
const output = await opts.client.send(command);
|
|
61
|
+
return {
|
|
62
|
+
index: opts.index,
|
|
63
|
+
key: opts.path,
|
|
64
|
+
url: await getFileURL(opts, output.ETag || '', output.VersionId || ''),
|
|
65
|
+
versionId: output.VersionId,
|
|
66
|
+
eTag: output.ETag,
|
|
67
|
+
};
|
|
67
68
|
}
|
|
68
69
|
catch (err) {
|
|
69
|
-
|
|
70
|
+
throw new Error(`Failed to upload to S3: ${err instanceof Error ? err.message : String(err)}`);
|
|
70
71
|
}
|
|
71
|
-
let url;
|
|
72
|
-
if (!opts.urlPrefix) {
|
|
73
|
-
try {
|
|
74
|
-
url = await getFileURL(opts, output.ETag, output.VersionId);
|
|
75
|
-
}
|
|
76
|
-
catch (err) {
|
|
77
|
-
return Promise.reject(err);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
url = `${opts.urlPrefix}/${opts.path}`;
|
|
82
|
-
}
|
|
83
|
-
return {
|
|
84
|
-
index: opts.index,
|
|
85
|
-
key: opts.path,
|
|
86
|
-
url: url,
|
|
87
|
-
imgURL: url,
|
|
88
|
-
versionId: output.VersionId,
|
|
89
|
-
eTag: output.ETag,
|
|
90
|
-
};
|
|
91
72
|
}
|
|
92
73
|
async function getFileURL(opts, eTag, versionId) {
|
|
93
74
|
try {
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,9 +1,62 @@
|
|
|
1
1
|
import { IImgInfo } from "picgo";
|
|
2
2
|
import { HttpsProxyAgent, HttpProxyAgent } from "hpagent";
|
|
3
|
-
|
|
3
|
+
import { IS3UserConfig } from "./config";
|
|
4
|
+
declare class Generateor {
|
|
5
|
+
readonly date: Date;
|
|
6
|
+
constructor();
|
|
7
|
+
protected year(): string;
|
|
8
|
+
protected month(): string;
|
|
9
|
+
protected day(): string;
|
|
10
|
+
protected hour(): string;
|
|
11
|
+
protected minute(): string;
|
|
12
|
+
protected second(): string;
|
|
13
|
+
protected millisecond(): string;
|
|
14
|
+
protected timestamp(): string;
|
|
15
|
+
protected timestampMS(): string;
|
|
16
|
+
format(s?: string): string;
|
|
17
|
+
}
|
|
18
|
+
export declare class FileNameGenerator extends Generateor {
|
|
19
|
+
readonly info: IImgInfo;
|
|
20
|
+
constructor(info: IImgInfo);
|
|
21
|
+
fullName(): string;
|
|
22
|
+
fileName(): string;
|
|
23
|
+
extName(): string;
|
|
24
|
+
md5(): string;
|
|
25
|
+
md5B64(): string;
|
|
26
|
+
md5B64Short(): string;
|
|
27
|
+
sha1(): string;
|
|
28
|
+
sha256(): string;
|
|
29
|
+
imgBuffer(): string | Buffer;
|
|
30
|
+
format(s?: string): string;
|
|
31
|
+
}
|
|
32
|
+
export declare class OutputURLGenerator extends Generateor {
|
|
33
|
+
readonly _config: IS3UserConfig;
|
|
34
|
+
readonly _protocol: string;
|
|
35
|
+
readonly _host: string;
|
|
36
|
+
readonly _port: string;
|
|
37
|
+
readonly _path: string;
|
|
38
|
+
readonly _query: string;
|
|
39
|
+
readonly _hash: string;
|
|
40
|
+
readonly _info: IImgInfo;
|
|
41
|
+
constructor(config: IS3UserConfig, info: IImgInfo);
|
|
42
|
+
protocol(): string;
|
|
43
|
+
host(): string;
|
|
44
|
+
port(): string;
|
|
45
|
+
path(): string;
|
|
46
|
+
fileName(): string;
|
|
47
|
+
extName(): string;
|
|
48
|
+
dir(): string;
|
|
49
|
+
originalURL(): string;
|
|
50
|
+
query(): string;
|
|
51
|
+
hash(): string;
|
|
52
|
+
bucket(): string;
|
|
53
|
+
legacyFormat(): string;
|
|
54
|
+
format(): string;
|
|
55
|
+
}
|
|
4
56
|
export declare function extractInfo(info: IImgInfo): Promise<{
|
|
5
57
|
body?: Buffer;
|
|
6
58
|
contentType?: string;
|
|
7
59
|
contentEncoding?: string;
|
|
8
60
|
}>;
|
|
9
61
|
export declare function getProxyAgent(proxy: string | undefined, sslEnabled: boolean, rejectUnauthorized: boolean): HttpProxyAgent | HttpsProxyAgent | undefined;
|
|
62
|
+
export {};
|
package/dist/utils.js
CHANGED
|
@@ -3,51 +3,82 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
6
|
+
exports.OutputURLGenerator = exports.FileNameGenerator = void 0;
|
|
7
7
|
exports.extractInfo = extractInfo;
|
|
8
8
|
exports.getProxyAgent = getProxyAgent;
|
|
9
9
|
const crypto_1 = __importDefault(require("crypto"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
10
11
|
const file_type_1 = require("file-type");
|
|
11
12
|
const mime_1 = __importDefault(require("mime"));
|
|
12
13
|
const hpagent_1 = require("hpagent");
|
|
13
|
-
class
|
|
14
|
-
constructor(
|
|
14
|
+
class Generateor {
|
|
15
|
+
constructor() {
|
|
15
16
|
this.date = new Date();
|
|
16
|
-
this.info = info;
|
|
17
17
|
}
|
|
18
18
|
year() {
|
|
19
|
-
return
|
|
19
|
+
return this.date.getFullYear().toString();
|
|
20
20
|
}
|
|
21
21
|
month() {
|
|
22
|
-
return this.date.getMonth()
|
|
23
|
-
? `0${this.date.getMonth() + 1}`
|
|
24
|
-
: `${this.date.getMonth() + 1}`;
|
|
22
|
+
return (this.date.getMonth() + 1).toString().padStart(2, '0');
|
|
25
23
|
}
|
|
26
24
|
day() {
|
|
27
|
-
return this.date.getDate()
|
|
28
|
-
? `0${this.date.getDate()}`
|
|
29
|
-
: `${this.date.getDate()}`;
|
|
25
|
+
return this.date.getDate().toString().padStart(2, '0');
|
|
30
26
|
}
|
|
31
27
|
hour() {
|
|
32
|
-
return this.date.getHours().toString().padStart(2,
|
|
28
|
+
return this.date.getHours().toString().padStart(2, '0');
|
|
33
29
|
}
|
|
34
30
|
minute() {
|
|
35
|
-
return this.date.getMinutes().toString().padStart(2,
|
|
31
|
+
return this.date.getMinutes().toString().padStart(2, '0');
|
|
36
32
|
}
|
|
37
33
|
second() {
|
|
38
|
-
return this.date.getSeconds().toString().padStart(2,
|
|
34
|
+
return this.date.getSeconds().toString().padStart(2, '0');
|
|
39
35
|
}
|
|
40
36
|
millisecond() {
|
|
41
|
-
return this.date.getMilliseconds().toString().padStart(3,
|
|
37
|
+
return this.date.getMilliseconds().toString().padStart(3, '0');
|
|
38
|
+
}
|
|
39
|
+
timestamp() {
|
|
40
|
+
return Math.floor(this.date.getTime() / 1000).toString();
|
|
41
|
+
}
|
|
42
|
+
timestampMS() {
|
|
43
|
+
return this.date.getTime().toString();
|
|
44
|
+
}
|
|
45
|
+
format(s) {
|
|
46
|
+
if (!s) {
|
|
47
|
+
return '';
|
|
48
|
+
}
|
|
49
|
+
const formatters = {
|
|
50
|
+
year: () => this.year(),
|
|
51
|
+
month: () => this.month(),
|
|
52
|
+
day: () => this.day(),
|
|
53
|
+
hour: () => this.hour(),
|
|
54
|
+
minute: () => this.minute(),
|
|
55
|
+
second: () => this.second(),
|
|
56
|
+
millisecond: () => this.millisecond(),
|
|
57
|
+
timestamp: () => this.timestamp(),
|
|
58
|
+
timestampMS: () => this.timestampMS(),
|
|
59
|
+
};
|
|
60
|
+
return Object.entries(formatters).reduce((result, [key, formatter]) => result.replace(new RegExp(`{${key}}`, 'g'), formatter()), s);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
class FileNameGenerator extends Generateor {
|
|
64
|
+
constructor(info) {
|
|
65
|
+
super();
|
|
66
|
+
this.info = info;
|
|
42
67
|
}
|
|
43
68
|
fullName() {
|
|
44
|
-
return this.info.fileName;
|
|
69
|
+
return this.info.fileName || "";
|
|
45
70
|
}
|
|
46
71
|
fileName() {
|
|
47
|
-
|
|
72
|
+
var _a;
|
|
73
|
+
if (!((_a = this.info) === null || _a === void 0 ? void 0 : _a.fileName)) {
|
|
74
|
+
return '';
|
|
75
|
+
}
|
|
76
|
+
const ext = this.info.extname || '';
|
|
77
|
+
return this.info.fileName.replace(new RegExp(`${ext}$`), '');
|
|
48
78
|
}
|
|
49
79
|
extName() {
|
|
50
|
-
|
|
80
|
+
var _a, _b;
|
|
81
|
+
return ((_b = (_a = this.info) === null || _a === void 0 ? void 0 : _a.extname) === null || _b === void 0 ? void 0 : _b.replace('.', '')) || '';
|
|
51
82
|
}
|
|
52
83
|
md5() {
|
|
53
84
|
return crypto_1.default.createHash("md5").update(this.imgBuffer()).digest("hex");
|
|
@@ -76,47 +107,139 @@ class FileNameGenerator {
|
|
|
76
107
|
sha256() {
|
|
77
108
|
return crypto_1.default.createHash("sha256").update(this.imgBuffer()).digest("hex");
|
|
78
109
|
}
|
|
79
|
-
timestamp() {
|
|
80
|
-
return Math.floor(Date.now() / 1000).toString();
|
|
81
|
-
}
|
|
82
|
-
timestampMS() {
|
|
83
|
-
return Date.now().toString();
|
|
84
|
-
}
|
|
85
110
|
imgBuffer() {
|
|
86
|
-
return this.info.base64Image ? this.info.base64Image : this.info.buffer;
|
|
111
|
+
return this.info.base64Image ? this.info.base64Image : (this.info.buffer || "");
|
|
112
|
+
}
|
|
113
|
+
format(s) {
|
|
114
|
+
if (!s) {
|
|
115
|
+
return this.fullName();
|
|
116
|
+
}
|
|
117
|
+
const formatters = {
|
|
118
|
+
fullName: () => this.fullName(),
|
|
119
|
+
fileName: () => this.fileName(),
|
|
120
|
+
extName: () => this.extName(),
|
|
121
|
+
md5: () => this.md5(),
|
|
122
|
+
md5B64: () => this.md5B64(),
|
|
123
|
+
md5B64Short: () => this.md5B64Short(),
|
|
124
|
+
sha1: () => this.sha1(),
|
|
125
|
+
sha256: () => this.sha256(),
|
|
126
|
+
};
|
|
127
|
+
return Object.entries(formatters).reduce((result, [key, formatter]) => result.replace(new RegExp(`{${key}}`, 'g'), formatter()), super.format(s));
|
|
87
128
|
}
|
|
88
129
|
}
|
|
89
|
-
FileNameGenerator
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
130
|
+
exports.FileNameGenerator = FileNameGenerator;
|
|
131
|
+
class OutputURLGenerator extends Generateor {
|
|
132
|
+
constructor(config, info) {
|
|
133
|
+
super();
|
|
134
|
+
this._config = config;
|
|
135
|
+
this._info = info;
|
|
136
|
+
// parse the url from storage
|
|
137
|
+
const url = info.url || info.imgUrl || '';
|
|
138
|
+
try {
|
|
139
|
+
const u = new URL(url);
|
|
140
|
+
this._protocol = u.protocol;
|
|
141
|
+
this._host = u.hostname;
|
|
142
|
+
this._port = u.port;
|
|
143
|
+
this._path = u.pathname;
|
|
144
|
+
this._query = u.search;
|
|
145
|
+
this._hash = u.hash;
|
|
146
|
+
}
|
|
147
|
+
catch (e) {
|
|
148
|
+
console.error(`Failed to parse URL: ${url}`, e);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
protocol() {
|
|
152
|
+
if (this._protocol) {
|
|
153
|
+
return this._protocol.replace(/(:)$/, '');
|
|
154
|
+
}
|
|
155
|
+
return "https";
|
|
156
|
+
}
|
|
157
|
+
host() {
|
|
158
|
+
return this._host;
|
|
159
|
+
}
|
|
160
|
+
port() {
|
|
161
|
+
return this._port;
|
|
162
|
+
}
|
|
163
|
+
path() {
|
|
164
|
+
return this._path.replace(/^(\/)/, '');
|
|
165
|
+
}
|
|
166
|
+
fileName() {
|
|
167
|
+
if (this._info.fileName) {
|
|
168
|
+
return this._info.fileName;
|
|
169
|
+
}
|
|
170
|
+
return path_1.default.basename(this.path());
|
|
171
|
+
}
|
|
172
|
+
extName() {
|
|
173
|
+
if (this._info.extname) {
|
|
174
|
+
return this._info.extname.replace(/^./, '');
|
|
175
|
+
}
|
|
176
|
+
return path_1.default.extname(this.path()).replace(/^./, '');
|
|
177
|
+
}
|
|
178
|
+
dir() {
|
|
179
|
+
return path_1.default.dirname(this.path());
|
|
180
|
+
}
|
|
181
|
+
originalURL() {
|
|
182
|
+
let url = this._info.url;
|
|
183
|
+
if (!url) {
|
|
184
|
+
url = this._info.imgUrl;
|
|
185
|
+
}
|
|
186
|
+
return url;
|
|
187
|
+
}
|
|
188
|
+
query() {
|
|
189
|
+
return this._query;
|
|
190
|
+
}
|
|
191
|
+
hash() {
|
|
192
|
+
return this._hash;
|
|
193
|
+
}
|
|
194
|
+
bucket() {
|
|
195
|
+
return this._config.bucketName;
|
|
196
|
+
}
|
|
197
|
+
legacyFormat() {
|
|
198
|
+
let url = this.originalURL();
|
|
199
|
+
let uploadPath = this._info.uploadPath || this.path();
|
|
200
|
+
if (this._config.urlPrefix) {
|
|
201
|
+
let urlPrefix = this._config.urlPrefix.replace(/\/?$/, "");
|
|
202
|
+
if (this._config.pathStyleAccess && !this._config.disableBucketPrefixToURL) {
|
|
203
|
+
urlPrefix += "/" + this._config.bucketName;
|
|
204
|
+
}
|
|
205
|
+
url = `${urlPrefix}/${uploadPath}`;
|
|
206
|
+
}
|
|
207
|
+
url = `${url}${this._config.urlSuffix || ''}`;
|
|
208
|
+
return url;
|
|
209
|
+
}
|
|
210
|
+
format() {
|
|
211
|
+
if (!this._config.outputURLPattern) {
|
|
212
|
+
return this.legacyFormat();
|
|
213
|
+
}
|
|
214
|
+
const formatters = {
|
|
215
|
+
protocol: () => this.protocol(),
|
|
216
|
+
host: () => this.host(),
|
|
217
|
+
port: () => this.port(),
|
|
218
|
+
dir: () => this.dir(),
|
|
219
|
+
fileName: () => this.fileName(),
|
|
220
|
+
path: () => this.path(),
|
|
221
|
+
extName: () => this.extName(),
|
|
222
|
+
query: () => this.query(),
|
|
223
|
+
hash: () => this.hash(),
|
|
224
|
+
bucket: () => this.bucket(),
|
|
225
|
+
};
|
|
226
|
+
return Object.entries(formatters).reduce((result, [key, formatter]) => {
|
|
227
|
+
const simplePattern = new RegExp(`{${key}}`, "g");
|
|
228
|
+
const advancedPattern = new RegExp(`{${key}:/(.*?)/(\\w)?,['"](.*)['"]}`, "g");
|
|
229
|
+
if (advancedPattern.test(result)) {
|
|
230
|
+
result = result.replace(advancedPattern, (match, p1, p2, p3) => {
|
|
231
|
+
const r = formatter();
|
|
232
|
+
return p2 ? r.replace(new RegExp(p1, p2), p3) : r;
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
result = result.replace(simplePattern, formatter());
|
|
237
|
+
}
|
|
238
|
+
return result;
|
|
239
|
+
}, super.format(this._config.outputURLPattern));
|
|
240
|
+
}
|
|
119
241
|
}
|
|
242
|
+
exports.OutputURLGenerator = OutputURLGenerator;
|
|
120
243
|
async function extractInfo(info) {
|
|
121
244
|
var _a;
|
|
122
245
|
const result = {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "picgo-plugin-s3",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0-beta.2",
|
|
4
4
|
"description": "picgo amazon s3 uploader",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"publishConfig": {
|
|
@@ -30,23 +30,33 @@
|
|
|
30
30
|
"license": "MIT",
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/mime": "^3.0.4",
|
|
33
|
-
"@
|
|
34
|
-
"@
|
|
35
|
-
"@typescript-eslint/
|
|
36
|
-
"eslint": "^8.
|
|
33
|
+
"@eslint/js": "^9.16.0",
|
|
34
|
+
"@types/node": "^22.10.1",
|
|
35
|
+
"@typescript-eslint/eslint-plugin": "^8.17.0",
|
|
36
|
+
"@typescript-eslint/parser": "^8.17.0",
|
|
37
|
+
"eslint": "^9.16.0",
|
|
37
38
|
"eslint-config-prettier": "^9.1.0",
|
|
38
|
-
"eslint-plugin-prettier": "^5.1
|
|
39
|
-
"prettier": "^3.2
|
|
40
|
-
"typescript": "^5.
|
|
39
|
+
"eslint-plugin-prettier": "^5.2.1",
|
|
40
|
+
"prettier": "^3.4.2",
|
|
41
|
+
"typescript": "^5.7.2"
|
|
41
42
|
},
|
|
42
43
|
"dependencies": {
|
|
43
|
-
"@aws-sdk/client-s3": "^3.
|
|
44
|
-
"@aws-sdk/lib-storage": "^3.
|
|
45
|
-
"@aws-sdk/
|
|
46
|
-
"@
|
|
44
|
+
"@aws-sdk/client-s3": "^3.705.0",
|
|
45
|
+
"@aws-sdk/lib-storage": "^3.705.0",
|
|
46
|
+
"@aws-sdk/s3-request-presigner": "^3.705.0",
|
|
47
|
+
"@smithy/node-http-handler": "^3.3.1",
|
|
47
48
|
"file-type": ">16.5.0 <17.0.0",
|
|
48
49
|
"hpagent": "^1.2.0",
|
|
49
|
-
"mime": "
|
|
50
|
-
"picgo": "^1.5.
|
|
50
|
+
"mime": ">=3.0.0 <4.0.0",
|
|
51
|
+
"picgo": "^1.5.8"
|
|
52
|
+
},
|
|
53
|
+
"pnpm": {
|
|
54
|
+
"overrides": {
|
|
55
|
+
"got@<11.8.5": ">=11.8.5",
|
|
56
|
+
"ejs@<3.1.7": ">=3.1.7",
|
|
57
|
+
"http-cache-semantics@<4.1.1": ">=4.1.1",
|
|
58
|
+
"axios@>=0.8.1 <0.28.0": ">=0.28.0",
|
|
59
|
+
"ejs@<3.1.10": ">=3.1.10"
|
|
60
|
+
}
|
|
51
61
|
}
|
|
52
62
|
}
|
package/.eslintrc
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"parser": "@typescript-eslint/parser",
|
|
3
|
-
"extends": [
|
|
4
|
-
"eslint:recommended",
|
|
5
|
-
"plugin:@typescript-eslint/recommended",
|
|
6
|
-
"prettier"
|
|
7
|
-
],
|
|
8
|
-
"plugins": [
|
|
9
|
-
"prettier",
|
|
10
|
-
"@typescript-eslint"
|
|
11
|
-
],
|
|
12
|
-
"env": {
|
|
13
|
-
"es6": true,
|
|
14
|
-
"node": true
|
|
15
|
-
},
|
|
16
|
-
"rules": {
|
|
17
|
-
"prettier/prettier": [
|
|
18
|
-
"error",
|
|
19
|
-
{
|
|
20
|
-
"semi": false
|
|
21
|
-
}
|
|
22
|
-
]
|
|
23
|
-
}
|
|
24
|
-
}
|