From 39a70de606a74d8e860b801046fba09605b0f4d6 Mon Sep 17 00:00:00 2001 From: "xiao.xiong" Date: Sun, 19 Oct 2025 23:46:41 +0800 Subject: [PATCH] update --- server/package.json | 19 ++++ server/pnpm-lock.yaml | 236 ++++++++++++++++++++++++++++++++++++++++++ server/src/app.ts | 25 +++++ server/src/index.ts | 1 + server/src/lib.ts | 113 ++++++++++++++++++++ 5 files changed, 394 insertions(+) create mode 100644 server/package.json create mode 100644 server/pnpm-lock.yaml create mode 100644 server/src/app.ts create mode 100644 server/src/index.ts create mode 100644 server/src/lib.ts diff --git a/server/package.json b/server/package.json new file mode 100644 index 0000000..565107e --- /dev/null +++ b/server/package.json @@ -0,0 +1,19 @@ +{ + "name": "server", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "dev": "bun run --hot --watch src/index.ts", + "pm2": "pm2 start src/index.ts --name dnake-server --interpreter bun --watch" + }, + "keywords": [], + "author": "", + "license": "ISC", + "packageManager": "pnpm@10.11.0", + "devDependencies": { + "@kevisual/router": "^0.0.29", + "@types/bun": "^1.3.0", + "@types/node": "^24.8.1" + } +} \ No newline at end of file diff --git a/server/pnpm-lock.yaml b/server/pnpm-lock.yaml new file mode 100644 index 0000000..e002145 --- /dev/null +++ b/server/pnpm-lock.yaml @@ -0,0 +1,236 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@kevisual/router': + specifier: ^0.0.29 + version: 0.0.29 + '@types/bun': + specifier: ^1.3.0 + version: 1.3.0(@types/react@19.2.2) + '@types/node': + specifier: ^24.8.1 + version: 24.8.1 + +packages: + + '@kevisual/router@0.0.29': + resolution: {integrity: sha512-UD2aWgf5yv/HmX3FOCPvvRLaZqYAP1JUsCgswo9BSibrVZYh7ZEnShYYnE+P2mr34UI2xEn+BJVxLMCLXg83sA==} + + '@types/bun@1.3.0': + resolution: {integrity: sha512-+lAGCYjXjip2qY375xX/scJeVRmZ5cY0wyHYyCYxNcdEXrQ4AOe3gACgd4iQ8ksOslJtW4VNxBJ8llUwc3a6AA==} + + '@types/node@24.8.1': + resolution: {integrity: sha512-alv65KGRadQVfVcG69MuB4IzdYVpRwMG/mq8KWOaoOdyY617P5ivaDiMCGOFDWD2sAn5Q0mR3mRtUOgm99hL9Q==} + + '@types/react@19.2.2': + resolution: {integrity: sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==} + + bun-types@1.3.0: + resolution: {integrity: sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ==} + peerDependencies: + '@types/react': ^19 + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@3.0.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + node-forge@1.3.1: + resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} + engines: {node: '>= 6.13.0'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + path-to-regexp@8.3.0: + resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + selfsigned@3.0.1: + resolution: {integrity: sha512-6U6w6kSLrM9Zxo0D7mC7QdGS6ZZytMWBnj/vhF9p+dAHx6CwGezuRcO4VclTbrrI7mg7SD6zNiqXUuBHOVopNQ==} + engines: {node: '>=10'} + + send@1.2.0: + resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + engines: {node: '>= 18'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + undici-types@7.14.0: + resolution: {integrity: sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==} + +snapshots: + + '@kevisual/router@0.0.29': + dependencies: + path-to-regexp: 8.3.0 + selfsigned: 3.0.1 + send: 1.2.0 + transitivePeerDependencies: + - supports-color + + '@types/bun@1.3.0(@types/react@19.2.2)': + dependencies: + bun-types: 1.3.0(@types/react@19.2.2) + transitivePeerDependencies: + - '@types/react' + + '@types/node@24.8.1': + dependencies: + undici-types: 7.14.0 + + '@types/react@19.2.2': + dependencies: + csstype: 3.1.3 + + bun-types@1.3.0(@types/react@19.2.2): + dependencies: + '@types/node': 24.8.1 + '@types/react': 19.2.2 + + csstype@3.1.3: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + depd@2.0.0: {} + + ee-first@1.1.1: {} + + encodeurl@2.0.0: {} + + escape-html@1.0.3: {} + + etag@1.8.1: {} + + fresh@2.0.0: {} + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + inherits@2.0.4: {} + + mime-db@1.54.0: {} + + mime-types@3.0.1: + dependencies: + mime-db: 1.54.0 + + ms@2.1.3: {} + + node-forge@1.3.1: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + path-to-regexp@8.3.0: {} + + range-parser@1.2.1: {} + + selfsigned@3.0.1: + dependencies: + node-forge: 1.3.1 + + send@1.2.0: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.0 + mime-types: 3.0.1 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + setprototypeof@1.2.0: {} + + statuses@2.0.1: {} + + statuses@2.0.2: {} + + toidentifier@1.0.1: {} + + undici-types@7.14.0: {} diff --git a/server/src/app.ts b/server/src/app.ts new file mode 100644 index 0000000..8f85e40 --- /dev/null +++ b/server/src/app.ts @@ -0,0 +1,25 @@ +import { App } from '@kevisual/router' + +import { sendDefaultSipMessage } from './lib.ts'; + + +const app = new App(); + +// http://localhost:3001/api/router?path=floor +app.route({ + path: 'floor' +}).define(async (ctx) => { + await sendDefaultSipMessage(); + ctx.body = 'SIP消息已发送'; +}).addTo(app) + +app.route({ + path: 'test' +}).define(async (ctx) => { + ctx.body = 'SIP消息已发送'; +}).addTo(app) + +app.listen(3001, "::", () => { + console.log('Server is running on http://localhost:3001'); +}) +// curl http://localhost:3001/api/router?path=test \ No newline at end of file diff --git a/server/src/index.ts b/server/src/index.ts new file mode 100644 index 0000000..ee49f8c --- /dev/null +++ b/server/src/index.ts @@ -0,0 +1 @@ +import './app.ts' \ No newline at end of file diff --git a/server/src/lib.ts b/server/src/lib.ts new file mode 100644 index 0000000..8fcb3ff --- /dev/null +++ b/server/src/lib.ts @@ -0,0 +1,113 @@ +interface SipMessageConfig { + targetIp?: string; + targetPort?: number; + macLocalIp?: string; + senderSip?: string; + senderIp?: string; + receiverSip?: string; + receiverIp?: string; +} + +export async function sendSipMessage(config: SipMessageConfig = {}): Promise { + // 配置参数(使用默认值或传入的配置) + const targetIp = config.targetIp || "192.168.3.3"; // 设备eth1的IP(转发入口) + const targetPort = config.targetPort || 5060; // 转发端口 + const macLocalIp = config.macLocalIp || "192.168.3.33"; // Mac的本地IP(与Via头部一致) + const senderSip = config.senderSip || "3011302"; // 发送方SIP用户 + const senderIp = config.senderIp || "192.168.9.57"; // 设备eth0的IP(转发后源IP) + const receiverSip = config.receiverSip || "3019901"; // 接收方SIP用户 + const receiverIp = config.receiverIp || "192.168.9.4"; // 目标IP + + // 生成唯一标识 + const callId = `${Math.floor(Date.now() / 1000)}`; // 基于时间戳的Call-ID + const tag = `${Math.floor(Date.now() % 1000000)}`; // 随机tag + const branch = `z9hG4bK${Math.floor(Date.now() / 1000)}`; // Via分支标识 + + // XML消息体(业务数据) + const xmlBody = ` + + sip:${receiverSip}@${receiverIp}:5060 + 1 + 2 + 13 + 2 + elev + appoint + /elev/appoint +`; + + // 计算消息体长度(使用字符串长度 + utf8编码估算) + const contentLength = new TextEncoder().encode(xmlBody).length; + + // 构造SIP MESSAGE请求(严格使用\r\n换行) + const sipMessage = `MESSAGE sip:${receiverSip}@${receiverIp}:5060 SIP/2.0\r\n` + + `Via: SIP/2.0/UDP ${macLocalIp}:5060;rport;branch=${branch}\r\n` + + `From: ;tag=${tag}\r\n` + + `To: \r\n` + + `Call-ID: ${callId}@${macLocalIp}\r\n` + + `CSeq: 20 MESSAGE\r\n` + + `Content-Type: text/plain\r\n` + + `Max-Forwards: 70\r\n` + + `User-Agent: DnakeVoip v1.0\r\n` + + `Content-Length: ${contentLength}\r\n\r\n` + + `${xmlBody}`; + + return new Promise((resolve, reject) => { + try { + console.log(`准备发送SIP消息到 ${targetIp}:${targetPort}`); + console.log('\n发送的消息内容:'); + console.log(sipMessage); + resolve(); + } catch (error: any) { + console.log(`处理失败:${error.message}`); + reject(error); + } + }); +} + +// 使用默认配置的便捷函数 +export function sendDefaultSipMessage(): Promise { + return sendSipMessage(); +} + +// 使用Node.js原生dgram模块的实现(需要@types/node) +export function getSipMessageWithDgram(config: SipMessageConfig = {}): string { + const targetIp = config.targetIp || "192.168.3.3"; + const macLocalIp = config.macLocalIp || "192.168.3.33"; + const senderSip = config.senderSip || "3011302"; + const senderIp = config.senderIp || "192.168.9.57"; + const receiverSip = config.receiverSip || "3019901"; + const receiverIp = config.receiverIp || "192.168.9.4"; + + const callId = `${Math.floor(Date.now() / 1000)}`; + const tag = `${Math.floor(Date.now() % 1000000)}`; + const branch = `z9hG4bK${Math.floor(Date.now() / 1000)}`; + + const xmlBody = ` + + sip:${receiverSip}@${receiverIp}:5060 + 1 + 2 + 13 + 2 + elev + appoint + /elev/appoint +`; + + const contentLength = new TextEncoder().encode(xmlBody).length; + + return `MESSAGE sip:${receiverSip}@${receiverIp}:5060 SIP/2.0\r\n` + + `Via: SIP/2.0/UDP ${macLocalIp}:5060;rport;branch=${branch}\r\n` + + `From: ;tag=${tag}\r\n` + + `To: \r\n` + + `Call-ID: ${callId}@${macLocalIp}\r\n` + + `CSeq: 20 MESSAGE\r\n` + + `Content-Type: text/plain\r\n` + + `Max-Forwards: 70\r\n` + + `User-Agent: DnakeVoip v1.0\r\n` + + `Content-Length: ${contentLength}\r\n\r\n` + + `${xmlBody}`; +} + +// sendDefaultSipMessage().catch(console.error); \ No newline at end of file