diff --git a/libs/Universal-PicGo-Core/custom.d.ts b/libs/Universal-PicGo-Core/custom.d.ts index 1a74c95..8907bfc 100644 --- a/libs/Universal-PicGo-Core/custom.d.ts +++ b/libs/Universal-PicGo-Core/custom.d.ts @@ -1,4 +1,7 @@ declare module '*.json' { const value: { [key: string]: any } export default value -} \ No newline at end of file +} + +declare module "ali-oss" +declare module "arraybuffer-to-buffer" \ No newline at end of file diff --git a/libs/Universal-PicGo-Core/package.json b/libs/Universal-PicGo-Core/package.json index dc3c0a9..3589c5e 100644 --- a/libs/Universal-PicGo-Core/package.json +++ b/libs/Universal-PicGo-Core/package.json @@ -32,6 +32,7 @@ }, "dependencies": { "@picgo/i18n": "^1.0.0", + "ali-oss": "^6.20.0", "axios": "^1.6.8", "dayjs": "^1.11.10", "js-yaml": "^4.1.0", diff --git a/libs/Universal-PicGo-Core/src/plugins/uploader/aliyun-node.ts b/libs/Universal-PicGo-Core/src/plugins/uploader/aliyun-node.ts new file mode 100644 index 0000000..be99c11 --- /dev/null +++ b/libs/Universal-PicGo-Core/src/plugins/uploader/aliyun-node.ts @@ -0,0 +1,73 @@ +// noinspection ES6PreferShortImport + +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 => { + const aliYunOptions = ctx.getConfig("picBed.aliyun") + if (!aliYunOptions) { + throw new Error("Can't find aliYun OSS config") + } + + const store = new OSS({ + region: aliYunOptions.area, + accessKeyId: aliYunOptions.accessKeyId, + accessKeySecret: aliYunOptions.accessKeySecret, + bucket: aliYunOptions.bucket, + }) + + const imgList = ctx.output + const customUrl = aliYunOptions.customUrl + const path = aliYunOptions.path + + for (const img of imgList) { + if (img.fileName) { + let image = img.buffer + if (!image && img.base64Image) { + image = base64ToBuffer(img.base64Image) + } + if (!image) { + ctx.log.error("Can not find image buffer") + throw new Error("Can not find image buffer") + } + try { + const optionUrl = aliYunOptions.options || "" + const remotePath = `${path}${img.fileName}${optionUrl}` + + const result = await store.put(remotePath, new Blob([image])) + console.log("Using aliyun SDK for upload, result=>", result) + + if (result?.res?.status && result.res.status === 200) { + delete img.base64Image + delete img.buffer + if (customUrl) { + img.imgUrl = `${customUrl}/${path}${img.fileName}${optionUrl}` + } else { + img.imgUrl = `https://${aliYunOptions.bucket}.${aliYunOptions.area}.aliyuncs.com/${path}${img.fileName}${optionUrl}` + } + } else { + throw new Error("Upload failed") + } + } catch (e: any) { + let errMsg: any + if (e?.statusCode) { + errMsg = e.response?.body ?? e.stack ?? "unknown error" + } else { + errMsg = e.toString() + } + ctx.log.error(errMsg) + ctx.emit(IBuildInEvent.NOTIFICATION, { + title: ctx.i18n.translate("UPLOAD_FAILED"), + body: ctx.i18n.translate("CHECK_SETTINGS"), + }) + throw errMsg + } + } + } + return ctx +} + +export { handleNode } diff --git a/libs/Universal-PicGo-Core/src/plugins/uploader/aliyun-web.ts b/libs/Universal-PicGo-Core/src/plugins/uploader/aliyun-web.ts new file mode 100644 index 0000000..3d6b75c --- /dev/null +++ b/libs/Universal-PicGo-Core/src/plugins/uploader/aliyun-web.ts @@ -0,0 +1,100 @@ +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 { AxiosRequestConfig } from "axios" +import { base64ToBuffer, safeParse } from "../../utils/common" + +// generate OSS signature +const generateSignature = (options: IAliyunConfig, fileName: string): string => { + const date = new Date().toUTCString() + const mimeType = mime.lookup(fileName) + if (!mimeType) throw Error(`No mime type found for file ${fileName}`) + + const signString = `PUT\n\n${mimeType}\n${date}\n/${options.bucket}/${options.path}${fileName}` + + const signature = crypto.createHmac("sha1", options.accessKeySecret).update(signString).digest("base64") + return `OSS ${options.accessKeyId}:${signature}` +} + +const postOptions = ( + options: IAliyunConfig, + fileName: string, + signature: string, + image: Buffer +): AxiosRequestConfig => { + const xCorsHeaders = { + Host: `${options.bucket}.${options.area}.aliyuncs.com`, + Date: new Date().toUTCString(), + } + + return { + method: "PUT", + url: `https://${options.bucket}.${options.area}.aliyuncs.com/${encodeURI(options.path)}${encodeURI(fileName)}`, + headers: { + Authorization: signature, + "Content-Type": mime.lookup(fileName), + "x-cors-headers": JSON.stringify(xCorsHeaders), + }, + data: image, + resolveWithFullResponse: true, + } as AxiosRequestConfig +} + +const handleWeb = async (ctx: IPicGo): Promise => { + const aliYunOptions = ctx.getConfig("picBed.aliyun") + if (!aliYunOptions) { + throw new Error("Can't find aliYun OSS config") + } + + const imgList = ctx.output + const customUrl = aliYunOptions.customUrl + const path = aliYunOptions.path + for (const img of imgList) { + if (img.fileName) { + let image = img.buffer + if (!image && img.base64Image) { + image = base64ToBuffer(img.base64Image) + } + if (!image) { + ctx.log.error("Can not find image buffer") + throw new Error("Can not find image buffer") + } + try { + const signature = generateSignature(aliYunOptions, img.fileName) + const options = postOptions(aliYunOptions, img.fileName, signature, image) + const res: any = await ctx.request(options) + const body = safeParse(res) + if (body.statusCode === 200) { + delete img.base64Image + delete img.buffer + const optionUrl = aliYunOptions.options || "" + if (customUrl) { + img.imgUrl = `${customUrl}/${path}${img.fileName}${optionUrl}` + } else { + img.imgUrl = `https://${aliYunOptions.bucket}.${aliYunOptions.area}.aliyuncs.com/${path}${img.fileName}${optionUrl}` + } + } else { + throw new Error("Upload failed") + } + } catch (e: any) { + let errMsg: any + if (e?.statusCode) { + errMsg = e.response?.body ?? e.stack ?? "unknown error" + } else { + errMsg = e.toString() + } + ctx.log.error(errMsg) + ctx.emit(IBuildInEvent.NOTIFICATION, { + title: ctx.i18n.translate("UPLOAD_FAILED"), + body: ctx.i18n.translate("CHECK_SETTINGS"), + }) + throw errMsg + } + } + } + return ctx +} + +export { handleWeb } diff --git a/libs/Universal-PicGo-Core/src/plugins/uploader/aliyun.ts b/libs/Universal-PicGo-Core/src/plugins/uploader/aliyun.ts index 8d85491..c94796d 100644 --- a/libs/Universal-PicGo-Core/src/plugins/uploader/aliyun.ts +++ b/libs/Universal-PicGo-Core/src/plugins/uploader/aliyun.ts @@ -1,101 +1,14 @@ import { IAliyunConfig, IPicGo, IPluginConfig } from "../../types" -import crypto from "crypto" -import mime from "mime-types" -import { IBuildInEvent } from "../../utils/enums" import { ILocalesKey } from "../../i18n/zh-CN" -import { AxiosRequestConfig } from "axios" -import { base64ToBuffer, bufferToBase64, safeParse } from "../../utils/common" - -// generate OSS signature -const generateSignature = (options: IAliyunConfig, fileName: string): string => { - const date = new Date().toUTCString() - const mimeType = mime.lookup(fileName) - if (!mimeType) throw Error(`No mime type found for file ${fileName}`) - - const signString = `PUT\n\n${mimeType}\n${date}\n/${options.bucket}/${options.path}${fileName}` - - const signature = crypto.createHmac("sha1", options.accessKeySecret).update(signString).digest("base64") - return `OSS ${options.accessKeyId}:${signature}` -} - -const postOptions = ( - options: IAliyunConfig, - fileName: string, - signature: string, - image: Buffer -): AxiosRequestConfig => { - const xCorsHeaders = { - Host: `${options.bucket}.${options.area}.aliyuncs.com`, - Date: new Date().toUTCString(), - } - - return { - method: "PUT", - url: `https://${options.bucket}.${options.area}.aliyuncs.com/${encodeURI(options.path)}${encodeURI(fileName)}`, - headers: { - Authorization: signature, - "Content-Type": mime.lookup(fileName), - // "x-cors-headers": JSON.stringify(xCorsHeaders), - }, - data: image, - resolveWithFullResponse: true, - proxy: "must" as any, - } as AxiosRequestConfig -} +import { hasNodeEnv } from "universal-picgo-store" +import { handleNode } from "./aliyun-node" +import { handleWeb } from "./aliyun-web" const handle = async (ctx: IPicGo): Promise => { - const aliYunOptions = ctx.getConfig("picBed.aliyun") - if (!aliYunOptions) { - throw new Error("Can't find aliYun OSS config") + if (hasNodeEnv) { + return handleNode(ctx) } - - const imgList = ctx.output - const customUrl = aliYunOptions.customUrl - const path = aliYunOptions.path - for (const img of imgList) { - if (img.fileName) { - let image = img.buffer - if (!image && img.base64Image) { - image = base64ToBuffer(img.base64Image) - } - if (!image) { - ctx.log.error("Can not find image buffer") - throw new Error("Can not find image buffer") - } - try { - const signature = generateSignature(aliYunOptions, img.fileName) - const options = postOptions(aliYunOptions, img.fileName, signature, image) - const res: any = await ctx.request(options) - const body = safeParse(res) - if (body.statusCode === 200) { - delete img.base64Image - delete img.buffer - const optionUrl = aliYunOptions.options || "" - if (customUrl) { - img.imgUrl = `${customUrl}/${path}${img.fileName}${optionUrl}` - } else { - img.imgUrl = `https://${aliYunOptions.bucket}.${aliYunOptions.area}.aliyuncs.com/${path}${img.fileName}${optionUrl}` - } - } else { - throw new Error("Upload failed") - } - } catch (e: any) { - let errMsg: any - if (e?.statusCode) { - errMsg = e.response?.body ?? e.stack ?? "unknown error" - } else { - errMsg = e.toString() - } - ctx.log.error(errMsg) - ctx.emit(IBuildInEvent.NOTIFICATION, { - title: ctx.i18n.translate("UPLOAD_FAILED"), - body: ctx.i18n.translate("CHECK_SETTINGS"), - }) - throw errMsg - } - } - } - return ctx + return handleWeb(ctx) } const config = (ctx: IPicGo): IPluginConfig[] => { @@ -108,7 +21,7 @@ const config = (ctx: IPicGo): IPluginConfig[] => { return ctx.i18n.translate("PICBED_ALICLOUD_ACCESSKEYID") }, default: userConfig.accessKeyId || "", - required: true, + required: true }, { name: "accessKeySecret", @@ -117,7 +30,7 @@ const config = (ctx: IPicGo): IPluginConfig[] => { return ctx.i18n.translate("PICBED_ALICLOUD_ACCESSKEYSECRET") }, default: userConfig.accessKeySecret || "", - required: true, + required: true }, { name: "bucket", @@ -126,7 +39,7 @@ const config = (ctx: IPicGo): IPluginConfig[] => { return ctx.i18n.translate("PICBED_ALICLOUD_BUCKET") }, default: userConfig.bucket || "", - required: true, + required: true }, { name: "area", @@ -141,7 +54,7 @@ const config = (ctx: IPicGo): IPluginConfig[] => { get message() { return ctx.i18n.translate("PICBED_ALICLOUD_MESSAGE_AREA") }, - required: true, + required: true }, { name: "path", @@ -156,7 +69,7 @@ const config = (ctx: IPicGo): IPluginConfig[] => { return ctx.i18n.translate("PICBED_ALICLOUD_MESSAGE_PATH") }, default: userConfig.path || "", - required: false, + required: false }, { name: "customUrl", @@ -171,7 +84,7 @@ const config = (ctx: IPicGo): IPluginConfig[] => { return ctx.i18n.translate("PICBED_ALICLOUD_MESSAGE_CUSTOMURL") }, default: userConfig.customUrl || "", - required: false, + required: false }, { name: "options", @@ -186,8 +99,8 @@ const config = (ctx: IPicGo): IPluginConfig[] => { return ctx.i18n.translate("PICBED_ALICLOUD_MESSAGE_OPTIONS") }, default: userConfig.options || "", - required: false, - }, + required: false + } ] return config } @@ -198,6 +111,6 @@ export default function register(ctx: IPicGo): void { return ctx.i18n.translate("PICBED_ALICLOUD") }, handle, - config, + config }) } diff --git a/packages/picgo-plugin-app/src/components/common/ConfigForm.vue b/packages/picgo-plugin-app/src/components/common/ConfigForm.vue index a95b600..1b4703a 100644 --- a/packages/picgo-plugin-app/src/components/common/ConfigForm.vue +++ b/packages/picgo-plugin-app/src/components/common/ConfigForm.vue @@ -14,7 +14,7 @@ import { IPluginConfig } from "zhi-siyuan-picgo" import { useVueI18n } from "$composables/useVueI18n.ts" import { createAppLogger } from "@/utils/appLogger.ts" import _ from "lodash-es" -import { PicgoHelper } from "zhi-siyuan-picgo/src" +import { PicgoHelper } from "zhi-siyuan-picgo" const logger = createAppLogger("picbed-config-form") const { t } = useVueI18n() diff --git a/packages/picgo-plugin-app/src/components/home/controls/UrlCopy.vue b/packages/picgo-plugin-app/src/components/home/controls/UrlCopy.vue index e4e2ec7..1172ecc 100644 --- a/packages/picgo-plugin-app/src/components/home/controls/UrlCopy.vue +++ b/packages/picgo-plugin-app/src/components/home/controls/UrlCopy.vue @@ -10,7 +10,7 @@