mirror of
https://github.com/Kuingsmile/PicList.git
synced 2026-05-22 00:30:33 +08:00
✨ Feature(server): add http server for uploading images by a http request
port 37766
This commit is contained in:
@@ -36,6 +36,13 @@ import {
|
||||
} from '~/main/migrate/shortKeyUpdateHelper'
|
||||
import shortKeyHandler from '~/main/utils/shortKeyHandler'
|
||||
import logger from '~/main/utils/logger'
|
||||
import {
|
||||
UPLOAD_WITH_FILES,
|
||||
UPLOAD_WITH_FILES_RESPONSE,
|
||||
UPLOAD_WITH_CLIPBOARD_FILES,
|
||||
UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE
|
||||
} from '~/main/utils/busApi/constants'
|
||||
import server from '~/main/server/index'
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production'
|
||||
protocol.registerSchemesAsPrivileged([{ scheme: 'picgo', privileges: { secure: true, standard: true } }])
|
||||
@@ -246,8 +253,8 @@ const createWindow = () => {
|
||||
return window
|
||||
}
|
||||
|
||||
const createMiniWidow = () => {
|
||||
if (miniWindow) {
|
||||
const createMiniWindow = () => {
|
||||
if (miniWindow || process.platform === 'darwin') {
|
||||
return false
|
||||
}
|
||||
let obj: IBrowserWindowOptions = {
|
||||
@@ -322,7 +329,7 @@ const createSettingWindow = () => {
|
||||
}
|
||||
})
|
||||
createMenu()
|
||||
createMiniWidow()
|
||||
createMiniWindow()
|
||||
return settingWindow
|
||||
}
|
||||
|
||||
@@ -368,9 +375,9 @@ const showWindow = (bounds: IBounds) => {
|
||||
window!.focus()
|
||||
}
|
||||
|
||||
const uploadClipboardFiles = async () => {
|
||||
const uploadClipboardFiles = async (): Promise<string> => {
|
||||
let win
|
||||
if (miniWindow!.isVisible()) {
|
||||
if (miniWindow && miniWindow!.isVisible()) {
|
||||
win = miniWindow
|
||||
} else {
|
||||
win = settingWindow || window || createSettingWindow()
|
||||
@@ -392,19 +399,24 @@ const uploadClipboardFiles = async () => {
|
||||
if (settingWindow) {
|
||||
settingWindow.webContents.send('updateGallery')
|
||||
}
|
||||
return img[0].imgUrl as string
|
||||
} else {
|
||||
const notification = new Notification({
|
||||
title: '上传不成功',
|
||||
body: '你剪贴板最新的一条记录不是图片哦'
|
||||
})
|
||||
notification.show()
|
||||
return ''
|
||||
}
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
const uploadChoosedFiles = async (webContents: WebContents, files: IFileWithPath[]) => {
|
||||
const uploadChoosedFiles = async (webContents: WebContents, files: IFileWithPath[]): Promise<string[]> => {
|
||||
const input = files.map(item => item.path)
|
||||
const imgs = await uploader.setWebContents(webContents).upload(input)
|
||||
const result = []
|
||||
if (imgs !== false) {
|
||||
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
|
||||
let pasteText = ''
|
||||
@@ -419,12 +431,16 @@ const uploadChoosedFiles = async (webContents: WebContents, files: IFileWithPath
|
||||
notification.show()
|
||||
}, i * 100)
|
||||
db.insert('uploaded', imgs[i])
|
||||
result.push(imgs[i].imgUrl!)
|
||||
}
|
||||
clipboard.writeText(pasteText)
|
||||
window!.webContents.send('uploadFiles', imgs)
|
||||
if (settingWindow) {
|
||||
settingWindow.webContents.send('updateGallery')
|
||||
}
|
||||
return result
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -522,7 +538,7 @@ ipcMain.on('openSettingWindow', () => {
|
||||
|
||||
ipcMain.on('openMiniWindow', () => {
|
||||
if (!miniWindow) {
|
||||
createMiniWidow()
|
||||
createMiniWindow()
|
||||
}
|
||||
miniWindow!.show()
|
||||
miniWindow!.focus()
|
||||
@@ -611,7 +627,7 @@ app.on('ready', async () => {
|
||||
updateShortKeyFromVersion212(db, db.get('settings.shortKey'))
|
||||
shortKeyHandler.init()
|
||||
})
|
||||
|
||||
server.startup()
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
let files = getUploadFiles()
|
||||
if (files === null) {
|
||||
@@ -654,6 +670,7 @@ app.on('activate', () => {
|
||||
app.on('will-quit', () => {
|
||||
globalShortcut.unregisterAll()
|
||||
bus.removeAllListeners()
|
||||
server.shutdown()
|
||||
})
|
||||
|
||||
app.setLoginItemSettings({
|
||||
@@ -664,7 +681,9 @@ function initEventCenter () {
|
||||
const eventList: any = {
|
||||
'picgo:upload': uploadClipboardFiles,
|
||||
'createSettingWindow': shortKeyRequestSettingWindow,
|
||||
hideMiniWindow
|
||||
hideMiniWindow,
|
||||
[UPLOAD_WITH_CLIPBOARD_FILES]: busCallUploadClipboardFiles,
|
||||
[UPLOAD_WITH_FILES]: busCallUploadFiles
|
||||
}
|
||||
for (let i in eventList) {
|
||||
bus.on(i, eventList[i])
|
||||
@@ -682,17 +701,35 @@ function hideMiniWindow () {
|
||||
}
|
||||
}
|
||||
|
||||
async function busCallUploadClipboardFiles () {
|
||||
const imgUrl = await uploadClipboardFiles()
|
||||
bus.emit(UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE, imgUrl)
|
||||
}
|
||||
|
||||
async function busCallUploadFiles (pathList: IFileWithPath[]) {
|
||||
let win
|
||||
if (miniWindow && miniWindow.isVisible()) {
|
||||
win = miniWindow
|
||||
} else {
|
||||
win = settingWindow || window || createSettingWindow()
|
||||
}
|
||||
const urls = await uploadChoosedFiles(win.webContents, pathList)
|
||||
bus.emit(UPLOAD_WITH_FILES_RESPONSE, urls)
|
||||
}
|
||||
|
||||
// Exit cleanly on request from parent process in development mode.
|
||||
if (isDevelopment) {
|
||||
if (process.platform === 'win32') {
|
||||
process.on('message', data => {
|
||||
if (data === 'graceful-exit') {
|
||||
app.quit()
|
||||
server.shutdown()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
process.on('SIGTERM', () => {
|
||||
app.quit()
|
||||
server.shutdown()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
72
src/main/server/index.ts
Normal file
72
src/main/server/index.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import http from 'http'
|
||||
import routers from './routerManager'
|
||||
import {
|
||||
handleResponse
|
||||
} from './utils'
|
||||
|
||||
class Server {
|
||||
private httpServer: http.Server
|
||||
private port: number = 36677
|
||||
constructor () {
|
||||
this.httpServer = http.createServer(this.handleRequest)
|
||||
}
|
||||
private handleRequest = (request: http.IncomingMessage, response: http.ServerResponse) => {
|
||||
if (request.method === 'POST') {
|
||||
if (!routers.getHandler(request.url!)) {
|
||||
handleResponse({
|
||||
response,
|
||||
statusCode: 404,
|
||||
header: {},
|
||||
body: {
|
||||
success: false
|
||||
}
|
||||
})
|
||||
} else {
|
||||
let body: string = ''
|
||||
let postObj: IObj
|
||||
request.on('data', chunk => {
|
||||
body += chunk
|
||||
})
|
||||
request.on('end', () => {
|
||||
try {
|
||||
postObj = JSON.parse(body)
|
||||
} catch (err) {
|
||||
return handleResponse({
|
||||
response,
|
||||
body: {
|
||||
success: false,
|
||||
message: 'Not sending data in JSON format'
|
||||
}
|
||||
})
|
||||
}
|
||||
const handler = routers.getHandler(request.url!)
|
||||
handler!({
|
||||
...postObj,
|
||||
response
|
||||
})
|
||||
})
|
||||
}
|
||||
} else {
|
||||
response.statusCode = 404
|
||||
response.end()
|
||||
}
|
||||
}
|
||||
private listen = (port: number) => {
|
||||
console.log(`server listen at ${port}`)
|
||||
this.httpServer.listen(port).on('error', (err: ErrnoException) => {
|
||||
if (err.errno === 'EADDRINUSE') {
|
||||
console.log(`----- Port ${port} is busy, trying with port ${port + 1} -----`)
|
||||
this.port += 1
|
||||
this.listen(this.port)
|
||||
}
|
||||
})
|
||||
}
|
||||
startup () {
|
||||
this.listen(this.port)
|
||||
}
|
||||
shutdown () {
|
||||
this.httpServer.close()
|
||||
}
|
||||
}
|
||||
|
||||
export default new Server()
|
||||
20
src/main/server/router.ts
Normal file
20
src/main/server/router.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
class Router {
|
||||
private router = new Map<string, routeHandler>()
|
||||
|
||||
get (url: string, callback: routeHandler): void {
|
||||
this.router.set(url, callback)
|
||||
}
|
||||
post (url: string, callback: routeHandler): void {
|
||||
this.router.set(url, callback)
|
||||
}
|
||||
|
||||
getHandler (url: string) {
|
||||
if (this.router.has(url)) {
|
||||
return this.router.get(url)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new Router()
|
||||
69
src/main/server/routerManager.ts
Normal file
69
src/main/server/routerManager.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import router from './router'
|
||||
import {
|
||||
uploadWithClipboardFiles,
|
||||
uploadWithFiles
|
||||
} from '~/main/utils/busApi/index'
|
||||
import {
|
||||
handleResponse
|
||||
} from './utils'
|
||||
import logger from '../utils/logger'
|
||||
|
||||
router.get('/upload', async ({
|
||||
response,
|
||||
list = []
|
||||
} : {
|
||||
response: IHttpResponse,
|
||||
list?: string[]
|
||||
}): Promise<void> => {
|
||||
try {
|
||||
if (list.length === 0) {
|
||||
// upload with clipboard
|
||||
const res = await uploadWithClipboardFiles()
|
||||
if (res.success) {
|
||||
handleResponse({
|
||||
response,
|
||||
body: {
|
||||
success: true,
|
||||
result: [res.result]
|
||||
}
|
||||
})
|
||||
} else {
|
||||
handleResponse({
|
||||
response
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// upload with files
|
||||
const pathList = list.map(item => {
|
||||
return {
|
||||
path: item
|
||||
}
|
||||
})
|
||||
const res = await uploadWithFiles(pathList)
|
||||
if (res.success) {
|
||||
handleResponse({
|
||||
response,
|
||||
body: {
|
||||
success: true,
|
||||
result: res.result
|
||||
}
|
||||
})
|
||||
} else {
|
||||
handleResponse({
|
||||
response
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(err)
|
||||
handleResponse({
|
||||
response,
|
||||
body: {
|
||||
success: false,
|
||||
message: err
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
19
src/main/server/utils.ts
Normal file
19
src/main/server/utils.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export const handleResponse = ({
|
||||
response,
|
||||
statusCode = 200,
|
||||
header = {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body = {
|
||||
success: false
|
||||
}
|
||||
} : {
|
||||
response: IHttpResponse,
|
||||
statusCode?: number,
|
||||
header?: IObj,
|
||||
body?: any
|
||||
}) => {
|
||||
response.writeHead(statusCode, header)
|
||||
response.write(JSON.stringify(body))
|
||||
response.end()
|
||||
}
|
||||
6
src/main/utils/busApi/constants.ts
Normal file
6
src/main/utils/busApi/constants.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export const GET_SETTING_WINDOW = 'GET_SETTING_WINDOW'
|
||||
export const GET_MINI_WINDOW = 'GET_SETTING_WINDOW'
|
||||
export const UPLOAD_WITH_FILES = 'UPLOAD_WITH_FILES'
|
||||
export const UPLOAD_WITH_FILES_RESPONSE = 'UPLOAD_WITH_FILES_RESPONSE'
|
||||
export const UPLOAD_WITH_CLIPBOARD_FILES = 'UPLOAD_WITH_CLIPBOARD_FILES'
|
||||
export const UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE = 'UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE'
|
||||
49
src/main/utils/busApi/index.ts
Normal file
49
src/main/utils/busApi/index.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import bus from '../eventBus'
|
||||
import {
|
||||
UPLOAD_WITH_FILES,
|
||||
UPLOAD_WITH_FILES_RESPONSE,
|
||||
UPLOAD_WITH_CLIPBOARD_FILES,
|
||||
UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE
|
||||
} from './constants'
|
||||
|
||||
export const uploadWithClipboardFiles = (): Promise<{
|
||||
success: boolean,
|
||||
result?: string[]
|
||||
}> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
bus.emit(UPLOAD_WITH_CLIPBOARD_FILES)
|
||||
bus.once(UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE, (result: string) => {
|
||||
if (result) {
|
||||
return resolve({
|
||||
success: true,
|
||||
result: [result]
|
||||
})
|
||||
} else {
|
||||
return resolve({
|
||||
success: false
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export const uploadWithFiles = (pathList: IFileWithPath[]): Promise<{
|
||||
success: boolean,
|
||||
result?: string[]
|
||||
}> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
bus.emit(UPLOAD_WITH_FILES, pathList)
|
||||
bus.once(UPLOAD_WITH_FILES_RESPONSE, (result: string[]) => {
|
||||
if (result.length) {
|
||||
return resolve({
|
||||
success: true,
|
||||
result
|
||||
})
|
||||
} else {
|
||||
return resolve({
|
||||
success: false
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 60 KiB |
BIN
src/renderer/assets/squareLogo.png
Normal file
BIN
src/renderer/assets/squareLogo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
@@ -29,7 +29,7 @@ import path from 'path'
|
||||
mixins: [mixin]
|
||||
})
|
||||
export default class extends Vue {
|
||||
logo = path.join(process.env.BASE_URL as string, 'squareLogo.png')
|
||||
logo = require('../assets/squareLogo.png')
|
||||
dragover = false
|
||||
progress = 0
|
||||
showProgress = false
|
||||
|
||||
17
src/universal/types/types.d.ts
vendored
17
src/universal/types/types.d.ts
vendored
@@ -7,6 +7,23 @@ interface IObjT<T> {
|
||||
[propName: string]: T
|
||||
}
|
||||
|
||||
interface ErrnoException extends Error {
|
||||
errno?: number | string;
|
||||
code?: string;
|
||||
path?: string;
|
||||
syscall?: string;
|
||||
stack?: string;
|
||||
}
|
||||
|
||||
type routeHandler = (ctx: IServerCTX) => Promise<void>
|
||||
|
||||
type IHttpResponse = import('http').ServerResponse
|
||||
|
||||
interface IServerCTX {
|
||||
response: IHttpResponse
|
||||
[propName: string]: any
|
||||
}
|
||||
|
||||
// Image && PicBed
|
||||
interface ImgInfo {
|
||||
buffer?: Buffer
|
||||
|
||||
Reference in New Issue
Block a user