From 3cdd8b683cf59a2103220989d6c2b2b86b6084dd Mon Sep 17 00:00:00 2001 From: stapxs <1007028430.stapx@gmail.com> Date: Thu, 21 Nov 2024 15:01:25 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B8=85=E7=90=86=20require=E3=80=81process?= =?UTF-8?q?=EF=BC=88=E6=AD=A4=E7=89=88=E6=9C=AC=E6=9A=82=E6=97=B6=E4=B8=8D?= =?UTF-8?q?=E5=8F=AF=E7=94=A8=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 14 +++ .envrc | 1 + package.json | 1 + src/preload/index.d.ts | 7 ++ src/preload/index.ts | 12 ++- src/renderer/src/App.vue | 34 +++---- src/renderer/src/components/MsgBody.vue | 2 +- .../components/msg-component/CardMessage.vue | 2 +- src/renderer/src/env.d.ts | 6 ++ .../src/function/elements/information.ts | 4 +- src/renderer/src/function/notify.ts | 20 +--- src/renderer/src/function/utils/appUtil.ts | 11 +-- src/renderer/src/function/utils/msgUtil.ts | 4 +- src/renderer/src/function/utils/systemUtil.ts | 30 +++--- src/renderer/src/main.ts | 2 +- src/renderer/src/pages/Chat.vue | 8 +- .../Chat\347\273\210\347\253\257.vue" | 2 +- .../src/pages/chat-view/SystemNotice.vue | 4 +- src/renderer/src/pages/options/OptDev.vue | 4 +- .../src/pages/options/OptFunction.vue | 4 +- src/renderer/src/registerServiceWorker.ts | 4 +- ssqq-web/.gitignore | 2 + ssqq-web/.npmignore | 3 + ssqq-web/index.ts | 95 +++++++++++++++++++ ssqq-web/package.json | 26 +++++ ssqq-web/tsconfig.json | 6 ++ ssqq-web/update.ts | 64 +++++++++++++ yarn.lock | 5 + 28 files changed, 295 insertions(+), 82 deletions(-) create mode 100644 .env create mode 100644 .envrc create mode 100644 ssqq-web/.gitignore create mode 100644 ssqq-web/.npmignore create mode 100644 ssqq-web/index.ts create mode 100644 ssqq-web/package.json create mode 100644 ssqq-web/tsconfig.json create mode 100644 ssqq-web/update.ts diff --git a/.env b/.env new file mode 100644 index 00000000..61ef6484 --- /dev/null +++ b/.env @@ -0,0 +1,14 @@ +# 功能 API 相关设置 +VITE_APP_LINK_VIEW=https://api.stapxs.cn/tool/page-info/ + +# 用户统计 umami 相关设置 +VITE_APP_MU_ADDRESS=https://status.stapxs.cn +VITE_APP_MU_ID=fa20b37d-93a3-41d5-a180-dd66fbbcc573 +VITE_APP_MU_SHARE=https://status.stapxs.cn/share/dxwhBcriczpA7wWQ/Stapxs%20QQ%20Lite%202.0 + +# 高德地图相关设置 +VITE_APP_AMAP_KEY=99b8849d7b67f05e9f834bde4108f0f9 +VITE_APP_AMAP_SECRET=e9ac5aac621defdd6bb111b55fb75d73 + +# 后端连接相关设置 +VITE_APP_BACKEND_CONNECT=true diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..b7e8bccc --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +export PYTHON_PATH=/Library/Frameworks/Python.framework/Versions/2.7/bin/python diff --git a/package.json b/package.json index 5d41c4f7..5cc2848a 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "electron-store": "^10.0.0", "electron-window-state": "^5.0.3", "log4js": "^6.9.1", + "pofile": "^1.1.4", "vite-plugin-vue-devtools": "^7.6.4", "ws": "^8.18.0" }, diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts index 04ef9820..3093206d 100644 --- a/src/preload/index.d.ts +++ b/src/preload/index.d.ts @@ -1,4 +1,11 @@ import { ElectronAPI } from '@electron-toolkit/preload' +import { Shell } from 'electron' + +declare module '@electron-toolkit/preload' { + interface ElectronAPI { + shell: Shell + } +} declare global { interface Window { diff --git a/src/preload/index.ts b/src/preload/index.ts index eeca257e..1ff59a25 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -1,15 +1,21 @@ -import { contextBridge } from 'electron' +import { contextBridge, shell } from 'electron' import { electronAPI } from '@electron-toolkit/preload' // Custom APIs for renderer const api = {} +// 拓展 electronAPI +const extendedElectronAPI = { + ...electronAPI, + shell: shell +} + // Use `contextBridge` APIs to expose Electron APIs to // renderer only if context isolation is enabled, otherwise // just add to the DOM global. if (process.contextIsolated) { try { - contextBridge.exposeInMainWorld('electron', electronAPI) + contextBridge.exposeInMainWorld('electron', extendedElectronAPI) contextBridge.exposeInMainWorld('api', api) } catch (error) { // eslint-disable-next-line no-console @@ -17,7 +23,7 @@ if (process.contextIsolated) { } } else { // @ts-ignore (define in dts) - window.electron = electronAPI + window.electron = extendedElectronAPI // @ts-ignore (define in dts) window.api = api } diff --git a/src/renderer/src/App.vue b/src/renderer/src/App.vue index d4b81209..2e7100cc 100644 --- a/src/renderer/src/App.vue +++ b/src/renderer/src/App.vue @@ -365,7 +365,7 @@ }, data() { return { - dev: process.env.NODE_ENV == 'development', + dev: import.meta.env.DEV, Connector: Connector, defineAsyncComponent: defineAsyncComponent, save: Option.runASWEvent, @@ -411,14 +411,13 @@ window.onload = async () => { // 初始化全局参数 runtimeData.tags.isElectron = window.electron != undefined - // const reader = electron ? electron.ipcRenderer : null - // runtimeData.reader = reader - // if (reader) { - // runtimeData.tags.platform = - // await reader.invoke('sys:getPlatform') - // runtimeData.tags.release = - // await reader.invoke('sys:getRelease') - // } + runtimeData.reader = window.electron.ipcRenderer + if (runtimeData.reader) { + runtimeData.tags.platform = + await runtimeData.reader.invoke('sys:getPlatform') + runtimeData.tags.release = + await runtimeData.reader.invoke('sys:getRelease') + } app.config.globalProperties.$viewer = this.viewerBody // 初始化波浪动画 runtimeData.tags.loginWaveTimer = this.waveAnimation( @@ -431,7 +430,7 @@ App.createMenu() // Electron:创建菜单 App.createIpc() // Electron:创建 IPC 通信 // 加载开发者相关功能 - if (process.env.NODE_ENV == 'development') { + if (this.dev) { document.title = 'Stapxs QQ Lite (Dev)' // 布局检查工具 Spacing.start() @@ -456,7 +455,7 @@ // 基础初始化完成 logger.debug('欢迎使用 Stapxs QQ Lite!') - logger.debug('当前启动模式为: ' + process.env.NODE_ENV) + logger.debug('当前启动模式为: ' + this.dev ? 'development' : 'production') logger.debug('Electron 环境: ' + runtimeData.tags.isElectron) // 加载额外样式 App.loadAppendStyle() @@ -475,20 +474,17 @@ // ============================================================= // 初始化完成 // UM:加载 Umami 统计功能 - if ( - !Option.get('close_ga') && - process.env.NODE_ENV == 'production' - ) { + if (!Option.get('close_ga') && this.dev) { // 给页面添加一个来源域名方便在 electron 中获取 const config = { - baseUrl: process.env.VUE_APP_MU_ADDRESS, - websiteId: process.env.VUE_APP_MU_ID, + baseUrl: import.meta.env.VITE_UMAMI_URL, + websiteId: import.meta.env.VITE_UMAMI_ID, } as any if (runtimeData.tags.isElectron) { config.hostName = 'electron.stapxs.cn' } Umami.initialize(config) - } else if (process.env.NODE_ENV == 'development') { + } else if (this.dev) { logger.debug('由于运行在调试模式下,分析组件并未初始化 ……') } else if (Option.get('close_ga')) { logger.debug('统计功能已被关闭,分析组件并未初始化 ……') @@ -533,7 +529,7 @@ // UM:发送页面路由分析 if ( !Option.get('close_ga') && - process.env.NODE_ENV == 'production' + this.dev ) { Umami.trackPageView('/' + view) } diff --git a/src/renderer/src/components/MsgBody.vue b/src/renderer/src/components/MsgBody.vue index f8e137e3..a81a54e0 100644 --- a/src/renderer/src/components/MsgBody.vue +++ b/src/renderer/src/components/MsgBody.vue @@ -612,7 +612,7 @@ const fistLink = linkList[0] // 获取链接预览 fetch( - process.env.VUE_APP_LINK_VIEW + + import.meta.env.VITE_APP_LINK_VIEW + encodeURIComponent(fistLink), ) .then((res) => res.json()) diff --git a/src/renderer/src/components/msg-component/CardMessage.vue b/src/renderer/src/components/msg-component/CardMessage.vue index 2e119fb9..e1136b0d 100644 --- a/src/renderer/src/components/msg-component/CardMessage.vue +++ b/src/renderer/src/components/msg-component/CardMessage.vue @@ -116,7 +116,7 @@ */ createMap() { const json = JSON.parse(this.item.data) - window.createMap(process.env.VUE_APP_AMAP_KEY, this.id, { + window.createMap(import.meta.env.VITE_AMAP_KEY, this.id, { lat: json.meta['Location.Search'].lat, lng: json.meta['Location.Search'].lng, }) diff --git a/src/renderer/src/env.d.ts b/src/renderer/src/env.d.ts index 4e450ed9..2bf8a08c 100644 --- a/src/renderer/src/env.d.ts +++ b/src/renderer/src/env.d.ts @@ -16,3 +16,9 @@ declare interface Window { lng: number }) => void } + +// po 文件,按字符串处理 +declare module '*.po' { + const value: string + export default value +} diff --git a/src/renderer/src/function/elements/information.ts b/src/renderer/src/function/elements/information.ts index 2ece279b..31db6c69 100644 --- a/src/renderer/src/function/elements/information.ts +++ b/src/renderer/src/function/elements/information.ts @@ -1,3 +1,5 @@ +import { IpcRenderer } from '@electron-toolkit/preload' + export enum BotMsgType { CQCode, Array, @@ -5,7 +7,7 @@ export enum BotMsgType { } export interface RunTimeDataElem { - reader?: Electron.IpcRenderer | null + reader?: IpcRenderer | null sysConfig: { [key: string]: any } jsonMap?: any botInfo: { [key: string]: any } diff --git a/src/renderer/src/function/notify.ts b/src/renderer/src/function/notify.ts index 7f67afc7..7c29fbba 100644 --- a/src/renderer/src/function/notify.ts +++ b/src/renderer/src/function/notify.ts @@ -15,9 +15,7 @@ export class Notify { */ public notify(info: NotifyInfo) { const $t = app.config.globalProperties.$t - const isElectron = - (process.env.IS_ELECTRON as unknown as boolean) && - window.require != undefined + const isElectron = runtimeData.tags.isElectron // 判断当前 userId 是否已存在通知 const userId = info.tag.split('/')[0] if (Notify.userNotifyList[userId] === undefined) { @@ -61,9 +59,7 @@ export class Notify { * @param info 通知信息 */ public notifySingle(info: NotifyInfo) { - const isElectron = - (process.env.IS_ELECTRON as unknown as boolean) && - window.require != undefined + const isElectron = runtimeData.tags.isElectron if (isElectron) { if (runtimeData.reader) runtimeData.reader.send('sys:sendNotice', info) @@ -92,9 +88,7 @@ export class Notify { * @param userId 用户 ID */ public closeAll(userId: string) { - const isElectron = - (process.env.IS_ELECTRON as unknown as boolean) && - window.require != undefined + const isElectron = runtimeData.tags.isElectron if (isElectron) { if (runtimeData.reader) runtimeData.reader.send('sys:closeAllNotice', userId) @@ -113,9 +107,7 @@ export class Notify { * 关闭所有通知 */ public clear() { - const isElectron = - (process.env.IS_ELECTRON as unknown as boolean) && - window.require != undefined + const isElectron = runtimeData.tags.isElectron if (isElectron) { if (runtimeData.reader) runtimeData.reader.send('sys:clearNotice') } else { @@ -133,9 +125,7 @@ export class Notify { * @param tag 通知标签 */ private close(tag: string) { - const isElectron = - (process.env.IS_ELECTRON as unknown as boolean) && - window.require != undefined + const isElectron = runtimeData.tags.isElectron if (isElectron) { if (runtimeData.reader) runtimeData.reader.send('sys:closeNotice', tag) diff --git a/src/renderer/src/function/utils/appUtil.ts b/src/renderer/src/function/utils/appUtil.ts index dc47ae66..8b128ac9 100644 --- a/src/renderer/src/function/utils/appUtil.ts +++ b/src/renderer/src/function/utils/appUtil.ts @@ -74,8 +74,7 @@ export function openLink(url: string, external = false) { { text: app.config.globalProperties.$t('打开…'), fun: () => { - const electron = window.require('electron') - const shell = electron ? electron.shell : null + const shell = window.electron.shell if (shell) { shell.openExternal(url) } @@ -93,8 +92,7 @@ export function openLink(url: string, external = false) { } runtimeData.popBoxList.push(popInfo) } else { - const electron = window.require('electron') - const shell = electron ? electron.shell : null + const shell = window.electron.shell if (shell) { shell.openExternal(url) } @@ -266,7 +264,8 @@ export async function loadWinColor() { } export function updateWinColor(color: string) { - if (process.platform == 'win32') { + const process = window.electron.process + if (process && process.platform == 'win32') { const red = parseInt(color.substr(0, 2), 16) const green = parseInt(color.substr(2, 2), 16) const blue = parseInt(color.substr(4, 2), 16) @@ -796,7 +795,7 @@ export function loadJsonMap(name: string) { // UM:统计事件统一上传方法 export function sendStatEvent(event: string, data: any) { - if (!option.get('close_ga') && process.env.NODE_ENV == 'production') { + if (!option.get('close_ga') && import.meta.env.DEV) { Umami.trackEvent(event, data) } } diff --git a/src/renderer/src/function/utils/msgUtil.ts b/src/renderer/src/function/utils/msgUtil.ts index 9ddc6ced..c2e032d7 100644 --- a/src/renderer/src/function/utils/msgUtil.ts +++ b/src/renderer/src/function/utils/msgUtil.ts @@ -109,12 +109,12 @@ function replaceJPValue(jpStr: string) { export function getFace(id: number) { const pathList = import.meta.glob('@renderer/assets/img/qq-face/public/*/s*.*') for(const path in pathList) { - if (path.includes(`/${id}.gif`)) { + if (path.includes(`/s${id}.gif`)) { return path } } for(const path in pathList) { - if (path.includes(`/${id}.png`)) { + if (path.includes(`/s${id}.png`)) { return path } } diff --git a/src/renderer/src/function/utils/systemUtil.ts b/src/renderer/src/function/utils/systemUtil.ts index 7011ee58..05984431 100644 --- a/src/renderer/src/function/utils/systemUtil.ts +++ b/src/renderer/src/function/utils/systemUtil.ts @@ -1,6 +1,7 @@ import app from '@renderer/main' import l10nConfig from '@renderer/assets/l10n/_l10nconfig.json' +import PO from 'pofile' /** * 区分安卓、iOS、MacOS 和其他 @@ -38,23 +39,18 @@ export function getTrueLang(): string { * @param name 文件名 */ export function getPortableFileLang(name: string) { - // // eslint-disable-next-line @typescript-eslint/no-var-requires - // const file = require(`@renderer/assets/l10n/${name}.po`) - // const pot = getText.po.parse(file.default) - // // 将翻译平铺为一个对象 {msgid: msgstr} - // const back = {} as Record - // for (const item of Object.keys(pot.translations[''])) { - // if (item !== '') { - // // 如果不存在则不添加,防止影响到 vue-i18n 的 fallback - // if (pot.translations[''][item].msgstr) { - // back[item] = - // pot.translations[''][item].msgstr[0] != '' - // ? pot.translations[''][item].msgstr[0] - // : item - // } - // } - // } - return {} + const files = import.meta.glob('@renderer/assets/l10n/*.po', { eager: true, as: 'raw' }) + const filePath = Object.keys(files).find( + (item) => item.includes(name)) + const final = {} as { [key: string]: string } + if(filePath) { + const file = files[filePath] + const items = PO.parse(file).items + for(const item of items) { + final[item.msgid] = item.msgstr[0] == '' ? item.msgid : item.msgstr[0] + } + } + return final } /** diff --git a/src/renderer/src/main.ts b/src/renderer/src/main.ts index 6dca0d6a..1db070d3 100644 --- a/src/renderer/src/main.ts +++ b/src/renderer/src/main.ts @@ -70,7 +70,7 @@ const colorList = [ const color = colorList[Math.floor(Math.random() * colorList.length)] const str = strList[Math.floor(Math.random() * strList.length)] console.log( - `%c${str}%c Stapxs QQ Lite - ${packageInfo.version} ( ${process.env.NODE_ENV} ) `, + `%c${str}%c Stapxs QQ Lite - ${packageInfo.version} ( ${import.meta.env.DEV ? 'development' : 'production'} ) `, `font-weight:bold;background:#${color};color:#fff;border-radius:7px 0 0 7px;padding:7px 14px;margin:7px 0 7px 7px;`, 'background:#e3e8ec;color:#000;border-radius:0 7px 7px 0;display:inline-block;padding:7px 14px;margin:7px 7px 7px 0;', ) diff --git a/src/renderer/src/pages/Chat.vue b/src/renderer/src/pages/Chat.vue index 7762de19..b5f782dc 100644 --- a/src/renderer/src/pages/Chat.vue +++ b/src/renderer/src/pages/Chat.vue @@ -288,13 +288,7 @@ + :src="getFace(context.face_index)"> diff --git "a/src/renderer/src/pages/chat-view/Chat\347\273\210\347\253\257.vue" "b/src/renderer/src/pages/chat-view/Chat\347\273\210\347\253\257.vue" index 431af127..ff681a89 100644 --- "a/src/renderer/src/pages/chat-view/Chat\347\273\210\347\253\257.vue" +++ "b/src/renderer/src/pages/chat-view/Chat\347\273\210\347\253\257.vue" @@ -206,7 +206,7 @@ getMsgRawTxt: getMsgRawTxt, popInfo: new PopInfo(), packageInfo: packageInfo, - runMode: process.env.NODE_ENV, + runMode: import.meta.env.DEV, timeLoad: markRaw({ time: Intl.DateTimeFormat(getTrueLang(), { hour: 'numeric', diff --git a/src/renderer/src/pages/chat-view/SystemNotice.vue b/src/renderer/src/pages/chat-view/SystemNotice.vue index cae45c8e..b3c0cfad 100644 --- a/src/renderer/src/pages/chat-view/SystemNotice.vue +++ b/src/renderer/src/pages/chat-view/SystemNotice.vue @@ -100,7 +100,7 @@
+ v-show="dev">
@@ -133,7 +133,7 @@ return { trueLang: getTrueLang(), runtimeData: runtimeData, - NODE_ENV: process.env.NODE_ENV, + dev: import.meta.env.DEV, } }, methods: { diff --git a/src/renderer/src/pages/options/OptDev.vue b/src/renderer/src/pages/options/OptDev.vue index 248f603a..5958da00 100644 --- a/src/renderer/src/pages/options/OptDev.vue +++ b/src/renderer/src/pages/options/OptDev.vue @@ -356,7 +356,8 @@ runtimeData.reader && runtimeData.tags.release ) { - switch (process.platform) { + const process = window.electron.process + switch (process && process.platform) { case 'linux': { // archlinux if ( @@ -392,7 +393,6 @@ info += 'Application Info:\n' info += ` Uptime -> ${Math.floor(((new Date().getTime() - uptime) / 1000) * 100) / 100} s\n` info += ` Package Version -> ${packageInfo.version}\n` - info += ` Runtime env -> ${process.env.NODE_ENV}\n` info += ` Service Work -> ${runtimeData.tags.sw}\n` info += 'Backend Info:\n' diff --git a/src/renderer/src/pages/options/OptFunction.vue b/src/renderer/src/pages/options/OptFunction.vue index f3dfa70b..36630f4f 100644 --- a/src/renderer/src/pages/options/OptFunction.vue +++ b/src/renderer/src/pages/options/OptFunction.vue @@ -261,8 +261,8 @@ } }, showStatus() { - if (process.env.VUE_APP_MU_SHARE) { - openLink(process.env.VUE_APP_MU_SHARE, true) + if (import.meta.env.VITE_APP_MU_SHARE) { + openLink(import.meta.env.VITE_APP_MU_SHARE, true) } }, }, diff --git a/src/renderer/src/registerServiceWorker.ts b/src/renderer/src/registerServiceWorker.ts index b12196b4..2b5ed810 100644 --- a/src/renderer/src/registerServiceWorker.ts +++ b/src/renderer/src/registerServiceWorker.ts @@ -8,8 +8,8 @@ import { runtimeData } from './function/msg' const popInfo = new PopInfo() const logger = new Logger() -if (process.env.NODE_ENV === 'production') { - register(`${process.env.BASE_URL}sw.js`, { +if (import.meta.env.DEV) { + register('/sw.js', { ready() { logger.debug( app.config.globalProperties.$t( diff --git a/ssqq-web/.gitignore b/ssqq-web/.gitignore new file mode 100644 index 00000000..333d3e3d --- /dev/null +++ b/ssqq-web/.gitignore @@ -0,0 +1,2 @@ +dist +bin \ No newline at end of file diff --git a/ssqq-web/.npmignore b/ssqq-web/.npmignore new file mode 100644 index 00000000..a64dbee3 --- /dev/null +++ b/ssqq-web/.npmignore @@ -0,0 +1,3 @@ +node_modules +.gitignore +yarn.lock \ No newline at end of file diff --git a/ssqq-web/index.ts b/ssqq-web/index.ts new file mode 100644 index 00000000..d7761c33 --- /dev/null +++ b/ssqq-web/index.ts @@ -0,0 +1,95 @@ +#!/usr/bin/env node + +import * as http from 'http' +import * as fs from 'fs' +import Logger from 'log4js' + +import { checkUpdate } from './update' + +// 日志配置 +const logger = Logger.getLogger('index') +logger.level = 'info' + +let hostname +let port + +// 获取输入参数 +// 参数包括 hostname、port 和 help +const argv = process.argv.slice(2).reduce((acc, cur) => { + const [key, value] = cur.split('=') + acc[key] = value || true + return acc +}, {}) +if(argv['hostname']) { + hostname = argv['hostname'] +} +if(argv['port']) { + port = argv['port'] +} +if(argv['help'] || !hostname || !port) { + // eslint-disable-next-line no-console + console.log(` + Stapxs QQ Lite 网页服务工具: + --hostname 指定服务运行的主机名 + --port 指定服务运行的端口 + --help 查看帮助 + + 主仓库:https://github.com/Stapxs/Stapxs-QQ-Lite-2.0 + 网页服务工具:https://www.npmjs.com/package/ssqq-web + + ** 欢迎 star 项目,如果有问题请在主仓库提 issue ** + `) + process.exit(0) +} + +logger.info('正在初始化...') +let localVersion = '0.0.1' +// 检查是否存在 dist 文件夹 +if (fs.existsSync('./dist')) { + try { + // 获取文件夹中的 package.json + const packageJson = fs.readFileSync('./dist/package.json', 'utf-8') + localVersion = JSON.parse(packageJson).version + logger.info(`本地版本号为: ${localVersion}`) + } catch (err) { + logger.error(`读取 package.json 失败: ${err}`) + } +} + +// 检查更新 +checkUpdate(localVersion) + +// 创建服务 +const server = http.createServer((req, res) => { + const url = req.url + if (url === '/') { + // 根路径下的请求映射到 dist 文件夹下的 index.html + fs.readFile('./dist/index.html', (err, data) => { + if (err) { + logger.error(`读取 index.html 失败: ${err}`) + res.statusCode = 404 + res.end() + } else { + res.statusCode = 200 + res.setHeader('Content-Type', 'text/html') + res.end(data) + } + }) + } else { + // 其他路径直接映射到 dist 文件夹下 + fs.readFile(`./dist${url}`, (err, data) => { + if (err) { + logger.error(`读取 ${url} 失败: ${err}`) + res.statusCode = 404 + res.end() + } else { + res.statusCode = 200 + res.end(data) + } + }) + } +}) + +server.listen(port, hostname, () => { + logger.info(`服务于 http://${hostname}:${port} 运行`) +}) \ No newline at end of file diff --git a/ssqq-web/package.json b/ssqq-web/package.json new file mode 100644 index 00000000..2fa27ea9 --- /dev/null +++ b/ssqq-web/package.json @@ -0,0 +1,26 @@ +{ + "name": "ssqq-web", + "version": "1.0.3", + "description": "一个兼容 OneBot 的非官方网页版 QQ 客户端,Web 端快速启动包。", + "main": "index.ts", + "bin": { + "ssqq-web": "bin/index.js" + }, + "scripts": { + "start": "tsc && node index.js", + "build": "tsc", + "test": "tsc && node index.js help" + }, + "repository": "https://github.com/Stapxs/Stapxs-QQ-Lite-2.0", + "author": "Stapx Steve [林槐]", + "license": "Apache-2.0", + "private": false, + "dependencies": { + "log4js": "^6.9.1", + "request": "^2.88.2", + "semver-compare": "^1.0.0", + "typescript": "^5.6.3", + "unzipper": "^0.12.3" + }, + "devDependencies": {} +} diff --git a/ssqq-web/tsconfig.json b/ssqq-web/tsconfig.json new file mode 100644 index 00000000..d80d4c52 --- /dev/null +++ b/ssqq-web/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "outDir": "bin" + } +} \ No newline at end of file diff --git a/ssqq-web/update.ts b/ssqq-web/update.ts new file mode 100644 index 00000000..aa56d0fe --- /dev/null +++ b/ssqq-web/update.ts @@ -0,0 +1,64 @@ +#!/usr/bin/env node + +import cmp from 'semver-compare' +import Logger from 'log4js' +import request from 'request' +import unzipper from 'unzipper' +import fs from 'fs' + +// 日志配置 +const logger = Logger.getLogger('update') +logger.level = 'info' + + +export function checkUpdate(nowVersion: string) { + // 检查本体版本 + fetch('https://api.github.com/repos/Stapxs/Stapxs-QQ-Lite-2.0/releases/latest') + .then(res => res.json()) + .then(json => { + const version = json.tag_name.slice(1) + logger.info(`Stapxs QQ Lite 当前版本: ${version}, 本地版本: ${nowVersion}`) + if (cmp(nowVersion, version) === -1) { + // 本地版本小于线上版本, 需要更新 + logger.info('发现新版本, 正在更新...') + logger.info(`运行路径: ${process.cwd()}`) + // 下载新版本 + const assetList = json.assets + assetList.forEach(asset => { + const name = asset.name + // 寻找结尾为 -web.zip 的文件 + if (name.endsWith('-web.zip')) { + // 删除 dist 文件夹 + if(fs.existsSync('./dist')) { + fs.rm('./dist', { recursive: true }, err => { + if (err) { + logger.error(`删除 dist 文件夹失败: ${err}`) + } + } + ) + } + const downloadUrl = asset.browser_download_url + logger.info(`下载地址: ${downloadUrl}`) + // 下载文件并解压 + request(downloadUrl).pipe(unzipper.Extract({ path: './' })) + .on('close', () => { + logger.info('更新完成') + // 保存版本缓存 + fs.writeFile('./dist/package.json', JSON.stringify({ + version: version + }), err => { + if (err) { + logger.error(`保存版本缓存失败: ${err}`) + } + }) + }) + } + }) + } else { + logger.info('当前已是最新版本') + } + }) + .catch(err => { + logger.error(`检查更新失败: ${err}`) + }) +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 040c86ef..a641af84 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3324,6 +3324,11 @@ plist@^3.0.4, plist@^3.0.5: base64-js "^1.5.1" xmlbuilder "^15.1.1" +pofile@^1.1.4: + version "1.1.4" + resolved "https://registry.npmmirror.com/pofile/-/pofile-1.1.4.tgz#eab7e29f5017589b2a61b2259dff608c0cad76a2" + integrity sha512-r6Q21sKsY1AjTVVjOuU02VYKVNQGJNQHjTIvs4dEbeuuYfxgYk/DGD2mqqq4RDaVkwdSq0VEtmQUOPe/wH8X3g== + postcss-selector-parser@^6.0.15: version "6.1.2" resolved "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de"