Feature: add custom short url server

#69

ISSUES CLOSED: #69
This commit is contained in:
萌萌哒赫萝 2023-07-11 20:07:33 -07:00
parent 3b3c70a53f
commit d4a22f9e63
16 changed files with 167 additions and 38 deletions

View File

@ -66,7 +66,7 @@
"mime-types": "^2.1.35",
"mitt": "^3.0.0",
"nodejs-file-downloader": "^4.12.1",
"piclist": "^0.7.3",
"piclist": "^0.7.4",
"pinia": "^2.1.4",
"pinia-plugin-persistedstate": "^3.1.0",
"qiniu": "^7.8.0",

View File

@ -227,6 +227,9 @@ SETTINGS_ISHIDEDOCK_TIPS: Not support hide dock and tray at the same time
SETTINGS_ENCODE_OUTPUT_URL: Encode Output(or Copyed) URL
SETTINGS_WATCH_CLIPBOARD: Watch clipboard when software start
SETTINGS_SHORT_URL: Use short url
SETTINGS_SHORT_URL_SERVER: Short url server
SETTINGS_SHORT_URL_YOURLS_DOMAIN: YOURLS domain
SETTINGS_SHORT_URL_YOURLS_SIGNATURE: YOURLS signature
SETTINGS_DELETE_LOCAL_FILE_AFTER_UPLOAD: Delete local file after upload
SETTINGS_SYNC_CONFIG: Settings Sync Configuration
SETTINGS_SYNC_CONFIG_TITLE: Sync Settings

View File

@ -230,6 +230,9 @@ SETTINGS_ISHIDEDOCK_TIPS: 不支持同时隐藏dock和托盘
SETTINGS_ENCODE_OUTPUT_URL: 输出(复制) URL 时进行转义
SETTINGS_WATCH_CLIPBOARD: 软件启动时自动监听剪贴板上传
SETTINGS_SHORT_URL: 使用短链接
SETTINGS_SHORT_URL_SERVER: 短链接服务
SETTINGS_SHORT_URL_YOURLS_DOMAIN: YOURLS域名
SETTINGS_SHORT_URL_YOURLS_SIGNATURE: YOURLS signature
SETTINGS_DELETE_LOCAL_FILE_AFTER_UPLOAD: 上传后删除本地文件
SETTINGS_SYNC_CONFIG: 设置配置同步
SETTINGS_SYNC_CONFIG_TITLE: 同步设置

View File

@ -228,6 +228,9 @@ SETTINGS_ISHIDEDOCK_TIPS: 不支持同時隱藏dock和托盘
SETTINGS_ENCODE_OUTPUT_URL: 輸出(複製) URL 時進行轉義
SETTINGS_WATCH_CLIPBOARD: 軟體啟動時自動監聽剪貼簿上傳
SETTINGS_SHORT_URL: 使用短網址
SETTINGS_SHORT_URL_SERVER: 短網址服務
SETTINGS_SHORT_URL_YOURLS_DOMAIN: YOURLS域名
SETTINGS_SHORT_URL_YOURLS_SIGNATURE: YOURLS signature
SETTINGS_DELETE_LOCAL_FILE_AFTER_UPLOAD: 上傳後刪除本地檔案
SETTINGS_SYNC_CONFIG: 設置同步配置
SETTINGS_SYNC_CONFIG_TITLE: 同步設置

View File

@ -39,11 +39,10 @@ import {
uploadChoosedFiles
} from '~/main/apis/app/uploader/apis'
import picgoCoreIPC from './picgoCoreIPC'
import { handleCopyUrl } from '~/main/utils/common'
import { handleCopyUrl, generateShortUrl } from '~/main/utils/common'
import { buildMainPageMenu, buildMiniPageMenu, buildPluginPageMenu, buildPicBedListMenu } from './remotes/menu'
import path from 'path'
import { T } from '~/main/i18n'
import { generateShortUrl } from '~/universal/utils/common'
import { uploadFile, downloadFile } from '../utils/syncSettings'
const STORE_PATH = app.getPath('userData')
@ -76,7 +75,6 @@ export default {
})
ipcMain.on('uploadClipboardFilesFromUploadPage', () => {
console.log('handle')
uploadClipboardFiles()
})

View File

@ -2,6 +2,10 @@ import fs from 'fs-extra'
import db from '~/main/apis/core/datastore'
import { clipboard, Notification, dialog } from 'electron'
import { handleUrlEncode } from '~/universal/utils/common'
import axios from 'axios'
import FormData from 'form-data'
import { C1 } from '#/utils/static'
import logger from '../apis/core/picgo/logger'
export const handleCopyUrl = (str: string): void => {
if (db.get('settings.autoCopy') !== false) {
@ -96,3 +100,45 @@ export const getClipboardFilePath = (): string => {
}
export const handleUrlEncodeWithSetting = (url: string) => db.get('settings.encodeOutputURL') ? handleUrlEncode(url) : url
const c1nApi = 'https://c1n.cn/link/short'
export const generateShortUrl = async (url: string) => {
const server = db.get('settings.shortUrlServer') || 'c1n'
if (server === 'c1n') {
const form = new FormData()
form.append('url', url)
const C = Buffer.from(C1, 'base64').toString()
try {
const res = await axios.post(c1nApi, form, {
headers: {
token: C
}
})
if (res.status >= 200 && res.status < 300 && res.data?.code === 0) {
return res.data.data
}
} catch (e: any) {
console.log(e)
}
} else if (server === 'yourls') {
const domain = db.get('settings.yourlsDomain') || ''
const signature = db.get('settings.yourlsSignature') || ''
if (domain && signature) {
try {
const res = await axios.get(`${domain}/yourls-api.php?signature=${signature}&action=shorturl&format=json&url=${url}`)
if (res.data.shorturl) {
return res.data.shorturl
}
} catch (e: any) {
if (e.response.data.message.indexOf('already exists in database') !== -1) {
return e.response.data.shorturl
}
console.log(e)
}
} else {
logger.warn('Yourls server or signature is not set')
}
}
return url
}

View File

@ -1,5 +1,5 @@
import { IPasteStyle } from '#/types/enum'
import { generateShortUrl } from '#/utils/common'
import { generateShortUrl } from '~/main/utils/common'
import db from '~/main/apis/core/datastore'
import { handleUrlEncodeWithSetting } from './common'

View File

@ -7,6 +7,7 @@ import GithubApi from './github'
import UpyunApi from './upyun'
import AwsS3Api from './awss3'
import WebdavApi from './webdav'
import LocalApi from './local'
const apiMap: IStringKeyMap = {
smms: SmmsApi,
@ -17,7 +18,8 @@ const apiMap: IStringKeyMap = {
github: GithubApi,
upyun: UpyunApi,
'aws-s3': AwsS3Api,
webdavplist: WebdavApi
webdavplist: WebdavApi,
local: LocalApi
}
export default class ALLApi {

View File

@ -0,0 +1,23 @@
import fs from 'fs-extra'
interface IConfigMap {
hash: string
}
export default class LocalApi {
static async delete (configMap: IConfigMap): Promise<boolean> {
const { hash } = configMap
if (!hash) {
console.error('SmmsApi.delete: invalid params')
return false
}
try {
await fs.remove(hash)
return true
} catch (error) {
console.error(error)
return false
}
}
}

View File

@ -3663,7 +3663,6 @@ const columns: Column<any>[] = [
]
onBeforeMount(async () => {
console.log('onBeforeMount')
await manageStore.refreshConfig()
showLoadingPage.value = true
await initCustomUrlList()

View File

@ -661,7 +661,7 @@ function remove (item: ImgInfo) {
type: 'warning'
}).then(async () => {
const file = await $$db.getById(item.id!)
const picBedsCanbeDeleted = ['smms', 'github', 'imgur', 'tcyun', 'aliyun', 'qiniu', 'upyun', 'aws-s3', 'webdavplist']
const picBedsCanbeDeleted = ['smms', 'github', 'imgur', 'tcyun', 'aliyun', 'qiniu', 'upyun', 'aws-s3', 'webdavplist', 'local']
if (await getConfig('settings.deleteCloudFile')) {
if (item.type !== undefined && picBedsCanbeDeleted.includes(item.type)) {
const result = await ALLApi.delete(item)
@ -771,7 +771,7 @@ function multiRemove () {
const files: IResult<ImgInfo>[] = []
const imageIDList = Object.keys(choosedList)
const isDeleteCloudFile = await getConfig('settings.deleteCloudFile')
const picBedsCanbeDeleted = ['smms', 'github', 'imgur', 'tcyun', 'aliyun', 'qiniu', 'upyun', 'aws-s3', 'webdavplist']
const picBedsCanbeDeleted = ['smms', 'github', 'imgur', 'tcyun', 'aliyun', 'qiniu', 'upyun', 'aws-s3', 'webdavplist', 'local']
if (isDeleteCloudFile) {
for (let i = 0; i < imageIDList.length; i++) {
const key = imageIDList[i]

View File

@ -373,6 +373,49 @@
@change="handleUseShortUrl"
/>
</el-form-item>
<el-form-item
v-if="form.useShortUrl"
:label="$T('SETTINGS_SHORT_URL_SERVER')"
>
<el-select
v-model="form.shortUrlServer"
size="small"
style="width: 50%"
:placeholder="$T('SETTINGS_SHORT_URL_SERVER')"
@change="handleShortUrlServerChange"
>
<el-option
v-for="item in shortUrlServerList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="form.useShortUrl && form.shortUrlServer === 'yourls'"
:label="$T('SETTINGS_SHORT_URL_YOURLS_DOMAIN')"
>
<el-input
v-model="form.yourlsDomain"
size="small"
style="width: 50%"
:placeholder="$T('SETTINGS_SHORT_URL_YOURLS_DOMAIN')"
@change="handleYourlsDomainChange"
/>
</el-form-item>
<el-form-item
v-if="form.useShortUrl && form.shortUrlServer === 'yourls'"
:label="$T('SETTINGS_SHORT_URL_YOURLS_SIGNATURE')"
>
<el-input
v-model="form.yourlsSignature"
size="small"
style="width: 50%"
:placeholder="$T('SETTINGS_SHORT_URL_YOURLS_SIGNATURE')"
@change="handleYourlsSignatureChange"
/>
</el-form-item>
<el-form-item
:label="$T('SETTINGS_ENCODE_OUTPUT_URL')"
>
@ -1376,6 +1419,16 @@ import { buildInRenameFormatTable } from '../manage/utils/common'
const imageProcessDialogVisible = ref(false)
const activeName = ref<'system' | 'syncAndConfigure' | 'upload' | 'advanced' | 'upadte'>('system')
const shortUrlServerList = [{
label: 'c1n',
value: 'c1n'
},
{
label: 'yourls',
value: 'yourls'
}
]
const waterMarkPositionMap = new Map([
['north', $T('UPLOAD_PAGE_IMAGE_PROCESS_POSITION_TOP')],
['northeast', $T('UPLOAD_PAGE_IMAGE_PROCESS_POSITION_TOP_RIGHT')],
@ -1489,6 +1542,9 @@ const form = reactive<ISettingForm>({
encodeOutputURL: false,
isAutoListenClipboard: false,
useShortUrl: false,
shortUrlServer: 'c1n',
yourlsDomain: '',
yourlsSignature: '',
deleteLocalFile: false
})
@ -1638,6 +1694,9 @@ async function initData () {
form.customMiniIcon = settings.customMiniIcon || ''
form.isHideDock = settings.isHideDock || false
form.useShortUrl = settings.useShortUrl || false
form.shortUrlServer = settings.shortUrlServer || 'c1n'
form.yourlsDomain = settings.yourlsDomain || ''
form.yourlsSignature = settings.yourlsSignature || ''
form.deleteLocalFile = settings.deleteLocalFile || false
currentLanguage.value = settings.language ?? 'zh-CN'
currentStartMode.value = settings.startMode || 'quiet'
@ -1955,6 +2014,18 @@ function handleUseShortUrl (val: ICheckBoxValueType) {
}
}
function handleShortUrlServerChange (val: string) {
saveConfig('settings.shortUrlServer', val)
}
function handleYourlsDomainChange (val: string) {
saveConfig('settings.yourlsDomain', val)
}
function handleYourlsSignatureChange (val: string) {
saveConfig('settings.yourlsSignature', val)
}
function confirmLogLevelSetting () {
if (form.logLevel.length === 0) {
return $message.error($T('TIPS_PLEASE_CHOOSE_LOG_LEVEL'))

View File

@ -223,6 +223,9 @@ interface ILocales {
SETTINGS_ENCODE_OUTPUT_URL: string
SETTINGS_WATCH_CLIPBOARD: string
SETTINGS_SHORT_URL: string
SETTINGS_SHORT_URL_SERVER: string
SETTINGS_SHORT_URL_YOURLS_DOMAIN: string
SETTINGS_SHORT_URL_YOURLS_SIGNATURE: string
SETTINGS_DELETE_LOCAL_FILE_AFTER_UPLOAD: string
SETTINGS_SYNC_CONFIG: string
SETTINGS_SYNC_CONFIG_TITLE: string

View File

@ -20,6 +20,9 @@ interface ISettingForm {
encodeOutputURL: boolean,
isAutoListenClipboard: boolean,
useShortUrl: boolean,
shortUrlServer: string,
yourlsDomain: string,
yourlsSignature: string,
deleteLocalFile: boolean
}

View File

@ -1,7 +1,3 @@
import axios from 'axios'
import FormData from 'form-data'
import { C1 } from './static'
export const isUrl = (url: string): boolean => {
try {
return Boolean(new URL(url))
@ -48,24 +44,3 @@ export const isDev = process.env.NODE_ENV === 'development'
export const trimValues = <T extends IStringKeyMap>(obj: T): {[K in keyof T]: T[K] extends string ? string : T[K]} => {
return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, typeof value === 'string' ? value.trim() : value])) as {[K in keyof T]: T[K] extends string ? string : T[K]}
}
const c1nApi = 'https://c1n.cn/link/short'
export const generateShortUrl = async (url: string) => {
const form = new FormData()
form.append('url', url)
const C = Buffer.from(C1, 'base64').toString()
try {
const res = await axios.post(c1nApi, form, {
headers: {
token: C
}
})
if (res.status >= 200 && res.status < 300 && res.data?.code === 0) {
return res.data.data
}
} catch (e: any) {
console.log(e)
}
return url
}

View File

@ -11054,10 +11054,10 @@ performance-now@^2.1.0:
resolved "https://registry.npmmirror.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==
piclist@^0.7.3:
version "0.7.3"
resolved "https://registry.npmjs.org/piclist/-/piclist-0.7.3.tgz#41435eb56157890e6720804faf1afcf098d41ef6"
integrity sha512-0mvD3QJm2VKuJfRbg/FBm5osh0SnHwiKSMZ2XphjfAyO4T1So9Y5XnuiGDLOYXiAMWnwbBMrixjiVahcsgGknA==
piclist@^0.7.4:
version "0.7.4"
resolved "https://registry.npmjs.org/piclist/-/piclist-0.7.4.tgz#125c9f337e2d7375ae0c5c17fafaf924134fd65f"
integrity sha512-PxE6S93uWP7sQNyMi6R7ZEs57oOXgm/nD14Moi0k0dbAlvtm1kWdrc4CboW/6e7hPVgeqT7pGi0+y0SF8iTxOA==
dependencies:
"@picgo/i18n" "^1.0.0"
"@picgo/store" "^2.0.4"