Skip to content

Commit

Permalink
Add support for TencentCloud COS
Browse files Browse the repository at this point in the history
Signed-off-by: Addo.Zhang <[email protected]>
  • Loading branch information
addozhang committed Sep 10, 2024
1 parent 54f6c8f commit 0572eae
Show file tree
Hide file tree
Showing 11 changed files with 162 additions and 17 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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).
Expand All @@ -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

Expand Down
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"authorUrl": "https://atbug.com",
"isDesktopOnly": true,
"minAppVersion": "0.11.0",
"version": "0.6.0"
"version": "0.8.0"
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "obsidian-image-upload-toolkit",
"version": "0.6.0",
"version": "0.8.0",
"description": "",
"author": "addozhang",
"main": "main.js",
Expand All @@ -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"
}
Expand Down
5 changes: 5 additions & 0 deletions src/imageStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
10 changes: 10 additions & 0 deletions src/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -25,6 +26,7 @@ export interface PublishSettings {
ossSetting: OssSetting;
imagekitSetting: ImagekitSetting;
awsS3Setting: AwsS3Setting;
cosSetting: CosSetting;
}

const DEFAULT_SETTINGS: PublishSettings = {
Expand Down Expand Up @@ -57,6 +59,14 @@ const DEFAULT_SETTINGS: PublishSettings = {
bucketName: "",
path: "",
customDomainName: "",
},
cosSetting: {
region: "",
bucket: "",
secretId: "",
secretKey: "",
path: "",
customDomainName: "",
}
};
export default class ObsidianPublish extends Plugin {
Expand Down
85 changes: 73 additions & 12 deletions src/ui/publishSettingTab.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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(
Expand All @@ -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 =>
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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))
}
}
19 changes: 19 additions & 0 deletions src/uploader/cos/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const TencentCloudRegionList: Record<string, string> = {
"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)",
}
44 changes: 44 additions & 0 deletions src/uploader/cos/cosUploader.ts
Original file line number Diff line number Diff line change
@@ -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<string> {
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;
}
3 changes: 3 additions & 0 deletions src/uploader/imageUploaderBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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!')
Expand Down
2 changes: 1 addition & 1 deletion src/uploader/oss/common.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const RegionList: Record<string, string> = {
export const AliYunRegionList: Record<string, string> = {
"oss-cn-hangzhou": "China (Hangzhou)",
"oss-cn-shanghai": "China (Shanghai)",
"oss-cn-nanjing": "China (Nanjing - Local Region)",
Expand Down
1 change: 0 additions & 1 deletion src/uploader/s3/awsS3Uploader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down

0 comments on commit 0572eae

Please sign in to comment.