diff --git a/package.json b/package.json
index 10a0c76..667e78e 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
   "main": "dist/main.js",
   "scripts": {
     "test": "echo \"Error: no test specified\" && exit 1",
-    "postinstall": "set ELECTRON_SKIP_BINARY_DOWNLOAD=1 && npm install electron --no-save",
+    "postinstall": "ELECTRON_SKIP_BINARY_DOWNLOAD=1 && npm install electron --no-save",
     "build": "npm run build-main && npm run build-preload && npm run build-renderer",
     "build-main": "webpack --config webpack.main.config.js",
     "build-preload": "webpack --config webpack.preload.config.js",
@@ -19,7 +19,6 @@
   "license": "ISC",
   "dependencies": {
     "express": "^4.18.2",
-    "express-ws": "^5.0.2",
     "json-bigint": "^1.0.0",
     "uuid": "^9.0.1",
     "ws": "^8.16.0"
diff --git a/src/common/config.ts b/src/common/config.ts
index 762a04b..12050b7 100644
--- a/src/common/config.ts
+++ b/src/common/config.ts
@@ -1,4 +1,5 @@
-import {Config} from "./types";
+import {Config, OB11Config} from "./types";
+import {mergeNewProperties} from "./utils";
 
 const fs = require("fs");
 
@@ -8,13 +9,25 @@ export class ConfigUtil {
     constructor(configPath: string) {
         this.configPath = configPath;
     }
-
-    getConfig(): Config {
-        let defaultConfig: Config = {
+    getConfig(){
+        try {
+            return this._getConfig()
+        }catch (e) {
+            console.log("获取配置文件出错", e)
+        }
+    }
+    _getConfig(): Config {
+        let ob11Default: OB11Config = {
             httpPort: 3000,
             httpHosts: [],
             wsPort: 3001,
             wsHosts: [],
+            enableWs: true,
+            enableWsReverse: true
+        }
+        let defaultConfig: Config = {
+            ob11: ob11Default,
+            heartInterval: 5000,
             token: "",
             enableBase64: false,
             debug: false,
@@ -28,28 +41,29 @@ export class ConfigUtil {
             let jsonData: Config = defaultConfig;
             try {
                 jsonData = JSON.parse(data)
+            } catch (e) {
             }
-            catch (e) {}
-            if (!jsonData.httpHosts) {
-                jsonData.httpHosts = []
-            }
-            if (!jsonData.wsHosts) {
-                jsonData.wsHosts = []
-            }
-            if (!jsonData.wsPort) {
-                jsonData.wsPort = 3001
-            }
-            if (!jsonData.httpPort) {
-                jsonData.httpPort = 3000
-            }
-            if (!jsonData.token) {
-                jsonData.token = ""
-            }
+            mergeNewProperties(defaultConfig, jsonData);
+            this.checkOldConfig(jsonData.ob11, jsonData, "httpPort", "port");
+            this.checkOldConfig(jsonData.ob11, jsonData, "httpHosts", "hosts");
+            this.checkOldConfig(jsonData.ob11, jsonData, "wsPort", "wsPort");
+            console.log("get config", jsonData);
             return jsonData;
         }
     }
-  
+
     setConfig(config: Config) {
         fs.writeFileSync(this.configPath, JSON.stringify(config, null, 2), "utf-8")
     }
+
+    private checkOldConfig(currentConfig: Config | OB11Config,
+                           oldConfig: Config | OB11Config,
+                           currentKey: string, oldKey: string) {
+        // 迁移旧的配置到新配置,避免用户重新填写配置
+        const oldValue = oldConfig[oldKey];
+        if (oldValue) {
+            currentConfig[currentKey] = oldValue;
+            delete oldConfig[oldKey];
+        }
+    }
 }
diff --git a/src/common/data.ts b/src/common/data.ts
index ce94b06..e070a41 100644
--- a/src/common/data.ts
+++ b/src/common/data.ts
@@ -88,4 +88,3 @@ export function getStrangerByUin(uin: string) {
 }
 
 export const version = "v3.4.0"
-export const heartInterval = 15000 // 毫秒
\ No newline at end of file
diff --git a/src/common/types.ts b/src/common/types.ts
index abc2b88..1952099 100644
--- a/src/common/types.ts
+++ b/src/common/types.ts
@@ -1,4 +1,4 @@
-export interface Config {
+export interface OB11Config {
     httpPort: number
     httpHosts: string[]
     wsPort: number
@@ -7,7 +7,12 @@ export interface Config {
     enableHttpPost?: boolean
     enableWs?: boolean
     enableWsReverse?: boolean
+}
+
+export interface Config {
+    ob11: OB11Config
     token?: string
+    heartInterval?: number  // ms
     enableBase64?: boolean
     debug?: boolean
     reportSelfMessage?: boolean
diff --git a/src/common/utils.ts b/src/common/utils.ts
index 2a1f67d..43c77fe 100644
--- a/src/common/utils.ts
+++ b/src/common/utils.ts
@@ -97,3 +97,22 @@ export async function file2base64(path: string){
     }
     return result;
 }
+
+
+// 在保证老对象已有的属性不变化的情况下将新对象的属性复制到老对象
+export function mergeNewProperties(newObj: any, oldObj: any) {
+    Object.keys(newObj).forEach(key => {
+        // 如果老对象不存在当前属性,则直接复制
+        if (!oldObj.hasOwnProperty(key)) {
+            oldObj[key] = newObj[key];
+        } else {
+            // 如果老对象和新对象的当前属性都是对象,则递归合并
+            if (typeof oldObj[key] === 'object' && typeof newObj[key] === 'object') {
+                mergeNewProperties(newObj[key], oldObj[key]);
+            } else if(typeof oldObj[key] === 'object' || typeof newObj[key] === 'object'){
+                // 属性冲突,有一方不是对象,直接覆盖
+                oldObj[key] = newObj[key];
+            }
+        }
+    });
+}
\ No newline at end of file
diff --git a/src/main/main.ts b/src/main/main.ts
index 009b7aa..ad8be04 100644
--- a/src/main/main.ts
+++ b/src/main/main.ts
@@ -3,7 +3,7 @@
 import {BrowserWindow, ipcMain} from 'electron';
 
 import {Config} from "../common/types";
-import {postMsg, setToken, startHTTPServer, initWebsocket} from "../onebot11/server";
+import {postMsg, initWebsocket} from "../onebot11/server";
 import {CHANNEL_GET_CONFIG, CHANNEL_LOG, CHANNEL_SET_CONFIG,} from "../common/channels";
 import {CONFIG_DIR, getConfigUtil, log} from "../common/utils";
 import {addHistoryMsg, getGroupMember, msgHistory, selfInfo} from "../common/data";
@@ -13,6 +13,7 @@ import {NTQQApi} from "../ntqqapi/ntcall";
 import {ChatType, RawMessage} from "../ntqqapi/types";
 import {OB11FriendRecallNoticeEvent} from "../onebot11/event/notice/OB11FriendRecallNoticeEvent";
 import {OB11GroupRecallNoticeEvent} from "../onebot11/event/notice/OB11GroupRecallNoticeEvent";
+import {ob11HTTPServer} from "../onebot11/server/http";
 
 const fs = require('fs');
 
@@ -29,19 +30,26 @@ function onLoad() {
         fs.mkdirSync(CONFIG_DIR, {recursive: true});
     }
     ipcMain.handle(CHANNEL_GET_CONFIG, (event: any, arg: any) => {
-        return getConfigUtil().getConfig();
+        try {
+            return getConfigUtil().getConfig();
+        }catch (e) {
+            console.log("获取配置文件出错", e)
+        }
     })
     ipcMain.on(CHANNEL_SET_CONFIG, (event: any, arg: Config) => {
         let oldConfig = getConfigUtil().getConfig();
         getConfigUtil().setConfig(arg)
-        if (arg.httpPort != oldConfig.httpPort) {
-            startHTTPServer(arg.httpPort)
+        if (arg.ob11.httpPort != oldConfig.ob11.httpPort && arg.ob11.enableHttp) {
+            ob11HTTPServer.restart(arg.ob11.httpPort);
         }
-        if (arg.wsPort != oldConfig.wsPort) {
-            initWebsocket(arg.wsPort)
+        if (!arg.ob11.enableHttp){
+            ob11HTTPServer.stop()
         }
-        if (arg.token != oldConfig.token) {
-            setToken(arg.token);
+        else{
+            ob11HTTPServer.start(arg.ob11.httpPort);
+        }
+        if (arg.ob11.wsPort != oldConfig.ob11.wsPort) {
+            initWebsocket(arg.ob11.wsPort)
         }
     })
 
@@ -125,11 +133,13 @@ function onLoad() {
             }
         })
         NTQQApi.getGroups(true).then()
-      
         const config = getConfigUtil().getConfig()
-        startHTTPServer(config.httpPort)
-        initWebsocket(config.wsPort);
-        setToken(config.token)
+        try {
+            ob11HTTPServer.start(config.ob11.httpPort)
+            initWebsocket(config.ob11.wsPort);
+        }catch (e) {
+            console.log("start failed", e)
+        }
         log("LLOneBot start")
     }
 
diff --git a/src/onebot11/actions/BaseAction.ts b/src/onebot11/action/BaseAction.ts
similarity index 100%
rename from src/onebot11/actions/BaseAction.ts
rename to src/onebot11/action/BaseAction.ts
diff --git a/src/onebot11/actions/CanSendImage.ts b/src/onebot11/action/CanSendImage.ts
similarity index 100%
rename from src/onebot11/actions/CanSendImage.ts
rename to src/onebot11/action/CanSendImage.ts
diff --git a/src/onebot11/actions/CanSendRecord.ts b/src/onebot11/action/CanSendRecord.ts
similarity index 100%
rename from src/onebot11/actions/CanSendRecord.ts
rename to src/onebot11/action/CanSendRecord.ts
diff --git a/src/onebot11/actions/DeleteMsg.ts b/src/onebot11/action/DeleteMsg.ts
similarity index 100%
rename from src/onebot11/actions/DeleteMsg.ts
rename to src/onebot11/action/DeleteMsg.ts
diff --git a/src/onebot11/actions/GetFriendList.ts b/src/onebot11/action/GetFriendList.ts
similarity index 100%
rename from src/onebot11/actions/GetFriendList.ts
rename to src/onebot11/action/GetFriendList.ts
diff --git a/src/onebot11/actions/GetGroupInfo.ts b/src/onebot11/action/GetGroupInfo.ts
similarity index 100%
rename from src/onebot11/actions/GetGroupInfo.ts
rename to src/onebot11/action/GetGroupInfo.ts
diff --git a/src/onebot11/actions/GetGroupList.ts b/src/onebot11/action/GetGroupList.ts
similarity index 100%
rename from src/onebot11/actions/GetGroupList.ts
rename to src/onebot11/action/GetGroupList.ts
diff --git a/src/onebot11/actions/GetGroupMemberInfo.ts b/src/onebot11/action/GetGroupMemberInfo.ts
similarity index 100%
rename from src/onebot11/actions/GetGroupMemberInfo.ts
rename to src/onebot11/action/GetGroupMemberInfo.ts
diff --git a/src/onebot11/actions/GetGroupMemberList.ts b/src/onebot11/action/GetGroupMemberList.ts
similarity index 100%
rename from src/onebot11/actions/GetGroupMemberList.ts
rename to src/onebot11/action/GetGroupMemberList.ts
diff --git a/src/onebot11/actions/GetLoginInfo.ts b/src/onebot11/action/GetLoginInfo.ts
similarity index 100%
rename from src/onebot11/actions/GetLoginInfo.ts
rename to src/onebot11/action/GetLoginInfo.ts
diff --git a/src/onebot11/actions/GetMsg.ts b/src/onebot11/action/GetMsg.ts
similarity index 100%
rename from src/onebot11/actions/GetMsg.ts
rename to src/onebot11/action/GetMsg.ts
diff --git a/src/onebot11/actions/GetStatus.ts b/src/onebot11/action/GetStatus.ts
similarity index 100%
rename from src/onebot11/actions/GetStatus.ts
rename to src/onebot11/action/GetStatus.ts
diff --git a/src/onebot11/actions/GetVersionInfo.ts b/src/onebot11/action/GetVersionInfo.ts
similarity index 100%
rename from src/onebot11/actions/GetVersionInfo.ts
rename to src/onebot11/action/GetVersionInfo.ts
diff --git a/src/onebot11/actions/SendGroupMsg.ts b/src/onebot11/action/SendGroupMsg.ts
similarity index 100%
rename from src/onebot11/actions/SendGroupMsg.ts
rename to src/onebot11/action/SendGroupMsg.ts
diff --git a/src/onebot11/actions/SendMsg.ts b/src/onebot11/action/SendMsg.ts
similarity index 84%
rename from src/onebot11/actions/SendMsg.ts
rename to src/onebot11/action/SendMsg.ts
index cc84d23..e96ab0a 100644
--- a/src/onebot11/actions/SendMsg.ts
+++ b/src/onebot11/action/SendMsg.ts
@@ -9,6 +9,41 @@ import BaseAction from "./BaseAction";
 import {ActionName} from "./types";
 import * as fs from "fs";
 
+function checkSendMessage(sendMsgList: OB11MessageData[]) {
+    function checkUri(uri: string): boolean {
+        const pattern = /^(file:\/\/|http:\/\/|https:\/\/|base64:\/\/)/;
+        return pattern.test(uri);
+    }
+
+    for (let msg of sendMsgList) {
+        if (msg["type"] && msg["data"]) {
+            let type = msg["type"];
+            let data = msg["data"];
+            if (type === "text" && !data["text"]) {
+                return 400;
+            } else if (["image", "voice", "record"].includes(type)) {
+                if (!data["file"]) {
+                    return 400;
+                } else {
+                    if (checkUri(data["file"])) {
+                        return 200;
+                    } else {
+                        return 400;
+                    }
+                }
+
+            } else if (type === "at" && !data["qq"]) {
+                return 400;
+            } else if (type === "reply" && !data["id"]) {
+                return 400;
+            }
+        } else {
+            return 400
+        }
+    }
+    return 200;
+}
+
 export interface ReturnDataType {
     message_id: number
 }
diff --git a/src/onebot11/actions/SendPrivateMsg.ts b/src/onebot11/action/SendPrivateMsg.ts
similarity index 100%
rename from src/onebot11/actions/SendPrivateMsg.ts
rename to src/onebot11/action/SendPrivateMsg.ts
diff --git a/src/onebot11/actions/index.ts b/src/onebot11/action/index.ts
similarity index 100%
rename from src/onebot11/actions/index.ts
rename to src/onebot11/action/index.ts
diff --git a/src/onebot11/actions/types.ts b/src/onebot11/action/types.ts
similarity index 100%
rename from src/onebot11/actions/types.ts
rename to src/onebot11/action/types.ts
diff --git a/src/onebot11/actions/utils.ts b/src/onebot11/action/utils.ts
similarity index 100%
rename from src/onebot11/actions/utils.ts
rename to src/onebot11/action/utils.ts
diff --git a/src/onebot11/event/notice/OB11GroupRecallNoticeEvent.ts b/src/onebot11/event/notice/OB11GroupRecallNoticeEvent.ts
index 83c31d5..11c180a 100644
--- a/src/onebot11/event/notice/OB11GroupRecallNoticeEvent.ts
+++ b/src/onebot11/event/notice/OB11GroupRecallNoticeEvent.ts
@@ -7,7 +7,6 @@ export class OB11GroupRecallNoticeEvent extends OB11GroupNoticeEvent {
 
     constructor(groupId: number, userId: number, operatorId: number, messageId: number) {
         super();
-
         this.group_id = groupId;
         this.user_id = userId;
         this.operator_id = operatorId;
diff --git a/src/onebot11/server.ts b/src/onebot11/server.ts
index b2a2a7c..df87476 100644
--- a/src/onebot11/server.ts
+++ b/src/onebot11/server.ts
@@ -1,134 +1,24 @@
-import * as http from "http";
 import * as websocket from "ws";
 import urlParse from "url";
-import express, {Request, Response} from "express";
 import {getConfigUtil, log} from "../common/utils";
-import {heartInterval, selfInfo} from "../common/data";
-import {OB11Message, OB11MessageData, OB11Return} from './types';
-import {actionHandlers, actionMap} from "./actions";
-import {OB11Response, OB11WebsocketResponse} from "./actions/utils";
+import {selfInfo} from "../common/data";
+import {OB11Message} from './types';
+import {actionMap} from "./action";
+import {OB11WebsocketResponse} from "./action/utils";
 import {callEvent, registerEventSender, unregisterEventSender} from "./event/manager";
 import {ReconnectingWebsocket} from "./ReconnectingWebsocket";
-import {ActionName} from "./actions/types";
+import {ActionName} from "./action/types";
 import {OB11BaseMetaEvent} from "./event/meta/OB11BaseMetaEvent";
 import {OB11BaseNoticeEvent} from "./event/notice/OB11BaseNoticeEvent";
-import BaseAction from "./actions/BaseAction";
+import BaseAction from "./action/BaseAction";
 import {LifeCycleSubType, OB11LifeCycleEvent} from "./event/meta/OB11LifeCycleEvent";
 import {OB11HeartbeatEvent} from "./event/meta/OB11HeartbeatEvent";
 
-let accessToken = "";
 let heartbeatRunning = false;
-
-// @SiberianHusky 2021-08-15
-
-function checkSendMessage(sendMsgList: OB11MessageData[]) {
-    function checkUri(uri: string): boolean {
-        const pattern = /^(file:\/\/|http:\/\/|https:\/\/|base64:\/\/)/;
-        return pattern.test(uri);
-    }
-
-    for (let msg of sendMsgList) {
-        if (msg["type"] && msg["data"]) {
-            let type = msg["type"];
-            let data = msg["data"];
-            if (type === "text" && !data["text"]) {
-                return 400;
-            } else if (["image", "voice", "record"].includes(type)) {
-                if (!data["file"]) {
-                    return 400;
-                } else {
-                    if (checkUri(data["file"])) {
-                        return 200;
-                    } else {
-                        return 400;
-                    }
-                }
-
-            } else if (type === "at" && !data["qq"]) {
-                return 400;
-            } else if (type === "reply" && !data["id"]) {
-                return 400;
-            }
-        } else {
-            return 400
-        }
-    }
-    return 200;
-}
-
-// ==end==
-
-const JSONbig = require('json-bigint')({storeAsString: true});
-
-const expressAPP = express();
-expressAPP.use(express.urlencoded({extended: true, limit: "500mb"}));
-
-let httpServer: http.Server = null;
-
 let websocketServer = null;
 
-expressAPP.use((req, res, next) => {
-    let data = '';
-    req.on('data', chunk => {
-        data += chunk.toString();
-    });
-    req.on('end', () => {
-        if (data) {
-            try {
-                // log("receive raw", data)
-                req.body = JSONbig.parse(data);
-            } catch (e) {
-                return next(e);
-            }
-        }
-        next();
-    });
-});
-
-const expressAuthorize = (req: Request, res: Response, next: () => void) => {
-    let token = ""
-    const authHeader = req.get("authorization")
-    if (authHeader) {
-        token = authHeader.split("Bearer ").pop()
-        log("receive http header token", token)
-    } else if (req.query.access_token) {
-        if (Array.isArray(req.query.access_token)) {
-            token = req.query.access_token[0].toString();
-        } else {
-            token = req.query.access_token.toString();
-        }
-        log("receive http url token", token)
-    }
-
-    if (accessToken) {
-        if (token != accessToken) {
-            return res.status(403).send(JSON.stringify({message: 'token verify failed!'}));
-        }
-    }
-    next();
-
-};
-
-export function setToken(token: string) {
-    accessToken = token
-}
-
-export function startHTTPServer(port: number) {
-    if (httpServer) {
-        httpServer.close();
-    }
-    expressAPP.get('/', (req: Request, res: Response) => {
-        res.send('LLOneBot已启动');
-    })
-
-    if (getConfigUtil().getConfig().enableHttp) {
-        httpServer = expressAPP.listen(port, "0.0.0.0", () => {
-            console.log(`llonebot http service started 0.0.0.0:${port}`);
-        });
-    }
-}
-
 export function initWebsocket(port: number) {
+    const {heartInterval, ob11: {enableWs}, token} = getConfigUtil().getConfig()
     if (!heartbeatRunning) {
         setInterval(() => {
             callEvent(new OB11HeartbeatEvent(true, true, heartInterval));
@@ -136,8 +26,7 @@ export function initWebsocket(port: number) {
 
         heartbeatRunning = true;
     }
-
-    if (getConfigUtil().getConfig().enableWs) {
+    if (enableWs) {
         if (websocketServer) {
             websocketServer.close((err) => {
                 log("ws server close failed!", err)
@@ -150,28 +39,26 @@ export function initWebsocket(port: number) {
         websocketServer.on("connection", (ws, req) => {
             const url = req.url.split("?").shift();
             log("receive ws connect", url)
-            let token: string = ""
+            let clientToken: string = ""
             const authHeader = req.headers['authorization'];
             if (authHeader) {
-                token = authHeader.split("Bearer ").pop()
-                log("receive ws header token", token);
+                clientToken = authHeader.split("Bearer ").pop()
+                log("receive ws header token", clientToken);
             } else {
                 const parsedUrl = urlParse.parse(req.url, true);
                 const urlToken = parsedUrl.query.access_token;
                 if (urlToken) {
                     if (Array.isArray(urlToken)) {
-                        token = urlToken[0]
+                        clientToken = urlToken[0]
                     } else {
-                        token = urlToken
+                        clientToken = urlToken
                     }
-                    log("receive ws url token", token);
+                    log("receive ws url token", clientToken);
                 }
             }
-            if (accessToken) {
-                if (token != accessToken) {
-                    ws.send(JSON.stringify(OB11WebsocketResponse.res(null, "failed", 1403, "token验证失败")))
-                    return ws.close()
-                }
+            if (token && clientToken != token) {
+                ws.send(JSON.stringify(OB11WebsocketResponse.res(null, "failed", 1403, "token验证失败")))
+                return ws.close()
             }
 
             if (url == "/api" || url == "/api/" || url == "/") {
@@ -204,7 +91,7 @@ export function initWebsocket(port: number) {
 
                 try {
                     wsReply(ws, new OB11LifeCycleEvent(LifeCycleSubType.CONNECT))
-                } catch (e){
+                } catch (e) {
                     log("发送生命周期失败", e)
                 }
 
@@ -218,11 +105,12 @@ export function initWebsocket(port: number) {
 
     initReverseWebsocket();
 }
+
 function initReverseWebsocket() {
     const config = getConfigUtil().getConfig();
-    if (config.enableWsReverse) {
+    if (config.ob11.enableWsReverse) {
         console.log("Prepare to connect all reverse websockets...");
-        for (const url of config.wsHosts) {
+        for (const url of config.ob11.wsHosts) {
             new Promise(() => {
                 try {
                     let wsClient = new ReconnectingWebsocket(url);
@@ -239,7 +127,7 @@ function initReverseWebsocket() {
                     wsClient.onmessage = async function (msg) {
                         let receiveData: { action: ActionName, params: any, echo?: string } = {action: null, params: {}}
                         let echo = ""
-                        log("收到正向Websocket消息", msg.toString())
+                        log("收到反向Websocket消息", msg.toString())
                         try {
                             receiveData = JSON.parse(msg.toString())
                             echo = receiveData.echo
@@ -257,8 +145,7 @@ function initReverseWebsocket() {
                             wsReply(wsClient, OB11WebsocketResponse.error(`api处理出错:${e}`, 1200, echo))
                         }
                     }
-                }
-                catch (e) {
+                } catch (e) {
                     log(e.stack);
                 }
             }).then();
@@ -292,7 +179,7 @@ export function postMsg(msg: PostMsgType) {
             return
         }
     }
-    for (const host of config.httpHosts) {
+    for (const host of config.ob11.httpHosts) {
         fetch(host, {
             method: "POST",
             headers: {
@@ -310,33 +197,3 @@ export function postMsg(msg: PostMsgType) {
     log("新消息事件ws上报", msg);
     callEvent(msg);
 }
-
-
-function registerRouter(action: string, handle: (payload: any) => Promise<any>) {
-    let url = action.toString()
-    if (!action.startsWith("/")) {
-        url = "/" + action
-    }
-
-    async function _handle(res: Response, payload: any) {
-        log("receive post data", url, payload)
-        try {
-            const result = await handle(payload)
-            res.send(result)
-        } catch (e) {
-            log(e.stack);
-            res.send(OB11Response.error(e.stack.toString(), 200))
-        }
-    }
-
-    expressAPP.post(url, expressAuthorize, (req: Request, res: Response) => {
-        _handle(res, req.body || {}).then()
-    });
-    expressAPP.get(url, expressAuthorize, (req: Request, res: Response) => {
-        _handle(res, req.query as any || {}).then()
-    });
-}
-
-for (const action of actionHandlers) {
-    registerRouter(action.actionName, (payload) => action.handle(payload))
-}
\ No newline at end of file
diff --git a/src/onebot11/server/http.ts b/src/onebot11/server/http.ts
new file mode 100644
index 0000000..b7408a2
--- /dev/null
+++ b/src/onebot11/server/http.ts
@@ -0,0 +1,26 @@
+import {Response} from "express";
+import {getConfigUtil} from "../../common/utils";
+import {OB11Response} from "../action/utils";
+import {HttpServerBase} from "../../server/http";
+import {actionHandlers} from "../action";
+
+class OB11HTTPServer extends HttpServerBase {
+    name = "OneBot V11 server"
+    handleFailed(res: Response, payload: any, e: any) {
+        res.send(OB11Response.error(e.stack.toString(), 200))
+    }
+
+    protected listen(port: number) {
+        if (getConfigUtil().getConfig().ob11.enableHttp) {
+            super.listen(port);
+        }
+    }
+}
+
+export const ob11HTTPServer = new OB11HTTPServer();
+
+for (const action of actionHandlers) {
+    for(const method of ["post", "get"]){
+        ob11HTTPServer.registerRouter(method, action.actionName, (res, payload) => action.handle(payload))
+    }
+}
diff --git a/src/renderer.ts b/src/renderer.ts
index e673b44..aa70f15 100644
--- a/src/renderer.ts
+++ b/src/renderer.ts
@@ -5,40 +5,42 @@
 async function onSettingWindowCreated(view: Element) {
     window.llonebot.log("setting window created");
     let config = await window.llonebot.getConfig()
+    const httpClass = "http";
+    const httpPostClass = "http-post";
+    const wsClass = "ws";
+    const reverseWSClass = "reverse-ws";
 
     function createHttpHostEleStr(host: string) {
         let eleStr = `
-            <setting-item data-direction="row" class="hostItem vertical-list-item">
-                <h2>事件上报地址(http)</h2>
+            <setting-item data-direction="row" class="hostItem vertical-list-item ${httpPostClass}">
+                <h2>HTTP事件上报地址(http)</h2>
                 <input class="httpHost input-text" type="text" value="${host}" 
                 style="width:60%;padding: 5px"
                 placeholder="如果localhost上报失败试试局域网ip"/>
             </setting-item>
-
             `
         return eleStr
     }
 
     function createWsHostEleStr(host: string) {
         let eleStr = `
-            <setting-item data-direction="row" class="hostItem vertical-list-item">
+            <setting-item data-direction="row" class="hostItem vertical-list-item ${reverseWSClass}">
                 <h2>事件上报地址(反向websocket)</h2>
                 <input class="wsHost input-text" type="text" value="${host}" 
                 style="width:60%;padding: 5px"
                 placeholder="如果localhost上报失败试试局域网ip"/>
             </setting-item>
-
             `
         return eleStr
     }
 
     let httpHostsEleStr = ""
-    for (const host of config.httpHosts) {
+    for (const host of config.ob11.httpHosts) {
         httpHostsEleStr += createHttpHostEleStr(host);
     }
 
     let wsHostsEleStr = ""
-    for (const host of config.wsHosts) {
+    for (const host of config.ob11.wsHosts) {
         wsHostsEleStr += createWsHostEleStr(host);
     }
 
@@ -47,63 +49,63 @@ async function onSettingWindowCreated(view: Element) {
         <setting-section>
             <setting-panel>
                 <setting-list class="wrap">
-                    <setting-item class="vertical-list-item" data-direction="row">
+                    <setting-item data-direction="row" class="hostItem vertical-list-item">
+                        <div>
+                            <div>启用HTTP服务</div>
+                        </div>
+                        <setting-switch id="http" ${config.ob11.enableHttp ? "is-active" : ""}></setting-switch>
+                    </setting-item>
+                    <setting-item class="vertical-list-item ${httpClass}" data-direction="row" style="display: ${config.ob11.enableHttp ? '' : 'none'}">
                         <setting-text>HTTP监听端口</setting-text>
-                        <input id="httpPort" type="number" value="${config.httpPort}"/>
+                        <input id="httpPort" type="number" value="${config.ob11.httpPort}"/>
                     </setting-item>
-                    <div>
-                        <button id="addHttpHost" class="q-button">添加HTTP POST上报地址</button>
+                    <setting-item data-direction="row" class="hostItem vertical-list-item">
+                        <div>
+                            <div>启用HTTP事件上报</div>
+                        </div>
+                        <setting-switch id="httpPost" ${config.ob11.enableHttpPost ? "is-active" : ""}></setting-switch>
+                    </setting-item>
+                    <div class="${httpPostClass}" style="display: ${config.ob11.enableHttpPost ? '' : 'none'}">
+                        <div >
+                            <button id="addHttpHost" class="q-button">添加HTTP POST上报地址</button>
+                        </div>
+                        <div id="httpHostItems">
+                            ${httpHostsEleStr}
+                        </div>
                     </div>
-                    <div id="httpHostItems">
-                        ${httpHostsEleStr}
-                    </div>
-                    
-                    <setting-item class="vertical-list-item" data-direction="row">
+                    <setting-item data-direction="row" class="hostItem vertical-list-item">
+                        <div>
+                            <div>启用正向Websocket协议</div>
+                        </div>
+                        <setting-switch id="websocket" ${config.ob11.enableWs ? "is-active" : ""}></setting-switch>
+                    </setting-item>
+                    <setting-item class="vertical-list-item ${wsClass}" data-direction="row" style="display: ${config.ob11.enableWs ? '' : 'none'}">
                         <setting-text>正向Websocket监听端口</setting-text>
-                        <input id="wsPort" type="number" value="${config.wsPort}"/>
+                        <input id="wsPort" type="number" value="${config.ob11.wsPort}"/>
                     </setting-item>
+                    
+                    <setting-item data-direction="row" class="hostItem vertical-list-item">
+                        <div>
+                            <div>启用反向Websocket协议</div>
+                        </div>
+                        <setting-switch id="websocketReverse" ${config.ob11.enableWsReverse ? "is-active" : ""}></setting-switch>
+                    </setting-item>
+                    <div class="${reverseWSClass}" style="display: ${config.ob11.enableWsReverse ? '' : 'none'}">
+                        <div>
+                            <button id="addWsHost" class="q-button">添加反向Websocket上报地址</button>
+                        </div>
+                        <div id="wsHostItems">
+                            ${wsHostsEleStr}
+                        </div>
+                    </div>
                     <setting-item class="vertical-list-item" data-direction="row">
                         <setting-text>Access Token</setting-text>
                         <input id="token" type="text" placeholder="可为空" value="${config.token}"/>
                     </setting-item>
-                    <div>
-                        <button id="addWsHost" class="q-button">添加反向Websocket上报地址</button>
-                    </div>
-                    <div id="wsHostItems">
-                        ${wsHostsEleStr}
-                    </div>
                     <button id="save" class="q-button">保存</button>
                 </setting-list>
             </setting-panel>
             <setting-panel>
-                <setting-item data-direction="row" class="hostItem vertical-list-item">
-                    <div>
-                        <div>启用HTTP支持</div>
-                        <div class="tips">修改后须重启QQ生效</div>
-                    </div>
-                    <setting-switch id="http" ${config.enableHttp ? "is-active" : ""}></setting-switch>
-                </setting-item>
-                <setting-item data-direction="row" class="hostItem vertical-list-item">
-                    <div>
-                        <div>启用HTTP POST支持</div>
-                        <div class="tips">修改后须重启QQ生效</div>
-                    </div>
-                    <setting-switch id="httpPost" ${config.enableHttpPost ? "is-active" : ""}></setting-switch>
-                </setting-item>
-                <setting-item data-direction="row" class="hostItem vertical-list-item">
-                    <div>
-                        <div>启用正向Websocket支持</div>
-                        <div class="tips">修改后须重启QQ生效</div>
-                    </div>
-                    <setting-switch id="websocket" ${config.enableWs ? "is-active" : ""}></setting-switch>
-                </setting-item>
-                <setting-item data-direction="row" class="hostItem vertical-list-item">
-                    <div>
-                        <div>启用反向Websocket支持</div>
-                        <div class="tips">修改后须重启QQ生效</div>
-                    </div>
-                    <setting-switch id="websocketReverse" ${config.enableWsReverse ? "is-active" : ""}></setting-switch>
-                </setting-item>
                 <setting-item data-direction="row" class="hostItem vertical-list-item">
                     <div>
                         <div>上报文件进行base64编码</div>
@@ -128,7 +130,7 @@ async function onSettingWindowCreated(view: Element) {
                 <setting-item data-direction="row" class="hostItem vertical-list-item">
                     <div>
                         <div>日志</div>
-                        <div class="tips">日志目录:${window.LiteLoader.plugins["LLOneBot"].path.data}</div>
+                        <div class="tips">目录:${window.LiteLoader.plugins["LLOneBot"].path.data}</div>
                     </div>
                     <setting-switch id="log" ${config.log ? "is-active" : ""}></setting-switch>
                 </setting-item>
@@ -160,8 +162,7 @@ async function onSettingWindowCreated(view: Element) {
             let addressDoc = parser.parseFromString(createWsHostEleStr(initValue), "text/html");
             addressEle = addressDoc.querySelector("setting-item")
             hostItemsEle = document.getElementById("wsHostItems");
-        }
-        else {
+        } else {
             let addressDoc = parser.parseFromString(createHttpHostEleStr(initValue), "text/html");
             addressEle = addressDoc.querySelector("setting-item")
             hostItemsEle = document.getElementById("httpHostItems");
@@ -174,24 +175,38 @@ async function onSettingWindowCreated(view: Element) {
     doc.getElementById("addHttpHost").addEventListener("click", () => addHostEle("http"))
     doc.getElementById("addWsHost").addEventListener("click", () => addHostEle("ws"))
 
-    function switchClick(eleId: string, configKey: string) {
+    function switchClick(eleId: string, configKey: string, _config=null) {
+        if (!_config){
+            _config = config
+        }
         doc.getElementById(eleId)?.addEventListener("click", (e) => {
             const switchEle = e.target as HTMLInputElement
-            if (config[configKey]) {
-                config[configKey] = false
+            if (_config[configKey]) {
+                _config[configKey] = false
                 switchEle.removeAttribute("is-active")
             } else {
-                config[configKey] = true
+                _config[configKey] = true
                 switchEle.setAttribute("is-active", "")
             }
+            // 妈蛋,手动操作DOM越写越麻烦,要不用vue算了
+            const keyClassMap = {
+                "enableHttp": httpClass,
+                "enableHttpPost": httpPostClass,
+                "enableWs": wsClass,
+                "enableWsReverse": reverseWSClass,
+            }
+            for (let e of document.getElementsByClassName(keyClassMap[configKey])) {
+                e["style"].display = _config[configKey] ? "" : "none"
+            }
+
             window.llonebot.setConfig(config)
         })
     }
 
-    switchClick("http", "enableHttp");
-    switchClick("httpPost", "enableHttpPost");
-    switchClick("websocket", "enableWs");
-    switchClick("websocketReverse", "enableWsReverse");
+    switchClick("http", "enableHttp", config.ob11);
+    switchClick("httpPost", "enableHttpPost", config.ob11);
+    switchClick("websocket", "enableWs", config.ob11);
+    switchClick("websocketReverse", "enableWsReverse", config.ob11);
     switchClick("debug", "debug");
     switchClick("switchBase64", "enableBase64");
     switchClick("reportSelfMessage", "reportSelfMessage");
@@ -210,27 +225,24 @@ async function onSettingWindowCreated(view: Element) {
             let httpHosts: string[] = [];
 
             for (const hostEle of httpHostEles) {
-                if (hostEle.value) {
-                    httpHosts.push(hostEle.value);
-                }
+                const value = hostEle.value.trim();
+                value && httpHosts.push(value);
             }
 
-            const wsPort = wsPortEle.value
-            const token = tokenEle.value
+            const wsPort = wsPortEle.value;
+            const token = tokenEle.value.trim();
             let wsHosts: string[] = [];
 
             for (const hostEle of wsHostEles) {
-                if (hostEle.value) {
-                    wsHosts.push(hostEle.value);
-                }
+                const value = hostEle.value.trim();
+                value && wsHosts.push(value);
             }
 
-
-            config.httpPort = parseInt(httpPort);
-            config.httpHosts = httpHosts;
-            config.wsPort = parseInt(wsPort);
-            config.wsHosts = wsHosts;
-            config.token = token.trim();
+            config.ob11.httpPort = parseInt(httpPort);
+            config.ob11.httpHosts = httpHosts;
+            config.ob11.wsPort = parseInt(wsPort);
+            config.ob11.wsHosts = wsHosts;
+            config.token = token;
             window.llonebot.setConfig(config);
             alert("保存成功");
         })
diff --git a/src/server/base.ts b/src/server/base.ts
new file mode 100644
index 0000000..7291840
--- /dev/null
+++ b/src/server/base.ts
@@ -0,0 +1,6 @@
+
+
+export abstract class ServerBase{
+    abstract start: () => void
+    abstract restart: ()=>void
+}
\ No newline at end of file
diff --git a/src/server/http.ts b/src/server/http.ts
new file mode 100644
index 0000000..6163140
--- /dev/null
+++ b/src/server/http.ts
@@ -0,0 +1,106 @@
+import express, {Express, Request, Response} from "express";
+import {getConfigUtil, log} from "../common/utils";
+import http from "http";
+
+const JSONbig = require('json-bigint')({storeAsString: true});
+
+type RegisterHandler = (res: Response, payload: any) => Promise<any>
+
+export abstract class HttpServerBase {
+    name: string = "LLOneBot";
+    private readonly expressAPP: Express;
+    private server: http.Server = null;
+
+    constructor() {
+        this.expressAPP = express();
+        this.expressAPP.use(express.urlencoded({extended: true, limit: "500mb"}));
+        this.expressAPP.use((req, res, next) => {
+            let data = '';
+            req.on('data', chunk => {
+                data += chunk.toString();
+            });
+            req.on('end', () => {
+                if (data) {
+                    try {
+                        // log("receive raw", data)
+                        req.body = JSONbig.parse(data);
+                    } catch (e) {
+                        return next(e);
+                    }
+                }
+                next();
+            });
+        });
+    }
+
+    authorize(req: Request, res: Response, next: () => void) {
+        let serverToken = getConfigUtil().getConfig().token;
+        let clientToken = ""
+        const authHeader = req.get("authorization")
+        if (authHeader) {
+            clientToken = authHeader.split("Bearer ").pop()
+            log("receive http header token", clientToken)
+        } else if (req.query.access_token) {
+            if (Array.isArray(req.query.access_token)) {
+                clientToken = req.query.access_token[0].toString();
+            } else {
+                clientToken = req.query.access_token.toString();
+            }
+            log("receive http url token", clientToken)
+        }
+
+        if (serverToken && clientToken != serverToken) {
+            return res.status(403).send(JSON.stringify({message: 'token verify failed!'}));
+        }
+        next();
+    };
+
+    start(port: number) {
+        this.expressAPP.get('/', (req: Request, res: Response) => {
+            res.send(`${this.name}已启动`);
+        })
+        this.listen(port);
+    }
+
+    stop() {
+        if (this.server){
+            this.server.close()
+            this.server = null;
+        }
+    }
+
+    restart(port: number){
+        this.stop()
+        this.start(port)
+    }
+
+    abstract handleFailed(res: Response, payload: any, err: any): void
+
+    registerRouter(method: string, url: string, handler: RegisterHandler) {
+        if (!url.startsWith("/")) {
+            url = "/" + url
+        }
+        const methodFunc = this.expressAPP[method]
+        if (!methodFunc){
+            const err = `${this.name} register router failed,${method} not exist`;
+            log(err);
+            throw err;
+        }
+        this.expressAPP[method](url, this.authorize, async (req: Request, res: Response) => {
+            const payload = req.body || req.query || {}
+            try{
+                res.send(await handler(res, payload))
+            }catch (e) {
+                this.handleFailed(res, payload, e.stack.toString())
+            }
+        });
+    }
+
+    protected listen(port: number) {
+        this.server = this.expressAPP.listen(port, "0.0.0.0", () => {
+            const info = `${this.name} started 0.0.0.0:${port}`
+            console.log(info);
+            log(info);
+        });
+    }
+}
\ No newline at end of file