From 0572eae4de0aac1dc3f3a13fae1ac4c243235659 Mon Sep 17 00:00:00 2001 From: "Addo.Zhang" Date: Tue, 10 Sep 2024 10:40:46 +0800 Subject: [PATCH] Add support for TencentCloud COS Signed-off-by: Addo.Zhang --- README.md | 4 +- manifest.json | 2 +- package.json | 4 +- src/imageStore.ts | 5 ++ src/publish.ts | 10 ++++ src/ui/publishSettingTab.ts | 85 ++++++++++++++++++++++++---- src/uploader/cos/common.ts | 19 +++++++ src/uploader/cos/cosUploader.ts | 44 ++++++++++++++ src/uploader/imageUploaderBuilder.ts | 3 + src/uploader/oss/common.ts | 2 +- src/uploader/s3/awsS3Uploader.ts | 1 - 11 files changed, 162 insertions(+), 17 deletions(-) create mode 100644 src/uploader/cos/common.ts create mode 100644 src/uploader/cos/cosUploader.ts diff --git a/README.md b/README.md index 88c37d3..322cb40 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Obsidian Image Upload Toolkit This plugin cloud upload all local images embedded in markdown to specified remote image store -(support [imgur](https://imgur.com),[AliYun OSS](https://www.alibabacloud.com/product/object-storage-service), [Imagekit](https://imagekit.io) and [AWS S3](https://aws.amazon.com/s3/) currently) and export markdown with image urls to clipboard directly. +(support [imgur](https://imgur.com),[AliYun OSS](https://www.alibabacloud.com/product/object-storage-service) and [Imagekit](https://imagekit.io), currently) and export markdown with image urls to clipboard directly. The origin markdown in vault is still using local images. It will be help for publishing to the static site such [GitHub pages](https://pages.github.com). @@ -25,9 +25,11 @@ and copy markdown with replaced image syntax to clipboard with notification. ## TODO - [ ] support uploading images to more storages + - [x] imageur - [x] Aliyun Oss - [x] ImageKit - [x] Amazon S3 + - [x] TencentCloud COS - [ ] more... - [x] setting for replacing images embedded in origin markdown directly diff --git a/manifest.json b/manifest.json index 1812959..3756afd 100644 --- a/manifest.json +++ b/manifest.json @@ -6,5 +6,5 @@ "authorUrl": "https://atbug.com", "isDesktopOnly": true, "minAppVersion": "0.11.0", - "version": "0.6.0" + "version": "0.8.0" } diff --git a/package.json b/package.json index 958fb43..6dc9c31 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-image-upload-toolkit", - "version": "0.6.0", + "version": "0.8.0", "description": "", "author": "addozhang", "main": "main.js", @@ -16,8 +16,10 @@ "typescript": "^4.0.3" }, "dependencies": { + "@octokit/rest": "^21.0.1", "ali-oss": "^6.17.1", "aws-sdk": "^2.1664.0", + "cos-nodejs-sdk-v5": "^2.14.6", "imagekit": "^5.0.0", "proxy-agent": "^5.0.0" } diff --git a/src/imageStore.ts b/src/imageStore.ts index 99f7532..b3df2ab 100644 --- a/src/imageStore.ts +++ b/src/imageStore.ts @@ -25,6 +25,11 @@ export default class ImageStore { "AWS S3" ) + static readonly TENCENTCLOUD_COS = new ImageStore( + "TENCENTCLOUD_COS", + "TencentCloud COS" + ) + private constructor(readonly id: string, readonly description: string) { ImageStore.values.push(this) } diff --git a/src/publish.ts b/src/publish.ts index 0882ffa..b1abbbf 100644 --- a/src/publish.ts +++ b/src/publish.ts @@ -13,6 +13,7 @@ import PublishSettingTab from "./ui/publishSettingTab"; import {OssSetting} from "./uploader/oss/ossUploader"; import {ImagekitSetting} from "./uploader/imagekit/imagekitUploader"; import {AwsS3Setting} from "./uploader/s3/awsS3Uploader"; +import {CosSetting} from "./uploader/cos/cosUploader"; export interface PublishSettings { imageAltText: boolean; @@ -25,6 +26,7 @@ export interface PublishSettings { ossSetting: OssSetting; imagekitSetting: ImagekitSetting; awsS3Setting: AwsS3Setting; + cosSetting: CosSetting; } const DEFAULT_SETTINGS: PublishSettings = { @@ -57,6 +59,14 @@ const DEFAULT_SETTINGS: PublishSettings = { bucketName: "", path: "", customDomainName: "", + }, + cosSetting: { + region: "", + bucket: "", + secretId: "", + secretKey: "", + path: "", + customDomainName: "", } }; export default class ObsidianPublish extends Plugin { diff --git a/src/ui/publishSettingTab.ts b/src/ui/publishSettingTab.ts index 5aab20a..5f4710f 100644 --- a/src/ui/publishSettingTab.ts +++ b/src/ui/publishSettingTab.ts @@ -1,7 +1,8 @@ import {App, Notice, PluginSettingTab, Setting} from "obsidian"; import ObsidianPublish from "../publish"; import ImageStore from "../imageStore"; -import {RegionList} from "../uploader/oss/common"; +import {AliYunRegionList} from "../uploader/oss/common"; +import {TencentCloudRegionList} from "../uploader/cos/common"; export default class PublishSettingTab extends PluginSettingTab { private plugin: ObsidianPublish; @@ -90,20 +91,23 @@ export default class PublishSettingTab extends PluginSettingTab { this.plugin.setupImageUploader(); } - private async drawImageStoreSettings(partentEL: HTMLDivElement) { - partentEL.empty(); + private async drawImageStoreSettings(parentEL: HTMLDivElement) { + parentEL.empty(); switch (this.plugin.settings.imageStore) { case ImageStore.IMGUR.id: - this.drawImgurSetting(partentEL); + this.drawImgurSetting(parentEL); break; case ImageStore.ALIYUN_OSS.id: - this.drawOSSSetting(partentEL); + this.drawOSSSetting(parentEL); break; case ImageStore.ImageKit.id: - this.drawImageKitSetting(partentEL); + this.drawImageKitSetting(parentEL); break; case ImageStore.AWS_S3.id: - this.drawAwsS3Setting(partentEL); + this.drawAwsS3Setting(parentEL); + break; + case ImageStore.TENCENTCLOUD_COS.id: + this.drawTencentCloudCosSetting(parentEL); break; default: throw new Error( @@ -113,8 +117,8 @@ export default class PublishSettingTab extends PluginSettingTab { } // Imgur Setting - private drawImgurSetting(partentEL: HTMLDivElement) { - new Setting(partentEL) + private drawImgurSetting(parentEL: HTMLDivElement) { + new Setting(parentEL) .setName("Client ID") .setDesc(PublishSettingTab.clientIdSettingDescription()) .addText(text => @@ -143,7 +147,7 @@ export default class PublishSettingTab extends PluginSettingTab { .setDesc("OSS data center region.") .addDropdown(dropdown => dropdown - .addOptions(RegionList) + .addOptions(AliYunRegionList) .setValue(this.plugin.settings.ossSetting.region) .onChange(value => { this.plugin.settings.ossSetting.region = value; @@ -285,7 +289,7 @@ export default class PublishSettingTab extends PluginSettingTab { .addText(text => text .setPlaceholder("Enter path") - .setValue(this.plugin.settings.ossSetting.path) + .setValue(this.plugin.settings.awsS3Setting.path) .onChange(value => this.plugin.settings.awsS3Setting.path = value)) //custom domain @@ -295,7 +299,64 @@ export default class PublishSettingTab extends PluginSettingTab { .addText(text => text .setPlaceholder("Enter path") - .setValue(this.plugin.settings.ossSetting.customDomainName) + .setValue(this.plugin.settings.awsS3Setting.customDomainName) .onChange(value => this.plugin.settings.awsS3Setting.customDomainName = value)) } + + private drawTencentCloudCosSetting(parentEL: HTMLDivElement) { + new Setting(parentEL) + .setName("Region") + .setDesc("COS data center region.") + .addDropdown(dropdown => + dropdown + .addOptions(TencentCloudRegionList) + .setValue(this.plugin.settings.cosSetting.region) + .onChange(value => { + this.plugin.settings.cosSetting.region = value; + }) + ) + new Setting(parentEL) + .setName("Secret Id") + .setDesc("The secret id of TencentCloud.") + .addText(text => + text + .setPlaceholder("Enter access key id") + .setValue(this.plugin.settings.cosSetting.secretId) + .onChange(value => this.plugin.settings.cosSetting.secretId = value)) + new Setting(parentEL) + .setName("Secret Key") + .setDesc("The secret key of TencentCloud.") + .addText(text => + text + .setPlaceholder("Enter access key secret") + .setValue(this.plugin.settings.cosSetting.secretKey) + .onChange(value => this.plugin.settings.cosSetting.secretKey = value)) + new Setting(parentEL) + .setName("Access Bucket Name") + .setDesc("The name of bucket to store images.") + .addText(text => + text + .setPlaceholder("Enter bucket name") + .setValue(this.plugin.settings.cosSetting.bucket) + .onChange(value => this.plugin.settings.cosSetting.bucket = value)) + + new Setting(parentEL) + .setName("Target Path") + .setDesc("The path to store image.\nSupport {year} {mon} {day} {random} {filename} vars. For example, /{year}/{mon}/{day}/{filename} with uploading pic.jpg, it will store as /2023/06/08/pic.jpg.") + .addText(text => + text + .setPlaceholder("Enter path") + .setValue(this.plugin.settings.cosSetting.path) + .onChange(value => this.plugin.settings.cosSetting.path = value)) + + //custom domain + new Setting(parentEL) + .setName("Custom Domain Name") + .setDesc("If the custom domain name is example.com, you can use https://example.com/pic.jpg to access pic.img.") + .addText(text => + text + .setPlaceholder("Enter path") + .setValue(this.plugin.settings.cosSetting.customDomainName) + .onChange(value => this.plugin.settings.cosSetting.customDomainName = value)) + } } \ No newline at end of file diff --git a/src/uploader/cos/common.ts b/src/uploader/cos/common.ts new file mode 100644 index 0000000..16ec0b0 --- /dev/null +++ b/src/uploader/cos/common.ts @@ -0,0 +1,19 @@ +export const TencentCloudRegionList: Record = { + "ap-beijing": "China (Beijing)", + "ap-nanjing": "China (Nanjing)", + "ap-shanghai": "China (Shanghai)", + "ap-guangzhou": "China (Guangzhou)", + "ap-chengdu": "China (Chengdu)", + "ap-chongqing": "China (Chongqing)", + "ap-hongkong": "China (Hong Kong)", + "ap-singapore": "Singapore", + "ap-mumbai": "Mumbai", + "ap-jakarta": "Jakarta", + "ap-seoul": "Seoul", + "ap-bangkok": "Bangkok", + "ap-tokyo": "Tokyo", + "na-siliconvalley": "North America (Silicon Valley - West US)", + "na-ashburn": "North America (Virginia - East US)", + "sa-saopaulo": "South America (São Paulo)", + "eu-frankfurt": "Europe (Frankfurt)", +} \ No newline at end of file diff --git a/src/uploader/cos/cosUploader.ts b/src/uploader/cos/cosUploader.ts new file mode 100644 index 0000000..73baa76 --- /dev/null +++ b/src/uploader/cos/cosUploader.ts @@ -0,0 +1,44 @@ +import COS from "cos-nodejs-sdk-v5" +import ImageUploader from "../imageUploader"; +import {UploaderUtils} from "../uploaderUtils"; + +export default class CosUploader implements ImageUploader { + + private readonly client!: COS; + private readonly pathTmpl: String; + private readonly customDomainName: String; + private readonly region: string; + private readonly bucket: string; + + constructor(setting: CosSetting) { + this.client = new COS({ + SecretId: setting.secretId, + SecretKey: setting.secretKey, + }) + this.pathTmpl = setting.path; + this.customDomainName = setting.customDomainName; + this.bucket = setting.bucket; + this.region = setting.region; + } + + async upload(image: File, fullPath: string): Promise { + const result = this.client.putObject({ + Body: Buffer.from((await image.arrayBuffer())), + Bucket: this.bucket, + Region: this.region, + Key: UploaderUtils.generateName(this.pathTmpl, image.name), + }); + var url = 'https://' + (await result).Location; + return UploaderUtils.customizeDomainName(url, this.customDomainName); + } + +} + +export interface CosSetting { + region: string; + bucket: string; + secretId: string; + secretKey: string; + path: string; + customDomainName: string; +} \ No newline at end of file diff --git a/src/uploader/imageUploaderBuilder.ts b/src/uploader/imageUploaderBuilder.ts index 775b61b..2887d6e 100644 --- a/src/uploader/imageUploaderBuilder.ts +++ b/src/uploader/imageUploaderBuilder.ts @@ -5,6 +5,7 @@ import ImgurAnonymousUploader from "./imgur/imgurAnonymousUploader"; import OssUploader from "./oss/ossUploader"; import ImagekitUploader from "./imagekit/imagekitUploader"; import AwsS3Uploader from "./s3/awsS3Uploader"; +import CosUploader from "./cos/cosUploader"; export default function buildUploader(settings: PublishSettings): ImageUploader { switch (settings.imageStore) { @@ -16,6 +17,8 @@ export default function buildUploader(settings: PublishSettings): ImageUploader return new ImagekitUploader(settings.imagekitSetting); case ImageStore.AWS_S3.id: return new AwsS3Uploader(settings.awsS3Setting); + case ImageStore.TENCENTCLOUD_COS.id: + return new CosUploader(settings.cosSetting); //todo more cases default: throw new Error('should not reach here!') diff --git a/src/uploader/oss/common.ts b/src/uploader/oss/common.ts index bddbc89..6841b70 100644 --- a/src/uploader/oss/common.ts +++ b/src/uploader/oss/common.ts @@ -1,4 +1,4 @@ -export const RegionList: Record = { +export const AliYunRegionList: Record = { "oss-cn-hangzhou": "China (Hangzhou)", "oss-cn-shanghai": "China (Shanghai)", "oss-cn-nanjing": "China (Nanjing - Local Region)", diff --git a/src/uploader/s3/awsS3Uploader.ts b/src/uploader/s3/awsS3Uploader.ts index e7ea853..58d4a0a 100644 --- a/src/uploader/s3/awsS3Uploader.ts +++ b/src/uploader/s3/awsS3Uploader.ts @@ -33,7 +33,6 @@ export default class AwsS3Uploader implements ImageUploader { return new Promise((resolve, reject) => { this.s3.upload(params, (err, data) => { if (err) { - console.log(err) reject(err); } else { resolve(UploaderUtils.customizeDomainName(data.Location, this.customDomainName));