Files
PicList/src/main/utils/common.ts

249 lines
6.3 KiB
TypeScript

import path from 'node:path'
import db from '@core/datastore'
import logger from '@core/picgo/logger'
import axios from 'axios'
import { clipboard, dialog, Notification, Tray } from 'electron'
import FormData from 'form-data'
import fs from 'fs-extra'
import { IShortUrlServer } from '#/types/enum'
import { IPrivateShowNotificationOption, IShowMessageBoxResult } from '#/types/types'
import { handleUrlEncode } from '#/utils/common'
import { configPaths } from '#/utils/configPaths'
const getExtension = (fileName: string) => path.extname(fileName).slice(1)
export const isImage = (fileName: string) =>
['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'ico', 'svg', 'avif'].includes(getExtension(fileName))
export let tray: Tray
export const setTray = (t: Tray) => {
tray = t
}
export const getTray = () => tray
export function setTrayToolTip (title: string): void {
if (tray) {
tray.setToolTip(title)
}
}
export const handleCopyUrl = (str: string): void => {
if (db.get(configPaths.settings.autoCopy) !== false) {
clipboard.writeText(str)
}
}
/**
* show notification
* @param options
*/
export const showNotification = (
options: IPrivateShowNotificationOption = {
title: '',
body: '',
clickToCopy: false,
copyContent: '',
clickFn: () => {}
}
) => {
const notification = new Notification({
title: options.title,
body: options.body
// icon: options.icon || undefined
})
const handleClick = () => {
if (options.clickToCopy) {
clipboard.writeText(options.copyContent || options.body)
}
if (options.clickFn) {
options.clickFn()
}
}
notification.once('click', handleClick)
notification.once('close', () => {
notification.removeListener('click', handleClick)
})
notification.show()
}
export const showMessageBox = (options: any) => {
return new Promise<IShowMessageBoxResult>(resolve => {
dialog.showMessageBox(options).then(res => {
resolve({
result: res.response,
checkboxChecked: res.checkboxChecked
})
})
})
}
/**
* macOS public.file-url will get encoded file path,
* so we need to decode it
*/
export const ensureFilePath = (filePath: string, prefix = 'file://'): string => {
filePath = filePath.replace(prefix, '')
if (fs.existsSync(filePath)) {
return `${prefix}${filePath}`
}
filePath = decodeURIComponent(filePath)
if (fs.existsSync(filePath)) {
return `${prefix}${filePath}`
}
return ''
}
/**
* for builtin clipboard to get image path from clipboard
* @returns
*/
export const getClipboardFilePath = (): string => {
// TODO: linux support
const img = clipboard.readImage()
const platform = process.platform
if (!img.isEmpty() && platform === 'darwin') {
let imgPath = clipboard.read('public.file-url') // will get file://xxx/xxx
imgPath = ensureFilePath(imgPath)
return imgPath ? imgPath.replace('file://', '') : ''
}
if (img.isEmpty() && platform === 'win32') {
const imgPath = clipboard
.readBuffer('FileNameW')
?.toString('ucs2')
?.replace(RegExp(String.fromCharCode(0), 'g'), '')
return imgPath || ''
}
return ''
}
export const handleUrlEncodeWithSetting = (url: string) =>
db.get(configPaths.settings.encodeOutputURL) ? handleUrlEncode(url) : url
const c1nApi = 'https://c1n.cn/link/short'
const generateC1NShortUrl = async (url: string) => {
const c1nToken = db.get(configPaths.settings.c1nToken) || ''
if (!c1nToken) {
logger.warn('c1n token is not set')
return url
}
try {
const form = new FormData()
form.append('url', url)
const res = await axios.post(c1nApi, form, {
headers: {
token: c1nToken
}
})
if (res.status >= 200 && res.status < 300 && res.data?.code === 0) {
return res.data.data
}
} catch (e: any) {
logger.error(e)
}
return url
}
const generateYOURLSShortUrl = async (url: string) => {
let domain = db.get(configPaths.settings.yourlsDomain) || ''
const signature = db.get(configPaths.settings.yourlsSignature) || ''
if (!domain || !signature) {
logger.warn('Yourls server or signature is not set')
return url
}
if (!/^https?:\/\//.test(domain)) {
domain = `http://${domain}`
}
const params = new URLSearchParams({
signature,
action: 'shorturl',
format: 'json',
url
})
try {
const res = await axios.get(`${domain}/yourls-api.php?${params.toString()}`)
if (res.data?.shorturl) {
return res.data.shorturl
}
} catch (e: any) {
if (e.response?.data?.message?.includes('already exists in database')) {
return e.response.data.shorturl
}
logger.error(e)
}
return url
}
const generateCFWORKERShortUrl = async (url: string) => {
let cfWorkerHost = db.get(configPaths.settings.cfWorkerHost) || ''
cfWorkerHost = cfWorkerHost.replace(/\/$/, '')
if (!cfWorkerHost) {
logger.warn('CF Worker host is not set')
return url
}
try {
const res = await axios.post(cfWorkerHost, { url })
if (res.data?.status === 200 && res.data?.key?.startsWith('/')) {
return `${cfWorkerHost}${res.data.key}`
}
} catch (e: any) {
logger.error(e)
}
return url
}
const generateSinkShortUrl = async (url: string) => {
let sinkDomain = db.get(configPaths.settings.sinkDomain) || ''
const sinkToken = db.get(configPaths.settings.sinkToken) || ''
if (!sinkDomain || !sinkToken) {
logger.warn('Sink domain or token is not set')
return url
}
if (!/^https?:\/\//.test(sinkDomain)) {
sinkDomain = `http://${sinkDomain}`
}
if (sinkDomain.endsWith('/')) {
sinkDomain = sinkDomain.slice(0, -1)
}
try {
const res = await axios.post(
`${sinkDomain}/api/link/create`,
{ url },
{ headers: { Authorization: `Bearer ${sinkToken}` } }
)
if (res.data?.link?.slug) {
return `${sinkDomain}/${res.data.link.slug}`
}
} catch (e: any) {
logger.error(e)
}
return url
}
export const generateShortUrl = async (url: string) => {
const server = db.get(configPaths.settings.shortUrlServer) || IShortUrlServer.C1N
switch (server) {
case IShortUrlServer.C1N:
return generateC1NShortUrl(url)
case IShortUrlServer.YOURLS:
return generateYOURLSShortUrl(url)
case IShortUrlServer.CFWORKER:
return generateCFWORKERShortUrl(url)
case IShortUrlServer.SINK:
return generateSinkShortUrl(url)
default:
return url
}
}