diff --git a/libs/Universal-PicGo-Core/src/plugins/uploader/aliyun.ts b/libs/Universal-PicGo-Core/src/plugins/uploader/aliyun/index.ts
similarity index 81%
rename from libs/Universal-PicGo-Core/src/plugins/uploader/aliyun.ts
rename to libs/Universal-PicGo-Core/src/plugins/uploader/aliyun/index.ts
index c94796d..a42488d 100644
--- a/libs/Universal-PicGo-Core/src/plugins/uploader/aliyun.ts
+++ b/libs/Universal-PicGo-Core/src/plugins/uploader/aliyun/index.ts
@@ -1,8 +1,17 @@
-import { IAliyunConfig, IPicGo, IPluginConfig } from "../../types"
-import { ILocalesKey } from "../../i18n/zh-CN"
+/*
+ * GNU GENERAL PUBLIC LICENSE
+ * Version 3, 29 June 2007
+ *
+ * Copyright (C) 2024 Terwer, Inc.
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+
+import { IAliyunConfig, IPicGo, IPluginConfig } from "../../../types"
+import { ILocalesKey } from "../../../i18n/zh-CN"
import { hasNodeEnv } from "universal-picgo-store"
-import { handleNode } from "./aliyun-node"
-import { handleWeb } from "./aliyun-web"
+import { handleNode } from "./node"
+import { handleWeb } from "./web"
const handle = async (ctx: IPicGo): Promise => {
if (hasNodeEnv) {
@@ -21,7 +30,7 @@ const config = (ctx: IPicGo): IPluginConfig[] => {
return ctx.i18n.translate("PICBED_ALICLOUD_ACCESSKEYID")
},
default: userConfig.accessKeyId || "",
- required: true
+ required: true,
},
{
name: "accessKeySecret",
@@ -30,7 +39,7 @@ const config = (ctx: IPicGo): IPluginConfig[] => {
return ctx.i18n.translate("PICBED_ALICLOUD_ACCESSKEYSECRET")
},
default: userConfig.accessKeySecret || "",
- required: true
+ required: true,
},
{
name: "bucket",
@@ -39,7 +48,7 @@ const config = (ctx: IPicGo): IPluginConfig[] => {
return ctx.i18n.translate("PICBED_ALICLOUD_BUCKET")
},
default: userConfig.bucket || "",
- required: true
+ required: true,
},
{
name: "area",
@@ -54,7 +63,7 @@ const config = (ctx: IPicGo): IPluginConfig[] => {
get message() {
return ctx.i18n.translate("PICBED_ALICLOUD_MESSAGE_AREA")
},
- required: true
+ required: true,
},
{
name: "path",
@@ -69,7 +78,7 @@ const config = (ctx: IPicGo): IPluginConfig[] => {
return ctx.i18n.translate("PICBED_ALICLOUD_MESSAGE_PATH")
},
default: userConfig.path || "",
- required: false
+ required: false,
},
{
name: "customUrl",
@@ -84,7 +93,7 @@ const config = (ctx: IPicGo): IPluginConfig[] => {
return ctx.i18n.translate("PICBED_ALICLOUD_MESSAGE_CUSTOMURL")
},
default: userConfig.customUrl || "",
- required: false
+ required: false,
},
{
name: "options",
@@ -99,8 +108,8 @@ const config = (ctx: IPicGo): IPluginConfig[] => {
return ctx.i18n.translate("PICBED_ALICLOUD_MESSAGE_OPTIONS")
},
default: userConfig.options || "",
- required: false
- }
+ required: false,
+ },
]
return config
}
@@ -111,6 +120,6 @@ export default function register(ctx: IPicGo): void {
return ctx.i18n.translate("PICBED_ALICLOUD")
},
handle,
- config
+ config,
})
}
diff --git a/libs/Universal-PicGo-Core/src/plugins/uploader/aliyun-node.ts b/libs/Universal-PicGo-Core/src/plugins/uploader/aliyun/node.ts
similarity index 81%
rename from libs/Universal-PicGo-Core/src/plugins/uploader/aliyun-node.ts
rename to libs/Universal-PicGo-Core/src/plugins/uploader/aliyun/node.ts
index be99c11..71cb1ea 100644
--- a/libs/Universal-PicGo-Core/src/plugins/uploader/aliyun-node.ts
+++ b/libs/Universal-PicGo-Core/src/plugins/uploader/aliyun/node.ts
@@ -1,9 +1,18 @@
+/*
+ * GNU GENERAL PUBLIC LICENSE
+ * Version 3, 29 June 2007
+ *
+ * Copyright (C) 2024 Terwer, Inc.
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+
// noinspection ES6PreferShortImport
-import { IAliyunConfig, IPicGo } from "../../types"
-import { IBuildInEvent } from "../../utils/enums"
-import { ILocalesKey } from "../../i18n/zh-CN"
-import { base64ToBuffer } from "../../utils/common"
+import { IAliyunConfig, IPicGo } from "../../../types"
+import { IBuildInEvent } from "../../../utils/enums"
+import { ILocalesKey } from "../../../i18n/zh-CN"
+import { base64ToBuffer } from "../../../utils/common"
import OSS from "ali-oss"
const handleNode = async (ctx: IPicGo): Promise => {
diff --git a/libs/Universal-PicGo-Core/src/plugins/uploader/aliyun-web.ts b/libs/Universal-PicGo-Core/src/plugins/uploader/aliyun/web.ts
similarity index 86%
rename from libs/Universal-PicGo-Core/src/plugins/uploader/aliyun-web.ts
rename to libs/Universal-PicGo-Core/src/plugins/uploader/aliyun/web.ts
index 3d6b75c..965c687 100644
--- a/libs/Universal-PicGo-Core/src/plugins/uploader/aliyun-web.ts
+++ b/libs/Universal-PicGo-Core/src/plugins/uploader/aliyun/web.ts
@@ -1,10 +1,19 @@
-import { IAliyunConfig, IPicGo } from "../../types"
+/*
+ * GNU GENERAL PUBLIC LICENSE
+ * Version 3, 29 June 2007
+ *
+ * Copyright (C) 2024 Terwer, Inc.
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+
+import { IAliyunConfig, IPicGo } from "../../../types"
import crypto from "crypto"
import mime from "mime-types"
-import { IBuildInEvent } from "../../utils/enums"
-import { ILocalesKey } from "../../i18n/zh-CN"
+import { IBuildInEvent } from "../../../utils/enums"
+import { ILocalesKey } from "../../../i18n/zh-CN"
import { AxiosRequestConfig } from "axios"
-import { base64ToBuffer, safeParse } from "../../utils/common"
+import { base64ToBuffer, safeParse } from "../../../utils/common"
// generate OSS signature
const generateSignature = (options: IAliyunConfig, fileName: string): string => {
diff --git a/libs/Universal-PicGo-Core/src/plugins/uploader/index.ts b/libs/Universal-PicGo-Core/src/plugins/uploader/index.ts
index c37395d..f3594a0 100644
--- a/libs/Universal-PicGo-Core/src/plugins/uploader/index.ts
+++ b/libs/Universal-PicGo-Core/src/plugins/uploader/index.ts
@@ -8,19 +8,21 @@
*/
import { IPicGo, IPicGoPlugin } from "../../types"
-import SMMSUploader from "./smms"
import githubUploader from "./github"
import gitlabUploader from "./gitlab"
import aliYunUploader from "./aliyun"
+import qiniuUploader from "./qiniu"
+import SMMSUploader from "./smms"
import imgurUploader from "./imgur"
const buildInUploaders: IPicGoPlugin = () => {
return {
register(ctx: IPicGo) {
- SMMSUploader(ctx)
githubUploader(ctx)
gitlabUploader(ctx)
aliYunUploader(ctx)
+ qiniuUploader(ctx)
+ SMMSUploader(ctx)
imgurUploader(ctx)
},
}
diff --git a/libs/Universal-PicGo-Core/src/plugins/uploader/qiniu/digest.ts b/libs/Universal-PicGo-Core/src/plugins/uploader/qiniu/digest.ts
new file mode 100644
index 0000000..58353a0
--- /dev/null
+++ b/libs/Universal-PicGo-Core/src/plugins/uploader/qiniu/digest.ts
@@ -0,0 +1,22 @@
+/*
+ * GNU GENERAL PUBLIC LICENSE
+ * Version 3, 29 June 2007
+ *
+ * Copyright (C) 2024 Terwer, Inc.
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+
+class Mac {
+ accessKey: string
+ secretKey: string
+ options: any
+
+ constructor(accessKey: string, secretKey: string, options?: Partial) {
+ this.accessKey = accessKey
+ this.secretKey = secretKey
+ this.options = { ...options }
+ }
+}
+
+export { Mac }
diff --git a/libs/Universal-PicGo-Core/src/plugins/uploader/qiniu/index.ts b/libs/Universal-PicGo-Core/src/plugins/uploader/qiniu/index.ts
new file mode 100644
index 0000000..05e3ca1
--- /dev/null
+++ b/libs/Universal-PicGo-Core/src/plugins/uploader/qiniu/index.ts
@@ -0,0 +1,210 @@
+/*
+ * GNU GENERAL PUBLIC LICENSE
+ * Version 3, 29 June 2007
+ *
+ * Copyright (C) 2024 Terwer, Inc.
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+
+import { ILocalesKey } from "../../../i18n/zh-CN"
+import { IPicGo, IPluginConfig, IQiniuConfig } from "../../../types"
+import { IBuildInEvent } from "../../../utils/enums"
+import { bufferToBase64, safeParse } from "../../../utils/common"
+import mime from "mime-types"
+import { AxiosRequestConfig } from "axios"
+import { Mac } from "./digest"
+import { PutPolicy } from "./rs"
+
+function postOptions(options: IQiniuConfig, fileName: string, token: string, imgBase64: string): AxiosRequestConfig {
+ const area = selectArea(options.area || "z0")
+ const path = options.path || ""
+ const base64FileName = Buffer.from(path + fileName, "utf-8")
+ .toString("base64")
+ .replace(/\+/g, "-")
+ .replace(/\//g, "_")
+ return {
+ method: "POST",
+ url: `http://upload${area}.qiniup.com/putb64/-1/key/${base64FileName}`,
+ headers: {
+ Authorization: `UpToken ${token}`,
+ "Content-Type": mime.lookup(fileName) || "application/octet-stream",
+ },
+ data: imgBase64,
+ proxy: false,
+ }
+}
+
+function selectArea(area: string): string {
+ return area === "z0" ? "" : "-" + area
+}
+
+function getToken(qiniuOptions: any): string {
+ const accessKey = qiniuOptions.accessKey
+ const secretKey = qiniuOptions.secretKey
+ const mac = new Mac(accessKey, secretKey)
+ const options = {
+ scope: qiniuOptions.bucket,
+ }
+ const putPolicy = new PutPolicy(options)
+ return putPolicy.uploadToken(mac)
+}
+
+const handle = async (ctx: IPicGo): Promise => {
+ const qiniuOptions = ctx.getConfig("picBed.qiniu")
+ if (!qiniuOptions) {
+ throw new Error("Can't find qiniu config")
+ }
+
+ const imgList = ctx.output
+ for (const img of imgList) {
+ if (img.fileName && img.buffer) {
+ try {
+ let base64Image = img.base64Image
+ if (!base64Image && img.buffer) {
+ base64Image = bufferToBase64(img.buffer)
+ }
+ if (!base64Image) {
+ ctx.log.error("Can not find image base64")
+ throw new Error("Can not find image base64")
+ }
+ const options = postOptions(qiniuOptions, img.fileName, getToken(qiniuOptions), base64Image)
+ const res: any = await ctx.request(options)
+ const body = safeParse(res)
+
+ if (body?.key) {
+ delete img.base64Image
+ delete img.buffer
+ const baseUrl = qiniuOptions.url
+ const options = qiniuOptions.options
+ img.imgUrl = `${baseUrl}/${body.key as string}${options}`
+ } else {
+ ctx.emit(IBuildInEvent.NOTIFICATION, {
+ title: ctx.i18n.translate("UPLOAD_FAILED"),
+ body: body.msg,
+ })
+ ctx.log.error("qiniu error", body)
+ throw new Error("Upload failed")
+ }
+ } catch (e: any) {
+ if (e.message !== "Upload failed") {
+ // err.response maybe undefined
+ if (e.error) {
+ const errMsg = e.error
+ ctx.emit(IBuildInEvent.NOTIFICATION, {
+ title: ctx.i18n.translate("UPLOAD_FAILED"),
+ body: errMsg,
+ })
+ throw errMsg
+ }
+ }
+ throw e
+ }
+ }
+ }
+ return ctx
+}
+
+const config = (ctx: IPicGo): IPluginConfig[] => {
+ const userConfig = ctx.getConfig("picBed.qiniu") || {}
+ const config: IPluginConfig[] = [
+ {
+ name: "accessKey",
+ type: "input",
+ get alias() {
+ return ctx.i18n.translate("PICBED_QINIU_ACCESSKEY")
+ },
+ default: userConfig.accessKey || "",
+ required: true,
+ },
+ {
+ name: "secretKey",
+ type: "password",
+ get alias() {
+ return ctx.i18n.translate("PICBED_QINIU_SECRETKEY")
+ },
+ default: userConfig.secretKey || "",
+ required: true,
+ },
+ {
+ name: "bucket",
+ type: "input",
+ get alias() {
+ return ctx.i18n.translate("PICBED_QINIU_BUCKET")
+ },
+ default: userConfig.bucket || "",
+ required: true,
+ },
+ {
+ name: "url",
+ type: "input",
+ get prefix() {
+ return ctx.i18n.translate("PICBED_QINIU_URL")
+ },
+ get alias() {
+ return ctx.i18n.translate("PICBED_QINIU_URL")
+ },
+ get message() {
+ return ctx.i18n.translate("PICBED_QINIU_MESSAGE_URL")
+ },
+ default: userConfig.url || "",
+ required: true,
+ },
+ {
+ name: "area",
+ type: "input",
+ get prefix() {
+ return ctx.i18n.translate("PICBED_QINIU_AREA")
+ },
+ get alias() {
+ return ctx.i18n.translate("PICBED_QINIU_AREA")
+ },
+ get message() {
+ return ctx.i18n.translate("PICBED_QINIU_MESSAGE_AREA")
+ },
+ default: userConfig.area || "",
+ required: true,
+ },
+ {
+ name: "options",
+ type: "input",
+ get prefix() {
+ return ctx.i18n.translate("PICBED_QINIU_OPTIONS")
+ },
+ get alias() {
+ return ctx.i18n.translate("PICBED_QINIU_OPTIONS")
+ },
+ get message() {
+ return ctx.i18n.translate("PICBED_QINIU_MESSAGE_OPTIONS")
+ },
+ default: userConfig.options || "",
+ required: false,
+ },
+ {
+ name: "path",
+ type: "input",
+ get prefix() {
+ return ctx.i18n.translate("PICBED_QINIU_PATH")
+ },
+ get alias() {
+ return ctx.i18n.translate("PICBED_QINIU_PATH")
+ },
+ get message() {
+ return ctx.i18n.translate("PICBED_QINIU_MESSAGE_PATH")
+ },
+ default: userConfig.path || "",
+ required: false,
+ },
+ ]
+ return config
+}
+
+export default function register(ctx: IPicGo): void {
+ ctx.helper.uploader.register("qiniu", {
+ get name() {
+ return ctx.i18n.translate("PICBED_QINIU")
+ },
+ handle,
+ config,
+ })
+}
diff --git a/libs/Universal-PicGo-Core/src/plugins/uploader/qiniu/rs.ts b/libs/Universal-PicGo-Core/src/plugins/uploader/qiniu/rs.ts
new file mode 100644
index 0000000..7ba2149
--- /dev/null
+++ b/libs/Universal-PicGo-Core/src/plugins/uploader/qiniu/rs.ts
@@ -0,0 +1,94 @@
+/*
+ * GNU GENERAL PUBLIC LICENSE
+ * Version 3, 29 June 2007
+ *
+ * Copyright (C) 2024 Terwer, Inc.
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+
+import { Mac } from "./digest"
+import { util } from "./util"
+
+// 用于与旧 SDK 版本兼容
+function _putPolicyBuildInKeys(): string[] {
+ return [
+ "scope",
+ "isPrefixalScope",
+ "insertOnly",
+ "saveKey",
+ "forceSaveKey",
+ "endUser",
+ "returnUrl",
+ "returnBody",
+ "callbackUrl",
+ "callbackHost",
+ "callbackBody",
+ "callbackBodyType",
+ "callbackFetchKey",
+ "persistentOps",
+ "persistentNotifyUrl",
+ "persistentPipeline",
+ "fsizeLimit",
+ "fsizeMin",
+ "detectMime",
+ "mimeLimit",
+ "deleteAfterDays",
+ "fileType",
+ ]
+}
+
+/**
+ * 上传策略
+ * @link https://developer.qiniu.com/kodo/manual/1206/put-policy
+ */
+class PutPolicy {
+ private readonly expires: number
+
+ constructor(options: any) {
+ if (typeof options !== "object") {
+ throw new Error("invalid putpolicy options")
+ }
+
+ const that = this as any
+ Object.keys(options).forEach((k) => {
+ if (k === "expires") {
+ return
+ }
+ that[k] = options[k]
+ })
+
+ this.expires = options.expires || 3600
+ _putPolicyBuildInKeys().forEach((k) => {
+ if ((this as any)[k] === undefined) {
+ that[k] = that[k] || null
+ }
+ })
+ }
+
+ getFlags(): any {
+ const that = this as any
+ const flags: any = {}
+
+ Object.keys(this).forEach((k) => {
+ if (k === "expires" || that[k] === null) {
+ return
+ }
+ flags[k] = that[k]
+ })
+
+ flags.deadline = this.expires + Math.floor(Date.now() / 1000)
+
+ return flags
+ }
+
+ uploadToken(mac: Mac): string {
+ const flags = this.getFlags()
+ const encodedFlags = util.urlsafeBase64Encode(JSON.stringify(flags))
+ const encoded = util.hmacSha1(encodedFlags, mac.secretKey)
+ const encodedSign = util.base64ToUrlSafe(encoded)
+ return [mac.accessKey, encodedSign, encodedFlags].join(":")
+ }
+}
+
+export { PutPolicy }
diff --git a/libs/Universal-PicGo-Core/src/plugins/uploader/qiniu/util.ts b/libs/Universal-PicGo-Core/src/plugins/uploader/qiniu/util.ts
new file mode 100644
index 0000000..5e9904b
--- /dev/null
+++ b/libs/Universal-PicGo-Core/src/plugins/uploader/qiniu/util.ts
@@ -0,0 +1,42 @@
+/*
+ * GNU GENERAL PUBLIC LICENSE
+ * Version 3, 29 June 2007
+ *
+ * Copyright (C) 2024 Terwer, Inc.
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+
+import { Buffer } from "../../../utils/nodePolyfill"
+import crypto from "crypto"
+
+const base64ToUrlSafe = function (v: string) {
+ return v.replace(/\//g, "_").replace(/\+/g, "-")
+}
+
+const urlSafeToBase64 = function (v: string) {
+ return v.replace(/_/g, "/").replace(/-/g, "+")
+}
+
+// UrlSafe Base64 Decode
+const urlsafeBase64Encode = function (jsonFlags: string) {
+ const encoded = Buffer.from(jsonFlags).toString("base64")
+ return base64ToUrlSafe(encoded)
+}
+
+// UrlSafe Base64 Decode
+const urlSafeBase64Decode = function (fromStr: string) {
+ return Buffer.from(urlSafeToBase64(fromStr), "base64").toString()
+}
+
+// Hmac-sha1 Crypt
+const hmacSha1 = (encodedFlags: string, secretKey: string) => {
+ // return value already encoded with base64
+ const hmac = crypto.createHmac("sha1", secretKey)
+ hmac.update(encodedFlags)
+ return hmac.digest("base64")
+}
+
+const util = { urlsafeBase64Encode, urlSafeBase64Decode, base64ToUrlSafe, urlSafeToBase64, hmacSha1 }
+
+export { util }
diff --git a/libs/Universal-PicGo-Core/vite.config.ts b/libs/Universal-PicGo-Core/vite.config.ts
index 28c77d7..86060dd 100644
--- a/libs/Universal-PicGo-Core/vite.config.ts
+++ b/libs/Universal-PicGo-Core/vite.config.ts
@@ -13,10 +13,6 @@ import fs from "fs"
const packageJson = fs.readFileSync("./package.json").toString()
const pkg = JSON.parse(packageJson) || {}
-const getAppBase = (): string => {
- return "/plugins/siyuan-plugin-picgo/"
-}
-
const getDefineEnv = (isDevMode: boolean) => {
const mode = process.env.NODE_ENV
const isTest = mode === "test"
@@ -25,8 +21,7 @@ const getDefineEnv = (isDevMode: boolean) => {
const defaultEnv = {
DEV_MODE: `${isDevMode || isTest}`,
- APP_BASE: `${appBase}`,
- NODE_ENV: "development",
+ NODE_ENV: isDevMode ? "development" : "production",
PICGO_VERSION: pkg.version,
}
const env = loadEnv(mode, process.cwd())
@@ -54,7 +49,6 @@ const isWatch = args.watch || args.w || false
const isDev = isServe || isWatch
const devDistDir = "./dist"
const distDir = isWatch ? devDistDir : "./dist"
-const appBase = getAppBase()
console.log("isWatch=>", isWatch)
console.log("distDir=>", distDir)
diff --git a/libs/zhi-siyuan-picgo/src/lib/picgoHelper.ts b/libs/zhi-siyuan-picgo/src/lib/picgoHelper.ts
index cebe7fb..519d362 100644
--- a/libs/zhi-siyuan-picgo/src/lib/picgoHelper.ts
+++ b/libs/zhi-siyuan-picgo/src/lib/picgoHelper.ts
@@ -111,7 +111,7 @@ class PicgoHelper {
}
})
.sort((a: any) => {
- if (a.type === "smms") {
+ if (a.type === "github") {
return -1
}
return 0