Skip to content

Commit

Permalink
✨ Feature(custom): add buildin alist
Browse files Browse the repository at this point in the history
  • Loading branch information
Kuingsmile committed Jul 2, 2024
1 parent 7b64fd8 commit c7f5659
Show file tree
Hide file tree
Showing 6 changed files with 348 additions and 0 deletions.
17 changes: 17 additions & 0 deletions src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,23 @@ export const EN: ILocales = {
PICBED_LOCAL_WEBPATH: 'Set Web Path',
PICBED_LOCAL_MESSAGE_WEBPATH: 'Used to generate url path',

// alist
PICBED_ALIST_PLIST: 'AList',
PICBED_ALIST_URL: 'Set URL',
PICBED_ALIST_MESSAGE_URL: 'Ex. https://alist.example.com',
PICBED_ALIST_TOKEN: 'Set Token',
PICBED_ALIST_MESSAGE_TOKEN: 'Please enter the token(choose one of either username-password or token)',
PICBED_ALIST_USERNAME: 'Set Username',
PICBED_ALIST_MESSAGE_USERNAME: 'Please enter the username(choose one of either username-password or token)',
PICBED_ALIST_PASSWORD: 'Set Password',
PICBED_ALIST_MESSAGE_PASSWORD: 'Please enter the password',
PICBED_ALIST_UPLOAD_PATH: 'Set Upload Path',
PICBED_ALIST_MESSAGE_UPLOAD_PATH: 'Ex. /test/',
PICBED_ALIST_WEB_PATH: 'Set Web Path',
PICBED_ALIST_MESSAGE_WEB_PATH: 'Used to generate url path',
PICBED_ALIST_CUSTOMURL: 'Set Custom URL',
PICBED_ALIST_MESSAGE_CUSTOMURL: 'Ex. https://test.com',

// Ali-cloud
PICBED_ALICLOUD: 'Ali Cloud',
PICBED_ALICLOUD_ACCESSKEYID: 'Set accessKeyId',
Expand Down
17 changes: 17 additions & 0 deletions src/i18n/zh-CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,23 @@ export const ZH_CN = {
PICBED_LOCAL_WEBPATH: '设定网站路径',
PICBED_LOCAL_MESSAGE_WEBPATH: '用于拼接网址路径',

// alist
PICBED_ALIST_PLIST: 'AList',
PICBED_ALIST_URL: '设定URL',
PICBED_ALIST_MESSAGE_URL: '例如:https://alist.example.com',
PICBED_ALIST_TOKEN: '设定Token',
PICBED_ALIST_MESSAGE_TOKEN: '请输入Token(与用户名-密码二选一)',
PICBED_ALIST_USERNAME: '设定用户名',
PICBED_ALIST_MESSAGE_USERNAME: '请输入用户名(与Token二选一)',
PICBED_ALIST_PASSWORD: '设定密码',
PICBED_ALIST_MESSAGE_PASSWORD: '请输入密码',
PICBED_ALIST_UPLOAD_PATH: '设定上传路径',
PICBED_ALIST_MESSAGE_UPLOAD_PATH: '例如:/test',
PICBED_ALIST_WEB_PATH: '设定网站路径',
PICBED_ALIST_MESSAGE_WEB_PATH: '用于拼接网址路径',
PICBED_ALIST_CUSTOMURL: '设定自定义域名',
PICBED_ALIST_MESSAGE_CUSTOMURL: '例如:https://test.com',

// Ali-cloud
PICBED_ALICLOUD: '阿里云OSS',
PICBED_ALICLOUD_ACCESSKEYID: '设定accessKeyId',
Expand Down
17 changes: 17 additions & 0 deletions src/i18n/zh-TW.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,23 @@ export const ZH_TW: ILocales = {
PICBED_LOCAL_WEBPATH: '設定網址路徑',
PICBED_LOCAL_MESSAGE_WEBPATH: '用于網頁顯示的路徑',

// alist
PICBED_ALIST_PLIST: 'AList',
PICBED_ALIST_URL: '設定URL',
PICBED_ALIST_MESSAGE_URL: '例如:https://alist.example.com',
PICBED_ALIST_TOKEN: '設定Token',
PICBED_ALIST_MESSAGE_TOKEN: '請輸入Token(與用戶名-密碼二選一)',
PICBED_ALIST_USERNAME: '設定用戶名',
PICBED_ALIST_MESSAGE_USERNAME: '請輸入用戶名(與Token二選一)',
PICBED_ALIST_PASSWORD: '設定密碼',
PICBED_ALIST_MESSAGE_PASSWORD: '請輸入密碼',
PICBED_ALIST_UPLOAD_PATH: '設定上傳路徑',
PICBED_ALIST_MESSAGE_UPLOAD_PATH: '例如:/test',
PICBED_ALIST_WEB_PATH: '設定網址路徑',
PICBED_ALIST_MESSAGE_WEB_PATH: '用于網頁顯示的路徑',
PICBED_ALIST_CUSTOMURL: '設定自訂網址',
PICBED_ALIST_MESSAGE_CUSTOMURL: '例如:https://test.com',

// Ali-cloud
PICBED_ALICLOUD: '阿里云OSS',
PICBED_ALICLOUD_ACCESSKEYID: '設定accessKeyId',
Expand Down
285 changes: 285 additions & 0 deletions src/plugins/uploader/alist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
import axios from 'axios'
import { IPicGo, IPluginConfig, IAlistConfig, IOldReqOptions, IFullResponse } from '../../types'
import { IBuildInEvent } from '../../utils/enum'
import { ILocalesKey } from '../../i18n/zh-CN'
import { encodePath, formatPathHelper } from './utils'
import path from 'path'

interface IAlistTokenStore {
token: string
refreshedAt: number
}

const getAlistToken = async (ctx: IPicGo, url: string, username: string, password: string): Promise<string> => {
const tokenStore = ctx.getConfig<IAlistTokenStore>('picgo-plugin-buildin-alistplist')
if (tokenStore && tokenStore.refreshedAt && Date.now() - tokenStore.refreshedAt < 3600000 && tokenStore.token) {
return tokenStore.token
}
const res = await axios.post(`${url}/api/auth/login`, {
username,
password
})
if (res.data.code === 200 && res.data.message === 'success') {
const token = res.data.data.token
ctx.saveConfig({
'picgo-plugin-buildin-alistplist': {
token,
refreshedAt: Date.now()
}
})
return token
}
throw new Error('Get token failed')
}

const postOptions = (url: string, token: string, fileName: string, filePath: string, image: Buffer): IOldReqOptions => {
return {
method: 'PUT',
url: `${url}/api/fs/form`,
headers: {
contentType: 'multipart/form-data',
'User-Agent': 'PicList',
Authorization: token,
'File-Path': encodeURIComponent(filePath)
},
formData: {
file: {
value: image,
options: {
filename: fileName
}
}
},
resolveWithFullResponse: true
}
}

const handleResError = (ctx: IPicGo, res: IFullResponse): void => {
if (res.statusCode !== 200 || res.body.code !== 200 || res.body.message !== 'success') {
ctx.emit(IBuildInEvent.NOTIFICATION, {
title: ctx.i18n.translate<ILocalesKey>('UPLOAD_FAILED'),
body: ctx.i18n.translate<ILocalesKey>('CHECK_SETTINGS_AND_NETWORK'),
text: res.body.message
})
throw new Error(res.body.message)
}
}

const extractConfig = (config: IAlistConfig) => {
const { url, token, username, password, uploadPath, webPath, customUrl } = config
return {
url: (url || '').replace(/\/$/, ''),
token: token || '',
username: username || '',
password: password || '',
uploadPath: formatPathHelper({
path: uploadPath || '',
startSlash: true,
endSlash: true,
rootToEmpty: false
}),
webPath: webPath
? formatPathHelper({
path: webPath || '',
startSlash: true,
endSlash: true,
rootToEmpty: false
})
: '',
customUrl: (customUrl || '').replace(/\/$/, '')
}
}

const handle = async (ctx: IPicGo): Promise<IPicGo> => {
const alistConfig = ctx.getConfig<IAlistConfig>('picBed.alistplist')
if (!alistConfig) throw new Error('Can not find alist config!')
const { url, username, password, uploadPath, webPath, customUrl } = extractConfig(alistConfig)
let { token } = extractConfig(alistConfig)
if (!token) {
token = await getAlistToken(ctx, url, username, password)
}
if (!url || !(token || (username && password))) throw new Error('Please check your alist config!')
const imgList = ctx.output
for (const img of imgList) {
if (img.fileName && img.buffer) {
let image = img.buffer
if (!image && img.base64Image) {
image = Buffer.from(img.base64Image, 'base64')
}
const fullUploadPath = `${uploadPath}${img.fileName}`
const postConfig = postOptions(url, token, img.fileName, fullUploadPath, image)
try {
const uploadRes = (await ctx.request(postConfig)) as unknown as IFullResponse
handleResError(ctx, uploadRes)
const refreshUrl = `${url}/api/fs/list`
const getInfoUrl = `${url}/api/fs/get`
const refreshRes = (await ctx.request({
method: 'POST',
url: refreshUrl,
headers: {
Authorization: token,
'Content-Type': 'application/json'
},
body: {
password: '',
page: 1,
per_page: 1,
refresh: true,
path: path.dirname(fullUploadPath)
},
resolveWithFullResponse: true
})) as unknown as IFullResponse
handleResError(ctx, refreshRes)
const getInfoRes = (await ctx.request({
method: 'POST',
url: getInfoUrl,
headers: {
Authorization: token,
'Content-Type': 'application/json'
},
body: {
password: '',
path: fullUploadPath,
page: 1,
per_page: 1,
refresh: true
},
resolveWithFullResponse: true
})) as unknown as IFullResponse
handleResError(ctx, getInfoRes)
const sign = getInfoRes.body.data.sign
const encodedPath = encodePath(`${webPath || uploadPath}${img.fileName}`)
img.imgUrl = `${customUrl || url}/d${encodedPath}`
img.imgUrl += sign ? `?sign=${sign}` : ''
delete img.base64Image
delete img.buffer
} catch (e: any) {
ctx.log.error(e)
throw e
}
}
}
return ctx
}

const config = (ctx: IPicGo): IPluginConfig[] => {
const userConfig = ctx.getConfig<IAlistConfig>('picBed.alistplist') || {}
const config: IPluginConfig[] = [
{
name: 'url',
type: 'input',
get prefix() {
return ctx.i18n.translate<ILocalesKey>('PICBED_ALIST_URL')
},
get alias() {
return ctx.i18n.translate<ILocalesKey>('PICBED_ALIST_URL')
},
get message() {
return ctx.i18n.translate<ILocalesKey>('PICBED_ALIST_MESSAGE_URL')
},
default: userConfig.url || '',
required: true
},
{
name: 'token',
type: 'input',
get prefix() {
return ctx.i18n.translate<ILocalesKey>('PICBED_ALIST_TOKEN')
},
get alias() {
return ctx.i18n.translate<ILocalesKey>('PICBED_ALIST_TOKEN')
},
get message() {
return ctx.i18n.translate<ILocalesKey>('PICBED_ALIST_MESSAGE_TOKEN')
},
default: userConfig.token || '',
required: false
},
{
name: 'username',
type: 'input',
get prefix() {
return ctx.i18n.translate<ILocalesKey>('PICBED_ALIST_USERNAME')
},
get alias() {
return ctx.i18n.translate<ILocalesKey>('PICBED_ALIST_USERNAME')
},
get message() {
return ctx.i18n.translate<ILocalesKey>('PICBED_ALIST_MESSAGE_USERNAME')
},
default: userConfig.username || '',
required: false
},
{
name: 'password',
type: 'input',
get prefix() {
return ctx.i18n.translate<ILocalesKey>('PICBED_ALIST_PASSWORD')
},
get alias() {
return ctx.i18n.translate<ILocalesKey>('PICBED_ALIST_PASSWORD')
},
get message() {
return ctx.i18n.translate<ILocalesKey>('PICBED_ALIST_MESSAGE_PASSWORD')
},
default: userConfig.password || '',
required: false
},
{
name: 'uploadPath',
type: 'input',
get prefix() {
return ctx.i18n.translate<ILocalesKey>('PICBED_ALIST_UPLOAD_PATH')
},
get alias() {
return ctx.i18n.translate<ILocalesKey>('PICBED_ALIST_UPLOAD_PATH')
},
get message() {
return ctx.i18n.translate<ILocalesKey>('PICBED_ALIST_MESSAGE_UPLOAD_PATH')
},
default: userConfig.uploadPath || '',
required: false
},
{
name: 'webPath',
type: 'input',
get prefix() {
return ctx.i18n.translate<ILocalesKey>('PICBED_ALIST_WEB_PATH')
},
get alias() {
return ctx.i18n.translate<ILocalesKey>('PICBED_ALIST_WEB_PATH')
},
get message() {
return ctx.i18n.translate<ILocalesKey>('PICBED_ALIST_MESSAGE_WEB_PATH')
},
default: userConfig.webPath || '',
required: false
},
{
name: 'customUrl',
type: 'input',
get prefix() {
return ctx.i18n.translate<ILocalesKey>('PICBED_ALIST_CUSTOMURL')
},
get alias() {
return ctx.i18n.translate<ILocalesKey>('PICBED_ALIST_CUSTOMURL')
},
get message() {
return ctx.i18n.translate<ILocalesKey>('PICBED_ALIST_MESSAGE_CUSTOMURL')
},
default: userConfig.customUrl || '',
required: false
}
]
return config
}

export default function register(ctx: IPicGo): void {
ctx.helper.uploader.register('alistplist', {
get name() {
return ctx.i18n.translate<ILocalesKey>('PICBED_ALIST_PLIST')
},
handle,
config
})
}
2 changes: 2 additions & 0 deletions src/plugins/uploader/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import telegraphUploader from './telegraph'
import piclistUploader from './piclist'
import lskyUploader from './lsky'
import awss3plistUploader from './awss3plist'
import alistUploader from './alist'

const buildInUploaders: IPicGoPlugin = () => {
return {
register(ctx: IPicGo) {
alistUploader(ctx)
aliYunUploader(ctx)
awss3plistUploader(ctx)
githubUploader(ctx)
Expand Down
10 changes: 10 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,16 @@ export interface ISmmsConfig {
token: string
backupDomain?: string
}
/** 内置alist 图床配置项 */
export interface IAlistConfig {
url: string
token?: string
username?: string
password?: string
uploadPath?: string
webPath?: string
customUrl?: string
}
/** 本地图床配置项 */
export interface ILocalConfig {
path: string
Expand Down

0 comments on commit c7f5659

Please sign in to comment.