diff --git a/.env b/.env new file mode 100644 index 0000000..a439f2d --- /dev/null +++ b/.env @@ -0,0 +1 @@ +PORT=9005 diff --git a/package-lock.json b/package-lock.json index dc31b1e..0cabdbe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "dayjs": "^1.11.13", + "dotenv": "^16.4.5", "ws": "^8.18.0" }, "bin": { @@ -1885,6 +1887,11 @@ "node": ">= 10" } }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" + }, "node_modules/debug": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", @@ -2088,12 +2095,14 @@ } }, "node_modules/dotenv": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-9.0.2.tgz", - "integrity": "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==", - "dev": true, + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", "engines": { - "node": ">=10" + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" } }, "node_modules/dotenv-expand": { @@ -4109,6 +4118,15 @@ "node": ">=12.0.0" } }, + "node_modules/read-config-file/node_modules/dotenv": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-9.0.2.tgz", + "integrity": "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/read-pkg": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", diff --git a/package.json b/package.json index 324dbc9..74b753c 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "sdserver", "version": "1.0.0", - "main": "dist/main.js", + "main": "dist/electron/main.js", "scripts": { "start": "electron .", - "build": "tsc && copyfiles -u 1 src/index.html dist/", + "build": "tsc && copyfiles -u 1 src/electron/index.html dist/ && copyfiles -u 1 src/electron/index.css dist/", "pack": "electron-packager . SDServer --platform=win32 --arch=x64 --out=dist/", "buildexe": "npx electron-builder", "watch": "nodemon" @@ -21,6 +21,8 @@ } }, "dependencies": { + "dayjs": "^1.11.13", + "dotenv": "^16.4.5", "ws": "^8.18.0" }, "devDependencies": { @@ -48,4 +50,4 @@ "author": "", "license": "ISC", "description": "" -} \ No newline at end of file +} diff --git a/src/electron/index.css b/src/electron/index.css new file mode 100644 index 0000000..10d9a0c --- /dev/null +++ b/src/electron/index.css @@ -0,0 +1,38 @@ +/* index.css */ +body { + font-family: Arial, sans-serif; + margin: 0; + padding: 0; + display: flex; + height: 100vh; + } + + #log-container { + width: 70%; + background-color: black; + color: green; + padding: 10px; + overflow-y: scroll; /* 允许垂直滚动 */ + white-space: pre-wrap; + font-family: 'Courier New', Courier, monospace; + box-sizing: border-box; /* 包括 padding 在内的计算方式 */ + } + + #control-panel { + width: 30%; + padding: 20px; + box-sizing: border-box; + background-color: #f0f0f0; + display: flex; + flex-direction: column; + align-items: flex-start; + } + + button { + margin-top: 10px; + } + + input { + margin-bottom: 10px; + } + \ No newline at end of file diff --git a/src/electron/index.html b/src/electron/index.html new file mode 100644 index 0000000..bb569a7 --- /dev/null +++ b/src/electron/index.html @@ -0,0 +1,29 @@ + + + + + SD Server Control + + + + + + +
+
+
+ +
+

Control Panel

+ + + + + +

Status: Waiting for actions...

+
+ + + + + \ No newline at end of file diff --git a/src/main.ts b/src/electron/main.ts similarity index 62% rename from src/main.ts rename to src/electron/main.ts index 5a98f33..d6e55d5 100644 --- a/src/main.ts +++ b/src/electron/main.ts @@ -1,10 +1,14 @@ // src/main.ts +import dotenv from 'dotenv'; import { app, BrowserWindow, ipcMain } from 'electron'; import * as path from 'path'; import { WebSocketServer } from 'ws'; +// Load environment variables from .env file +dotenv.config(); + let server: WebSocketServer | null = null; -let port = 8080; // 默认端口 +let port: number = process.env.PORT ? parseInt(process.env.PORT, 10) : 8080; // 默认端口为 8080 function createWindow() { const win = new BrowserWindow({ @@ -20,13 +24,28 @@ function createWindow() { }); win.loadFile(path.join(__dirname, 'index.html')); - win.webContents.openDevTools(); + // win.webContents.openDevTools(); return win; } let mainWindow: BrowserWindow | null = null; +// Create a function to send log messages to the renderer +function sendLogToRenderer(window: BrowserWindow, message: string) { + window.webContents.send('log-message', message); +} + +// 重寫 console.log 方法以便將訊息發送到渲染進程 +const originalConsoleLog = console.log; +console.log = (...args: any[]) => { + const message = args.join(' '); + originalConsoleLog(message); // 保留原始行為 + if (BrowserWindow.getAllWindows().length > 0) { + sendLogToRenderer(BrowserWindow.getAllWindows()[0], message); // 發送訊息到渲染進程 + } +}; + app.whenReady().then(() => { mainWindow = createWindow(); @@ -53,13 +72,15 @@ ipcMain.on('start-websocket', (event, portNumber: number) => { server = new WebSocketServer({ port: port }); - server.on('connection', (socket) => { - console.log(`WebSocket server started on port ${port}`); + server.on('connection', (socket, request) => { + const ip = request.socket.remoteAddress.replace("::ffff:", "") || 'Unknown IP'; + console.log(`Client connected from IP: ${ip}`); socket.send('Welcome to the WebSocket server'); socket.on('message', (message: string) => { - console.log(`Received message: ${message}`); + console.log(`[RPC] 收到client呼叫: ${message}`); socket.send(`Server received your message: ${message}`); + console.log(`[RPC] 回傳client呼叫: ${message}`); }); socket.on('close', () => { @@ -67,7 +88,7 @@ ipcMain.on('start-websocket', (event, portNumber: number) => { }); }); - event.reply('websocket-status', `WebSocket server started on port ${port} 88`); + event.reply('websocket-status', `WebSocket server started on port ${port}`); }); // 关闭 WebSocket 服务器 diff --git a/src/preload.ts b/src/electron/preload.ts similarity index 67% rename from src/preload.ts rename to src/electron/preload.ts index 96255c8..44a1782 100644 --- a/src/preload.ts +++ b/src/electron/preload.ts @@ -5,5 +5,9 @@ contextBridge.exposeInMainWorld('electron', { startWebSocket: (port: number) => ipcRenderer.send('start-websocket', port), stopWebSocket: () => ipcRenderer.send('stop-websocket'), openDevTools: () => ipcRenderer.send('open-devtools'), - onWebSocketStatus: (callback: (message: string) => void) => ipcRenderer.on('websocket-status', (event, message) => callback(message)) + onWebSocketStatus: (callback: (message: string) => void) => ipcRenderer.on('websocket-status', (event, message) => callback(message)), + onLogMessage: (callback: (message: string) => void) => ipcRenderer.on('log-message', (event, message) => callback(message)), + env: { + PORT: process.env.PORT || '8080' // 提供環境變數 + } }); diff --git a/src/electron/renderer.ts b/src/electron/renderer.ts new file mode 100644 index 0000000..f954661 --- /dev/null +++ b/src/electron/renderer.ts @@ -0,0 +1,74 @@ +const maxLogs = 200; // 设置最大日志数量 +const logs: string[] = []; + + + +// 格式化时间戳的函数 +function formatDate(date: Date): string { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始 + const day = String(date.getDate()).padStart(2, '0'); + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + const seconds = String(date.getSeconds()).padStart(2, '0'); + + // return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`; + return `${hours}:${minutes}:${seconds}`; +} + +function updateLogDisplay() { + const logContainer = document.getElementById('log') as HTMLElement; + if (logContainer) { + logContainer.innerText = logs.join('\n'); + logContainer.scrollTop = 0; // 保持滚动位置在顶部 + } +} + +function addLog(message: string) { + const timestamp = formatDate(new Date()); // 使用当前时间生成时间戳 + const logMessage = `[${timestamp}] ${message}`; // 在日志消息前添加时间戳 + + logs.unshift(logMessage); // 在数组开头插入新消息 + + // 如果日志超过200条,删除最旧的一条(数组末尾的元素) + if (logs.length > maxLogs) { + logs.pop(); + } + + updateLogDisplay(); +} + +// 设置默认端口值 +const portInputElement = document.getElementById('port') as HTMLInputElement; +if (portInputElement) { + const defaultPort = window.electron.env.PORT; // 从预加载脚本中获取默认端口 + portInputElement.value = defaultPort; +} + +document.getElementById('startBtn')?.addEventListener('click', () => { + const portElement = document.getElementById('port') as HTMLInputElement; + const port = parseInt(portElement.value, 10); + window.electron.startWebSocket(port); +}); + +document.getElementById('stopBtn')?.addEventListener('click', () => { + window.electron.stopWebSocket(); +}); + +document.getElementById('devToolsBtn')?.addEventListener('click', () => { + window.electron.openDevTools(); +}); + +window.electron.onWebSocketStatus((message: string) => { + console.log(`WebSocket status: ${message}`); + const statusElement = document.getElementById('status'); + if (statusElement) { + statusElement.innerText = `Status: ${message}`; + } + addLog(message); // 將 WebSocket 狀態消息添加到日誌顯示區 +}); + +// 监听主进程发送的日志消息 +window.electron.onLogMessage((message: string) => { + addLog(message); // 將日誌消息添加到顯示區 +}); \ No newline at end of file diff --git a/src/global.d.ts b/src/global.d.ts index bbfaef0..ea3c59e 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -5,5 +5,7 @@ interface Window { stopWebSocket: () => void; openDevTools: () => void; onWebSocketStatus: (callback: (message: string) => void) => void; + onLogMessage: (callback: (message: string) => void) => void; // 添加這行以處理日誌消息 + env: { PORT: string; }; }; } diff --git a/src/index.html b/src/index.html deleted file mode 100644 index 77d3674..0000000 --- a/src/index.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - WebSocket Control - - - - - - -

SD Server Control

- - - - - - - -

Status: Waiting for actions...

- - - - \ No newline at end of file diff --git a/src/server.ts b/src/pkg/server.ts similarity index 100% rename from src/server.ts rename to src/pkg/server.ts diff --git a/src/renderer.ts b/src/renderer.ts deleted file mode 100644 index 961705a..0000000 --- a/src/renderer.ts +++ /dev/null @@ -1,51 +0,0 @@ -document.getElementById('startBtn')?.addEventListener('click', () => { - // console.log('Start WebSocket button clicked'); - const portElement = document.getElementById('port') as HTMLInputElement; - const port = parseInt(portElement.value, 10); - window.electron.startWebSocket(port); -}); - -document.getElementById('stopBtn')?.addEventListener('click', () => { - // console.log('Stop WebSocket button clicked'); - window.electron.stopWebSocket(); -}); - -document.getElementById('devToolsBtn')?.addEventListener('click', () => { - // console.log('Open DevTools button clicked'); - window.electron.openDevTools(); -}); - -window.electron.onWebSocketStatus((message: string) => { - console.log(`WebSocket status: ${message}`); - const statusElement = document.getElementById('status'); - if (statusElement) { - statusElement.innerText = `Status: ${message}`; - } -}); - - -// document.getElementById('startBtn')?.addEventListener('click', () => { -// // console.log('Start WebSocket button clicked'); -// const portElement = document.getElementById('port') as HTMLInputElement; -// const port = parseInt(portElement.value, 10); -// ipcRenderer.send('start-websocket', port); -// }); - -// document.getElementById('stopBtn')?.addEventListener('click', () => { -// // console.log('Stop WebSocket button clicked'); -// ipcRenderer.send('stop-websocket'); -// }); - -// document.getElementById('devToolsBtn')?.addEventListener('click', () => { -// // console.log('Open DevTools button clicked'); -// ipcRenderer.send('open-devtools'); -// }); - -// function onWebSocketStatus(callback: (message: string) => void) { -// ipcRenderer.on('websocket-status', (event, message) => callback(message)); -// } - -// // 監聽 WebSocket 狀態 -// onWebSocketStatus((message) => { -// console.log('WebSocket status:', message); -// }); \ No newline at end of file