picgo-plugin-gitee 2.0.7 → 2.0.8

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/package.json CHANGED
@@ -1,17 +1,37 @@
1
1
  {
2
2
  "name": "picgo-plugin-gitee",
3
- "version": "2.0.7",
3
+ "version": "2.0.8",
4
4
  "description": "picgo uploader for gitee",
5
5
  "main": "src/index.js",
6
+ "files": [
7
+ "src/**/*",
8
+ "readme.md",
9
+ "LICENSE"
10
+ ],
6
11
  "publishConfig": {
7
- "access": "public"
12
+ "access": "public",
13
+ "registry": "https://registry.npmjs.org/"
8
14
  },
9
15
  "homepage": "https://github.com/zhanghuid/picgo-plugin-gitee",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/zhanghuid/picgo-plugin-gitee.git"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/zhanghuid/picgo-plugin-gitee/issues"
22
+ },
10
23
  "scripts": {
24
+ "lint": "eslint src/index.js",
11
25
  "test": "echo \"Error: no test specified\" && exit 1",
12
- "patch": "npm version patch && git push origin master && git push origin --tags",
13
- "minor": "npm version minor && git push origin master && git push origin --tags",
14
- "major": "npm version major && git push origin master && git push origin --tags"
26
+ "bump": "bumpp --commit \"chore(release): v%s\"",
27
+ "bump:patch": "bumpp patch --commit \"chore(release): v%s\"",
28
+ "bump:minor": "bumpp minor --commit \"chore(release): v%s\"",
29
+ "bump:major": "bumpp major --commit \"chore(release): v%s\"",
30
+ "release": "npm run bump && npm publish",
31
+ "release:patch": "npm run bump:patch && npm publish",
32
+ "release:minor": "npm run bump:minor && npm publish",
33
+ "release:major": "npm run bump:major && npm publish",
34
+ "prepublishOnly": "node --check src/index.js"
15
35
  },
16
36
  "keywords": [
17
37
  "picgo",
@@ -21,13 +41,13 @@
21
41
  "author": "zhanghuid",
22
42
  "license": "MIT",
23
43
  "devDependencies": {
44
+ "@varnxy/logger": "^1.1.2",
45
+ "bumpp": "^12.3.0",
24
46
  "eslint": "^5.0.1",
25
47
  "eslint-config-standard": "^11.0.0",
26
48
  "eslint-plugin-import": "^2.13.0",
27
49
  "eslint-plugin-node": "^6.0.1",
28
50
  "eslint-plugin-promise": "^3.8.0",
29
- "eslint-plugin-standard": "^3.1.0",
30
- "@varnxy/logger": "^1.1.2"
31
- },
32
- "dependencies": {}
33
- }
51
+ "eslint-plugin-standard": "^3.1.0"
52
+ }
53
+ }
package/readme.md CHANGED
@@ -17,9 +17,12 @@ npm i picgo-plugin-gitee
17
17
  #### input your config
18
18
  - owner: gitee project's owner name
19
19
  - repo: gitee project repo
20
- - token: gitee's api token
21
- - path: img path in response json (eg:url or data.url)
22
- - message: gitee commit
20
+ - path: img path in response json (eg:url or data.url), 根目录可留空
21
+ - token: gitee's api token(在 https://gitee.com/profile/personal_access_tokens 生成)
22
+ - customUrl: 自定义独立域名(可选),留空默认使用 `https://raw.giteeusercontent.com`,避免默认域名被 302 重定向
23
+ - message: gitee commit,默认 `picgo commit`
24
+
25
+ > 默认预览地址使用 [Gitee 独立域名 `raw.giteeusercontent.com`](https://help.gitee.com/repository/file-operate/raw#%E7%8B%AC%E7%AB%8B%E5%9F%9F%E5%90%8D),不再走 `gitee.com` 的 302。
23
26
 
24
27
  #### init your remote repo
25
28
  - create git repo?
@@ -42,6 +45,7 @@ git push -u origin master
42
45
 
43
46
  ### Feature
44
47
  - support sync gitee file delete
48
+ - 上传/删除结束均会弹出系统通知(PicGo 消息中心)
45
49
 
46
50
  ### Todo
47
51
 
package/src/index.js CHANGED
@@ -1,5 +1,9 @@
1
1
  const uploadedName = "gitee";
2
2
  const domain = "https://gitee.com";
3
+ // 独立域名:直接命中,避免走默认域名的 302 重定向。
4
+ // 参考:https://help.gitee.com/repository/file-operate/raw#%E7%8B%AC%E7%AB%8B%E5%9F%9F%E5%90%8D
5
+ const previewDomain = "https://raw.giteeusercontent.com";
6
+ const apiPath = "/api/v5/repos";
3
7
  const urlParser = require("url");
4
8
  const defaultMsg = "picgo commit";
5
9
 
@@ -28,9 +32,9 @@ module.exports = (ctx) => {
28
32
  }
29
33
 
30
34
  userConfig["baseUrl"] =
31
- domain + "/api/v5/repos/" + userConfig.owner + "/" + userConfig.repo;
35
+ domain + apiPath + "/" + userConfig.owner + "/" + userConfig.repo;
32
36
  userConfig["previewUrl"] =
33
- domain +
37
+ (userConfig.customUrl || previewDomain) +
34
38
  "/" +
35
39
  userConfig.owner +
36
40
  "/" +
@@ -51,6 +55,10 @@ module.exports = (ctx) => {
51
55
  userConfig.baseUrl + "/contents" + formatConfigPath(userConfig);
52
56
 
53
57
  let imgList = ctx.output;
58
+ let successCount = 0;
59
+ let failCount = 0;
60
+ const fails = [];
61
+
54
62
  for (let i in imgList) {
55
63
  let image = imgList[i].buffer;
56
64
  if (!image && imgList[i].base64Image) {
@@ -64,32 +72,53 @@ module.exports = (ctx) => {
64
72
  await ctx.Request.request(postConfig);
65
73
  imgList[i]["imgUrl"] =
66
74
  userConfig.previewUrl + "/" + imgList[i].fileName;
75
+ successCount += 1;
76
+ ctx.log.info(
77
+ "[上传操作]成功:" + imgList[i].fileName + " -> " + imgList[i].imgUrl
78
+ );
67
79
  } catch (err) {
68
- ctx.log.info("[上传操作]异常:" + err.message);
69
80
  // duplicate file, so continue
70
81
  if (checkIsDuplicateFile(err.message)) {
82
+ ctx.log.info("[上传操作]文件已存在:" + imgList[i].fileName);
71
83
  ctx.emit("notification", {
72
84
  title: "上传失败",
73
- body: "文件已经存在了",
85
+ body: `${imgList[i].fileName} 文件已经存在了`,
74
86
  });
87
+ failCount += 1;
75
88
  continue;
76
- } else {
77
- ctx.emit("notification", {
78
- title: "上传失败",
79
- body: JSON.stringify(err),
80
- });
81
89
  }
90
+ failCount += 1;
91
+ fails.push(`${imgList[i].fileName}: ${err.message}`);
92
+ ctx.log.info("[上传操作]异常:" + err.message);
93
+ ctx.emit("notification", {
94
+ title: "上传失败",
95
+ body: `${imgList[i].fileName} ${err.message}`,
96
+ });
82
97
  }
83
98
 
84
99
  delete imgList[i].base64Image;
85
100
  delete imgList[i].buffer;
86
101
  }
87
102
 
103
+ // 上传结束后统一给一条汇总通知,避免每次上传都点弹窗打扰用户。
104
+ if (successCount > 0) {
105
+ ctx.emit("notification", {
106
+ title: "上传完成",
107
+ body: `成功 ${successCount} 张${failCount > 0 ? `,失败 ${failCount} 张` : ""}`,
108
+ });
109
+ }
110
+ if (fails.length > 0) {
111
+ ctx.log.info("[上传操作]失败明细:" + JSON.stringify(fails));
112
+ }
113
+
88
114
  return ctx;
89
115
  };
90
116
 
91
117
  const checkIsDuplicateFile = (message) => {
92
- return message.indexOf("A file with this name already exists") != -1;
118
+ return (
119
+ message.indexOf("A file with this name already exists") != -1 ||
120
+ message.indexOf("文件已经存在") != -1
121
+ );
93
122
  };
94
123
 
95
124
  const postOptions = (url, image) => {
@@ -111,53 +140,97 @@ module.exports = (ctx) => {
111
140
 
112
141
  // trigger delete file
113
142
  const onRemove = async function (files) {
114
- // log request params
115
143
  const rms = files.filter((each) => each.type === uploadedName);
116
144
  if (rms.length === 0) {
117
145
  return;
118
146
  }
119
147
 
120
148
  ctx.log.info("删除个数:" + rms.length);
121
- ctx.log.info("uploaded 信息:");
122
149
  let headers = getHeaders();
123
150
  let config = getUserConfig();
124
- const fail = [];
151
+ const fails = [];
152
+ const successNames = [];
125
153
 
126
154
  for (let i = 0; i < rms.length; i++) {
127
155
  const each = rms[i];
128
156
  let filepath = getFilePath(each.imgUrl);
129
- let sha = await getSha(filepath).catch((err) => {
130
- ctx.log.info("[删除操作]获取sha值:" + JSON.stringify(err));
131
- });
157
+ let sha;
158
+ try {
159
+ sha = await getSha(filepath);
160
+ } catch (err) {
161
+ ctx.log.info(
162
+ "[删除操作]获取 sha 失败,跳过:" + each.imgUrl + " " + err.message
163
+ );
164
+ fails.push(`${each.fileName || each.imgUrl}: 获取 sha 失败`);
165
+ ctx.emit("notification", {
166
+ title: "删除失败",
167
+ body: `${each.fileName || each.imgUrl} 获取 sha 失败`,
168
+ });
169
+ continue;
170
+ }
132
171
 
133
- let url =
172
+ if (!sha) {
173
+ ctx.log.info("[删除操作]sha 为空,文件可能已被删除:" + each.imgUrl);
174
+ fails.push(`${each.fileName || each.imgUrl}: 文件不存在`);
175
+ continue;
176
+ }
177
+
178
+ const url =
134
179
  `${filepath}` +
135
180
  `?access_token=${config.token}` +
136
- `&message=${config.message}` +
181
+ `&message=${encodeURIComponent(config.message || defaultMsg)}` +
137
182
  `&sha=${sha}`;
138
183
  ctx.log.info("[删除操作]当前删除地址:" + url);
139
- let opts = {
184
+ const opts = {
140
185
  method: "DELETE",
141
- url: encodeURI(url),
186
+ url: url,
142
187
  headers: headers,
143
188
  };
144
- ctx.log.info("[删除操作]当前参数" + JSON.stringify(opts));
145
- // log request params
146
- response = await ctx.request(opts);
147
- ctx.log.info(response);
189
+ try {
190
+ const response = await ctx.Request.request(opts);
191
+ ctx.log.info("[删除操作]响应:" + response);
192
+ successNames.push(each.fileName || each.imgUrl);
193
+ } catch (err) {
194
+ ctx.log.info(
195
+ "[删除操作]失败:" +
196
+ (each.fileName || each.imgUrl) +
197
+ " " +
198
+ err.message
199
+ );
200
+ fails.push(`${each.fileName || each.imgUrl}: ${err.message}`);
201
+ ctx.emit("notification", {
202
+ title: "删除失败",
203
+ body: `${each.fileName || each.imgUrl} ${err.message}`,
204
+ });
205
+ }
148
206
  }
149
207
 
150
- ctx.emit("notification", {
151
- title: "删除提示",
152
- body: fail.length === 0 ? "成功同步删除" : `删除失败${fail.length}个`,
153
- });
208
+ // 汇总通知,避免重复打扰。
209
+ if (successNames.length > 0) {
210
+ ctx.emit("notification", {
211
+ title: "删除完成",
212
+ body:
213
+ `成功同步删除 ${successNames.length} 个` +
214
+ (fails.length > 0 ? `,失败 ${fails.length} 个` : ""),
215
+ });
216
+ }
217
+ if (fails.length > 0) {
218
+ ctx.log.info("[删除操作]失败明细:" + JSON.stringify(fails));
219
+ }
154
220
  };
155
221
 
222
+ // 把预览 URL 转成删除用的 API URL。
223
+ // previewUrl 可能是默认域名、独立域名(raw.giteeusercontent.com)或用户自定义 customUrl,
224
+ // 但 Gitee API 始终在 gitee.com,所以这里统一映射到 gitee.com/api/v5/repos/...。
156
225
  const getFilePath = function (url) {
157
- let pathInfo = urlParser.parse(url);
158
- let baseUrl = pathInfo.protocol + "//" + pathInfo.host;
159
- let urlStr = url.replace(baseUrl, baseUrl + "/api/v5/repos");
160
- return urlStr.replace("raw/master", "contents");
226
+ let parsed = urlParser.parse(url);
227
+ // 去掉 raw/master,保留 owner/repo/path
228
+ let urlStr = url.replace("raw/master", "contents");
229
+ // 把 host 替换为 gitee.com,并插入 /api/v5/repos
230
+ return urlStr.replace(
231
+ `${parsed.protocol}//${parsed.host}`,
232
+ `${parsed.protocol}//gitee.com${apiPath}`
233
+ );
161
234
  };
162
235
 
163
236
  const getSha = async function (filepath) {
@@ -167,7 +240,7 @@ module.exports = (ctx) => {
167
240
 
168
241
  const opts = {
169
242
  method: "GET",
170
- url: encodeURI(url),
243
+ url: url,
171
244
  headers: headers,
172
245
  };
173
246
 
@@ -187,21 +260,15 @@ module.exports = (ctx) => {
187
260
  userConfig = {};
188
261
  }
189
262
  return [
190
- // {
191
- // name: 'url',
192
- // type: 'input',
193
- // default: userConfig.url,
194
- // required: true,
195
- // message: 'https://gitee.com',
196
- // alias: 'url'
197
- // },
198
263
  {
199
264
  name: "owner",
200
265
  type: "input",
201
266
  default: userConfig.owner,
202
267
  required: true,
203
268
  message: "owner",
204
- alias: "owner",
269
+ alias: "仓库所属用户/组织",
270
+ tips:
271
+ "仓库 owner,例如仓库地址 https://gitee.com/zhanghuid/resources 中 owner 为 zhanghuid",
205
272
  },
206
273
  {
207
274
  name: "repo",
@@ -209,23 +276,37 @@ module.exports = (ctx) => {
209
276
  default: userConfig.repo,
210
277
  required: true,
211
278
  message: "repo",
212
- alias: "repo",
279
+ alias: "仓库名",
280
+ tips: "仓库名,例如 resources",
213
281
  },
214
282
  {
215
283
  name: "path",
216
284
  type: "input",
217
285
  default: userConfig.path,
218
286
  required: false,
219
- message: "path;根目录可不用填",
220
- alias: "path",
287
+ message: "根目录可留空,例如 img/2026",
288
+ alias: "存储路径",
289
+ tips: "图片在仓库内的存储子目录,根目录可不填",
221
290
  },
222
291
  {
223
292
  name: "token",
224
- type: "input",
293
+ type: "password",
225
294
  default: userConfig.token,
226
295
  required: true,
227
- message: "5664b5620fb11111e3183a98011113ca31",
228
- alias: "token",
296
+ message: "Gitee 私人令牌",
297
+ alias: "私人令牌 (token)",
298
+ tips:
299
+ "在 https://gitee.com/profile/personal_access_tokens 生成,需要 projects 和 pull_request 权限",
300
+ },
301
+ {
302
+ name: "customUrl",
303
+ type: "input",
304
+ default: userConfig.customUrl,
305
+ required: false,
306
+ message: "可留空,默认使用 raw.giteeusercontent.com",
307
+ alias: "自定义独立域名(可选)",
308
+ tips:
309
+ "如有自有独立域名(反代 raw.giteeusercontent.com 或 Gitee Page 绑定的域名),填这里;留空则默认使用 https://raw.giteeusercontent.com",
229
310
  },
230
311
  {
231
312
  name: "message",
@@ -233,7 +314,8 @@ module.exports = (ctx) => {
233
314
  default: userConfig.message,
234
315
  required: false,
235
316
  message: defaultMsg,
236
- alias: "message",
317
+ alias: "提交信息 (commit message)",
318
+ tips: "上传/删除文件时使用的 commit message,留空使用默认 picgo commit",
237
319
  },
238
320
  ];
239
321
  };
package/.eslintrc DELETED
@@ -1,3 +0,0 @@
1
- {
2
- "extends": "standard"
3
- }
package/CONTRIBUTING.md DELETED
@@ -1,14 +0,0 @@
1
- # 开发者说明
2
-
3
- ## 开发环境搭建
4
- [插件测试与发布](https://picgo.github.io/PicGo-Core-Doc/zh/dev-guide/deploy.html#%E6%99%AE%E9%80%9A%E6%8F%92%E4%BB%B6)
5
- [图片转base64工具](https://tool.chinaz.com/tools/imgtobase)
6
-
7
- ## 相关 API
8
- - [新建文件](https://gitee.com/api/v5/swagger#/postV5ReposOwnerRepoContentsPath)
9
- - [删除文件](https://gitee.com/api/v5/swagger#/deleteV5ReposOwnerRepoContentsPath)
10
-
11
- ## 步骤
12
- 1. 按照 **开发环境搭建 > 插件测试与发布** 的教程,直接使用本地安装
13
- 2. 安装完后,重启应用生效
14
- 3. 更改 `package.json`的版本号