Feature(custom): add buildin web server

ISSUES CLOSED: #180
This commit is contained in:
Kuingsmile 2024-04-02 13:29:55 +08:00
parent 8ed24cb2b8
commit ab8b62e661
9 changed files with 242 additions and 2 deletions

View File

@ -209,9 +209,12 @@ SETTINGS_ENABLE_SERVER: Enable Server
SETTINGS_SET_SERVER_HOST: Set Server Host SETTINGS_SET_SERVER_HOST: Set Server Host
SETTINGS_SET_SERVER_PORT: Set Server Port SETTINGS_SET_SERVER_PORT: Set Server Port
SETTINGS_SET_SERVER_KEY: Set Auth Key SETTINGS_SET_SERVER_KEY: Set Auth Key
SETTINGS_SET_WEB_SERVER: Set Web Server
SETTINGS_TIPS_WEB_SERVER_NOTICE: If you don't know what is the web server's function, please read the document, or don't modify the configuration.
SETTINGS_SET_ENABLE_WEB_SERVER: Enable Web Server SETTINGS_SET_ENABLE_WEB_SERVER: Enable Web Server
SETTINGS_SET_WEB_SERVER_HOST: Set Web Server Host SETTINGS_SET_WEB_SERVER_HOST: Set Web Server Host
SETTINGS_SET_WEB_SERVER_PORT: Set Web Server Port SETTINGS_SET_WEB_SERVER_PORT: Set Web Server Port
SETTINGS_SET_WEB_SERVER_PATH: Set Web Server Path
SETTINGS_TIP_PLACEHOLDER_WEB_HOST: Default:127.0.0.1 SETTINGS_TIP_PLACEHOLDER_WEB_HOST: Default:127.0.0.1
SETTINGS_TIP_PLACEHOLDER_WEB_PORT: Default:37777 SETTINGS_TIP_PLACEHOLDER_WEB_PORT: Default:37777
SETTINGS_TIP_PLACEHOLDER_HOST: Default:127.0.0.1 SETTINGS_TIP_PLACEHOLDER_HOST: Default:127.0.0.1

View File

@ -211,9 +211,12 @@ SETTINGS_ENABLE_SERVER: 是否开启Server
SETTINGS_SET_SERVER_HOST: 设置监听地址 SETTINGS_SET_SERVER_HOST: 设置监听地址
SETTINGS_SET_SERVER_PORT: 设置监听端口 SETTINGS_SET_SERVER_PORT: 设置监听端口
SETTINGS_SET_SERVER_KEY: 设置鉴权密钥 SETTINGS_SET_SERVER_KEY: 设置鉴权密钥
SETTINGS_SET_WEB_SERVER: 设置Web服务
SETTINGS_TIPS_WEB_SERVER_NOTICE: 如果你不知道Web服务的作用请阅读文档或者不用修改配置。
SETTINGS_SET_ENABLE_WEB_SERVER: 是否开启Web服务 SETTINGS_SET_ENABLE_WEB_SERVER: 是否开启Web服务
SETTINGS_SET_WEB_SERVER_HOST: 设置Web服务监听地址 SETTINGS_SET_WEB_SERVER_HOST: 设置Web服务监听地址
SETTINGS_SET_WEB_SERVER_PORT: 设置Web服务监听端口 SETTINGS_SET_WEB_SERVER_PORT: 设置Web服务监听端口
SETTINGS_SET_WEB_SERVER_PATH: 设置Web服务路径
SETTINGS_TIP_PLACEHOLDER_WEB_HOST: 推荐默认地址:127.0.0.1 SETTINGS_TIP_PLACEHOLDER_WEB_HOST: 推荐默认地址:127.0.0.1
SETTINGS_TIP_PLACEHOLDER_WEB_PORT: 推荐默认端口:37777 SETTINGS_TIP_PLACEHOLDER_WEB_PORT: 推荐默认端口:37777
SETTINGS_TIP_PLACEHOLDER_HOST: 推荐默认地址:127.0.0.1 SETTINGS_TIP_PLACEHOLDER_HOST: 推荐默认地址:127.0.0.1

View File

@ -209,9 +209,12 @@ SETTINGS_ENABLE_SERVER: 是否開啟Server
SETTINGS_SET_SERVER_HOST: 設定監聽地址 SETTINGS_SET_SERVER_HOST: 設定監聽地址
SETTINGS_SET_SERVER_PORT: 設定監聽端口 SETTINGS_SET_SERVER_PORT: 設定監聽端口
SETTINGS_SET_SERVER_KEY: 設定鑒權密鑰 SETTINGS_SET_SERVER_KEY: 設定鑒權密鑰
SETTINGS_SET_WEB_SERVER: 設定Web服務
SETTINGS_TIPS_WEB_SERVER_NOTICE: 如果你不知道Web服務的作用請閱讀文檔或者不用修改設定。
SETTINGS_SET_ENABLE_WEB_SERVER: 是否開啟Web服務 SETTINGS_SET_ENABLE_WEB_SERVER: 是否開啟Web服務
SETTINGS_SET_WEB_SERVER_HOST: 設定Web服務地 SETTINGS_SET_WEB_SERVER_HOST: 設定Web服務地
SETTINGS_SET_WEB_SERVER_PORT: 設定Web服務端口 SETTINGS_SET_WEB_SERVER_PORT: 設定Web服務端口
SETTINGS_SET_WEB_SERVER_PATH: 設定Web服務路徑
SETTINGS_TIP_PLACEHOLDER_WEB_HOST: 推薦預設地址:127.0.0.1 SETTINGS_TIP_PLACEHOLDER_WEB_HOST: 推薦預設地址:127.0.0.1
SETTINGS_TIP_PLACEHOLDER_WEB_PORT: 推薦預設端口:37777 SETTINGS_TIP_PLACEHOLDER_WEB_PORT: 推薦預設端口:37777
SETTINGS_TIP_PLACEHOLDER_HOST: 推薦預設地址:127.0.0.1 SETTINGS_TIP_PLACEHOLDER_HOST: 推薦預設地址:127.0.0.1

View File

@ -89,6 +89,7 @@ import SSHClient from '../utils/sshClient'
import { ISftpPlistConfig } from 'piclist' import { ISftpPlistConfig } from 'piclist'
import { removeFileFromS3InMain, removeFileFromDogeInMain, removeFileFromHuaweiInMain } from '~/main/utils/deleteFunc' import { removeFileFromS3InMain, removeFileFromDogeInMain, removeFileFromHuaweiInMain } from '~/main/utils/deleteFunc'
import webServer from '../server/webServer'
const STORE_PATH = app.getPath('userData') const STORE_PATH = app.getPath('userData')
@ -360,6 +361,12 @@ export default {
ipcMain.on('updateServer', () => { ipcMain.on('updateServer', () => {
server.restart() server.restart()
}) })
ipcMain.on('stopWebServer', () => {
webServer.stop()
})
ipcMain.on('restartWebServer', () => {
webServer.restart()
})
ipcMain.on(OPEN_DEVTOOLS, (event: IpcMainEvent) => { ipcMain.on(OPEN_DEVTOOLS, (event: IpcMainEvent) => {
event.sender.openDevTools() event.sender.openDevTools()
}) })

View File

@ -45,6 +45,7 @@ import path from 'path'
import { CLIPBOARD_IMAGE_FOLDER } from '~/universal/utils/static' import { CLIPBOARD_IMAGE_FOLDER } from '~/universal/utils/static'
import fs from 'fs-extra' import fs from 'fs-extra'
import { startFileServer } from '../fileServer' import { startFileServer } from '../fileServer'
import webServer from '../server/webServer'
import axios from 'axios' import axios from 'axios'
const isDevelopment = process.env.NODE_ENV !== 'production' const isDevelopment = process.env.NODE_ENV !== 'production'
@ -179,6 +180,7 @@ class LifeCycle {
}) })
server.startup() server.startup()
startFileServer() startFileServer()
webServer.start()
if (process.env.NODE_ENV !== 'development') { if (process.env.NODE_ENV !== 'development') {
handleStartUpFiles(process.argv, process.cwd()) handleStartUpFiles(process.argv, process.cwd())
} }

View File

@ -0,0 +1,96 @@
import http from 'http'
import fs from 'fs-extra'
import path from 'path'
import picgo from '@core/picgo'
import logger from '../../apis/core/picgo/logger'
const defaultPath = process.platform === 'win32' ? 'C:/Users/' : '/'
function generateDirectoryListingHtml (files: any[], requestPath: any) {
let html = '<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body><h1>Directory Listing</h1><ul>'
files.forEach((file: string) => {
html += `<li><a href="${path.join(requestPath, file)}">${file}</a></li>`
})
html += '</ul></body></html>'
return html
}
class WebServer {
private server: http.Server
private config: IStringKeyMap
constructor () {
this.config = this.getConfigWithDefaults()
this.server = this.getServer()
}
getConfigWithDefaults (): IStringKeyMap {
const enableWebServer = picgo.getConfig<boolean>('settings.enableWebServer') || false
const webServerHost = picgo.getConfig<string>('settings.webServerHost') || '0.0.0.0'
const webServerPort = picgo.getConfig<number>('settings.webServerPort') || 37777
const webServerPath = picgo.getConfig<string>('settings.webServerPath') || defaultPath
return { enableWebServer, webServerHost, webServerPort, webServerPath }
}
getServer (): http.Server {
return http.createServer((req, res) => {
const requestPath = req.url?.split('?')[0]
const filePath = path.join(this.config.webServerPath, decodeURIComponent(requestPath as string))
fs.stat(filePath, (err, stats) => {
if (err) {
res.writeHead(404)
res.end('404 Not Found')
return
}
if (stats.isDirectory()) {
fs.readdir(filePath, (err, files) => {
if (err) {
res.writeHead(500)
res.end('Error listing directory contents')
} else {
res.writeHead(200, { 'Content-Type': 'text/html' })
res.end(generateDirectoryListingHtml(files, requestPath))
}
})
} else {
fs.readFile(filePath, (err, data) => {
if (err) {
res.writeHead(404)
res.end('404 Not Found')
} else {
res.end(data)
}
})
}
})
})
}
start () {
if (this.config.enableWebServer) {
this.server.listen(this.config.webServerPort, this.config.webServerHost, () => {
logger.info(`Web server is running, http://${this.config.webServerHost}:${this.config.webServerPort}, root path is ${this.config.webServerPath}`)
}).on('error', (err) => {
logger.error(err)
})
} else {
logger.info('Web server is not enabled')
}
}
stop () {
this.server.close()
logger.info('Web server is stopped')
}
restart () {
this.stop()
this.config = this.getConfigWithDefaults()
this.server = this.getServer()
this.start()
}
}
export default new WebServer()

View File

@ -654,6 +654,18 @@
{{ $T('SETTINGS_CLICK_TO_SET') }} {{ $T('SETTINGS_CLICK_TO_SET') }}
</el-button> </el-button>
</el-form-item> </el-form-item>
<el-form-item
:label="$T('SETTINGS_SET_WEB_SERVER')"
>
<el-button
type="primary"
round
size="small"
@click="webServerVisible = true"
>
{{ $T('SETTINGS_CLICK_TO_SET') }}
</el-button>
</el-form-item>
<el-form-item <el-form-item
:label="$T('SETTINGS_SET_SERVER')" :label="$T('SETTINGS_SET_SERVER')"
> >
@ -732,6 +744,7 @@
:title="$T('SETTINGS_CUSTOM_LINK_FORMAT')" :title="$T('SETTINGS_CUSTOM_LINK_FORMAT')"
:modal-append-to-body="false" :modal-append-to-body="false"
center center
draggable
append-to-body append-to-body
> >
<el-form <el-form
@ -783,6 +796,7 @@
:modal-append-to-body="false" :modal-append-to-body="false"
width="70%" width="70%"
center center
draggable
append-to-body append-to-body
> >
<el-form <el-form
@ -840,6 +854,7 @@
:modal-append-to-body="false" :modal-append-to-body="false"
width="70%" width="70%"
center center
draggable
append-to-body append-to-body
> >
<el-form <el-form
@ -896,6 +911,7 @@
:title="$T('SETTINGS_CHECK_UPDATE')" :title="$T('SETTINGS_CHECK_UPDATE')"
:modal-append-to-body="false" :modal-append-to-body="false"
center center
draggable
append-to-body append-to-body
> >
<div> <div>
@ -1025,6 +1041,7 @@
:modal-append-to-body="false" :modal-append-to-body="false"
width="500px" width="500px"
center center
draggable
append-to-body append-to-body
> >
<el-form <el-form
@ -1122,6 +1139,7 @@
:title="$T('SETTINGS_SET_PICGO_SERVER')" :title="$T('SETTINGS_SET_PICGO_SERVER')"
:modal-append-to-body="false" :modal-append-to-body="false"
center center
draggable
append-to-body append-to-body
> >
<div class="notice-text"> <div class="notice-text">
@ -1187,6 +1205,69 @@
</el-button> </el-button>
</template> </template>
</el-dialog> </el-dialog>
<el-dialog
v-model="webServerVisible"
class="server-dialog"
width="60%"
:title="$T('SETTINGS_SET_WEB_SERVER')"
:modal-append-to-body="false"
align-center
draggable
append-to-body
@close="confirmWebServerSetting"
>
<div class="notice-text">
{{ $T('SETTINGS_TIPS_WEB_SERVER_NOTICE') }}
</div>
<el-form
label-position="right"
label-width="180px"
>
<el-form-item
:label="$T('SETTINGS_SET_ENABLE_WEB_SERVER')"
>
<el-switch
v-model="form.enableWebServer"
:active-text="$T('SETTINGS_OPEN')"
:inactive-text="$T('SETTINGS_CLOSE')"
@change="handleEnableWebServerChange"
/>
</el-form-item>
<template v-if="form.enableWebServer">
<el-form-item
:label="$T('SETTINGS_SET_WEB_SERVER_HOST')"
>
<el-input
v-model="form.webServerHost"
type="input"
:placeholder="$T('SETTINGS_TIP_PLACEHOLDER_WEB_HOST')"
@change="handleWebServerHostChange"
/>
</el-form-item>
<el-form-item
:label="$T('SETTINGS_SET_WEB_SERVER_PORT')"
>
<el-input-number
v-model="form.webServerPort"
:min="1"
:max="65535"
:placeholder="$T('SETTINGS_TIP_PLACEHOLDER_WEB_PORT')"
@change="handleWebServerPortChange"
/>
</el-form-item>
<el-form-item
:label="$T('SETTINGS_SET_WEB_SERVER_PATH')"
>
<el-input
v-model="form.webServerPath"
type="input"
:placeholder="$T('SETTINGS_SET_WEB_SERVER_PATH')"
@change="handleWebServerPathChange"
/>
</el-form-item>
</template>
</el-form>
</el-dialog>
<el-dialog <el-dialog
v-model="syncVisible" v-model="syncVisible"
class="server-dialog" class="server-dialog"
@ -1194,6 +1275,7 @@
:title="$T('SETTINGS_SYNC_CONFIG_TITLE')" :title="$T('SETTINGS_SYNC_CONFIG_TITLE')"
:modal-append-to-body="false" :modal-append-to-body="false"
center center
draggable
append-to-body append-to-body
> >
<div class="notice-text"> <div class="notice-text">
@ -1300,6 +1382,7 @@
:title="$T('SETTINGS_UP_DOWN_DESC')" :title="$T('SETTINGS_UP_DOWN_DESC')"
:modal-append-to-body="false" :modal-append-to-body="false"
center center
draggable
append-to-body append-to-body
> >
<el-form <el-form
@ -1871,7 +1954,11 @@ const form = reactive<ISettingForm>({
deleteLocalFile: false, deleteLocalFile: false,
serverKey: '', serverKey: '',
aesPassword: '', aesPassword: '',
manualPageOpen: 'browser' manualPageOpen: 'browser',
enableWebServer: false,
webServerHost: '0.0.0.0',
webServerPort: 37777,
webServerPath: ''
}) })
const languageList = i18nManager.languageList.map(item => ({ const languageList = i18nManager.languageList.map(item => ({
@ -1888,6 +1975,7 @@ const logFileVisible = ref(false)
const customLinkVisible = ref(false) const customLinkVisible = ref(false)
const checkUpdateVisible = ref(false) const checkUpdateVisible = ref(false)
const serverVisible = ref(false) const serverVisible = ref(false)
const webServerVisible = ref(false)
const syncVisible = ref(false) const syncVisible = ref(false)
const upDownConfigVisible = ref(false) const upDownConfigVisible = ref(false)
const proxyVisible = ref(false) const proxyVisible = ref(false)
@ -2039,6 +2127,10 @@ async function initData () {
form.serverKey = settings.serverKey || '' form.serverKey = settings.serverKey || ''
form.aesPassword = settings.aesPassword || 'PicList-aesPassword' form.aesPassword = settings.aesPassword || 'PicList-aesPassword'
form.manualPageOpen = settings.manualPageOpen || 'window' form.manualPageOpen = settings.manualPageOpen || 'window'
form.enableWebServer = settings.enableWebServer || false
form.webServerHost = settings.webServerHost || '0.0.0.0'
form.webServerPort = settings.webServerPort || 37777
form.webServerPath = settings.webServerPath || ''
currentLanguage.value = settings.language ?? 'zh-CN' currentLanguage.value = settings.language ?? 'zh-CN'
currentStartMode.value = settings.startMode || 'quiet' currentStartMode.value = settings.startMode || 'quiet'
customLink.value = settings.customLink || '![$fileName]($url)' customLink.value = settings.customLink || '![$fileName]($url)'
@ -2296,6 +2388,31 @@ function cancelCheckVersion () {
checkUpdateVisible.value = false checkUpdateVisible.value = false
} }
function handleEnableWebServerChange (val: ICheckBoxValueType) {
saveConfig('settings.enableWebServer', val)
}
function handleWebServerHostChange (val: string) {
saveConfig('settings.webServerHost', val)
}
function handleWebServerPortChange (val?: number, oldVal?: number) {
saveConfig('settings.webServerPort', Number(val) || 37777)
}
function handleWebServerPathChange (val: string) {
saveConfig('settings.webServerPath', val)
}
function confirmWebServerSetting () {
console.log('confirmWebServerSetting')
if (form.enableWebServer) {
sendToMain('restartWebServer')
} else {
sendToMain('stopWebServer')
}
}
function handleServerKeyChange (val: string) { function handleServerKeyChange (val: string) {
saveConfig('settings.serverKey', val) saveConfig('settings.serverKey', val)
} }

View File

@ -204,9 +204,14 @@ interface ILocales {
SETTINGS_SET_SERVER_HOST: string SETTINGS_SET_SERVER_HOST: string
SETTINGS_SET_SERVER_PORT: string SETTINGS_SET_SERVER_PORT: string
SETTINGS_SET_SERVER_KEY: string SETTINGS_SET_SERVER_KEY: string
SETTINGS_SET_WEB_SERVER: string
SETTINGS_TIPS_WEB_SERVER_NOTICE: string
SETTINGS_SET_ENABLE_WEB_SERVER: string SETTINGS_SET_ENABLE_WEB_SERVER: string
SETTINGS_SET_WEB_SERVER_HOST: string SETTINGS_SET_WEB_SERVER_HOST: string
SETTINGS_SET_WEB_SERVER_PORT: string SETTINGS_SET_WEB_SERVER_PORT: string
SETTINGS_SET_WEB_SERVER_PATH: string
SETTINGS_TIP_PLACEHOLDER_WEB_HOST: string
SETTINGS_TIP_PLACEHOLDER_WEB_PORT: string
SETTINGS_TIP_PLACEHOLDER_HOST: string SETTINGS_TIP_PLACEHOLDER_HOST: string
SETTINGS_TIP_PLACEHOLDER_PORT: string SETTINGS_TIP_PLACEHOLDER_PORT: string
SETTINGS_TIP_PLACEHOLDER_KEY: string SETTINGS_TIP_PLACEHOLDER_KEY: string

View File

@ -32,7 +32,11 @@ interface ISettingForm {
deleteLocalFile: boolean, deleteLocalFile: boolean,
serverKey: string, serverKey: string,
aesPassword: string, aesPassword: string,
manualPageOpen: string manualPageOpen: string,
enableWebServer: boolean,
webServerHost: string,
webServerPort: number,
webServerPath: string,
} }
interface IShortKeyMap { interface IShortKeyMap {