test
This commit is contained in:
commit
54f5caeeaa
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
node_modules
|
24
js-demo/client/.gitignore
vendored
Normal file
24
js-demo/client/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
3
js-demo/client/.vscode/extensions.json
vendored
Normal file
3
js-demo/client/.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["johnsoncodehk.volar"]
|
||||||
|
}
|
5
js-demo/client/README.md
Normal file
5
js-demo/client/README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
### DEMO
|
||||||
|
|
||||||
|
此处为验证API的调试环境
|
||||||
|
|
||||||
|
By Vite
|
13
js-demo/client/index.html
Normal file
13
js-demo/client/index.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Vite App</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
2694
js-demo/client/package-lock.json
generated
Normal file
2694
js-demo/client/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
js-demo/client/package.json
Normal file
22
js-demo/client/package.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "poros-demo-client",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite --port 8888 --host 0.0.0.0 ",
|
||||||
|
"build": "vue-tsc --noEmit && vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"vue": "^3.2.25"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@originjs/vite-plugin-commonjs": "1.0.3",
|
||||||
|
"@types/node": "17.0.25",
|
||||||
|
"@vitejs/plugin-vue": "2.3.0",
|
||||||
|
"axios": "0.26.1",
|
||||||
|
"typescript": "4.5.4",
|
||||||
|
"vite": "2.9.0",
|
||||||
|
"vue-tsc": "0.29.8"
|
||||||
|
}
|
||||||
|
}
|
BIN
js-demo/client/public/favicon.ico
Normal file
BIN
js-demo/client/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
196
js-demo/client/src/App.vue
Normal file
196
js-demo/client/src/App.vue
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// This starter template is using Vue 3 <script setup> SFCs
|
||||||
|
// Check out https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup
|
||||||
|
import { ref } from "vue"
|
||||||
|
import axios from "axios"
|
||||||
|
import { createSocket, destroySocket } from "./socket/index"
|
||||||
|
|
||||||
|
const api = axios.create({
|
||||||
|
baseURL: "http://localhost:3000",
|
||||||
|
})
|
||||||
|
|
||||||
|
// 替换你的秘钥
|
||||||
|
const appKey = ref("")
|
||||||
|
const appSecret = ref("")
|
||||||
|
// 替换你的主播身份码
|
||||||
|
const codeId = ref("")
|
||||||
|
// 替换你的app应用 [这里测试为互动游戏]
|
||||||
|
const appId = ref("")
|
||||||
|
// [向 node server请求接口后自动返回]
|
||||||
|
const gameId = ref("")
|
||||||
|
// v2改为server response 服务器返回websocket信息,而非手动获取
|
||||||
|
const authBody = ref("")
|
||||||
|
const wssLinks = ref([])
|
||||||
|
// heartBeat Timer
|
||||||
|
const heartBeatTimer = ref<NodeJS.Timer>()
|
||||||
|
// be ready
|
||||||
|
clearInterval(heartBeatTimer.value!)
|
||||||
|
/**
|
||||||
|
* 测试请求鉴权接口
|
||||||
|
*/
|
||||||
|
const getAuth = () => {
|
||||||
|
api.post("/getAuth", {
|
||||||
|
appKey: appKey.value,
|
||||||
|
appSecret: appSecret.value,
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
console.log("-----鉴权成功-----")
|
||||||
|
console.log("返回:", data)
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log("-----鉴权失败-----")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const heartBeatThis = (game_id) => {
|
||||||
|
// 心跳 是否成功
|
||||||
|
api.post("/gameHeartBeat", {
|
||||||
|
game_id,
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
console.log("-----心跳成功-----")
|
||||||
|
console.log("返回:", data)
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log("-----心跳失败-----")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @comment 注意所有的接口基于鉴权成功后才能正确返回
|
||||||
|
* 测试请求游戏开启接口
|
||||||
|
*/
|
||||||
|
const gameStart = () => {
|
||||||
|
api.post("/gameStart", {
|
||||||
|
code: codeId.value,
|
||||||
|
app_id: Number(appId.value),
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
const res = data.data
|
||||||
|
const { game_info, websocket_info } = res
|
||||||
|
const { auth_body, wss_link } = websocket_info
|
||||||
|
authBody.value = auth_body
|
||||||
|
wssLinks.value = wss_link
|
||||||
|
console.log("-----游戏开始成功-----")
|
||||||
|
console.log("返回GameId:", game_info)
|
||||||
|
gameId.value = game_info.game_id
|
||||||
|
// v2改为20s请求心跳一次,不然60s会自动关闭
|
||||||
|
heartBeatTimer.value = setInterval(() => {
|
||||||
|
heartBeatThis(game_info.game_id)
|
||||||
|
}, 20000)
|
||||||
|
} else {
|
||||||
|
console.log("-----游戏开始失败-----")
|
||||||
|
console.log("原因:", data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log("-----游戏开始失败-----")
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @comment 基于gameStart成功后才会关闭正常,否则获取不到game_id
|
||||||
|
* 测试请求游戏关闭接口
|
||||||
|
*/
|
||||||
|
const gameEnd = () => {
|
||||||
|
api.post("/gameEnd", {
|
||||||
|
game_id: gameId.value,
|
||||||
|
app_id: Number(appId.value),
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
console.log("-----游戏关闭成功-----")
|
||||||
|
console.log("返回:", data)
|
||||||
|
// 清空长链
|
||||||
|
authBody.value = ""
|
||||||
|
wssLinks.value = []
|
||||||
|
clearInterval(heartBeatTimer.value)
|
||||||
|
handleDestroySocket()
|
||||||
|
console.log("-----心跳关闭成功-----")
|
||||||
|
} else {
|
||||||
|
console.log("-----游戏关闭失败-----")
|
||||||
|
console.log("原因:", data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log("-----游戏关闭失败-----")
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试创建长长连接接口
|
||||||
|
*/
|
||||||
|
const handleCreateSocket = () => {
|
||||||
|
if (authBody.value && wssLinks.value) {
|
||||||
|
createSocket(authBody.value, wssLinks.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试销毁长长连接接口
|
||||||
|
*/
|
||||||
|
const handleDestroySocket = () => {
|
||||||
|
destroySocket()
|
||||||
|
console.log("-----长连接销毁成功-----")
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h3>-- !-- 所有输出信息都在控制台 -- --</h3>
|
||||||
|
<div>鉴权部分</div>
|
||||||
|
<div class="form">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="填写access_key_id"
|
||||||
|
v-model="appKey"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="填写access_key_secret"
|
||||||
|
v-model="appSecret"
|
||||||
|
/>
|
||||||
|
<button @click="getAuth">鉴权</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
<div>程序开启部分 [需要先鉴权]</div>
|
||||||
|
<div class="form">
|
||||||
|
<input type="text" placeholder="填写主播身份码" v-model="codeId" />
|
||||||
|
<input type="text" placeholder="填写 app_id" v-model="appId" />
|
||||||
|
<button @click="gameStart">游戏开始</button>
|
||||||
|
<button @click="gameEnd">游戏结束</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
<div>长连接部分 [需要先游戏开启]</div>
|
||||||
|
<div class="form">
|
||||||
|
<button @click="handleCreateSocket">建立长连接</button>
|
||||||
|
<button @click="handleDestroySocket">销毁长连接</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#app {
|
||||||
|
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
text-align: center;
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-top: 60px;
|
||||||
|
}
|
||||||
|
.form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.form input,
|
||||||
|
.form button {
|
||||||
|
width: 400px;
|
||||||
|
height: 50px;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
</style>
|
8
js-demo/client/src/assets/danmaku-websocket.min.js
vendored
Normal file
8
js-demo/client/src/assets/danmaku-websocket.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
9
js-demo/client/src/env.d.ts
vendored
Normal file
9
js-demo/client/src/env.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
declare module '*.vue' {
|
||||||
|
import type { DefineComponent } from 'vue'
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||||
|
const component: DefineComponent<{}, {}, any>
|
||||||
|
export default component
|
||||||
|
}
|
||||||
|
|
5
js-demo/client/src/main.ts
Normal file
5
js-demo/client/src/main.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { createApp } from "vue"
|
||||||
|
import App from "./App.vue"
|
||||||
|
|
||||||
|
createApp(App).mount("#app")
|
||||||
|
|
80
js-demo/client/src/socket/index.ts
Normal file
80
js-demo/client/src/socket/index.ts
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import DanmakuWebSocket from "../assets/danmaku-websocket.min.js"
|
||||||
|
|
||||||
|
let ws: DanmakuWebSocket
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建socket长连接
|
||||||
|
* @param authBody
|
||||||
|
* @param wssLinks
|
||||||
|
*/
|
||||||
|
function createSocket(authBody: string, wssLinks: string[]) {
|
||||||
|
const opt = {
|
||||||
|
...getWebSocketConfig(authBody, wssLinks),
|
||||||
|
// 收到消息,
|
||||||
|
onReceivedMessage: (res) => {
|
||||||
|
console.log(res)
|
||||||
|
},
|
||||||
|
// 收到心跳处理回调
|
||||||
|
onHeartBeatReply: (data) => console.log("收到心跳处理回调:", data),
|
||||||
|
onError: (data) => console.log("error", data),
|
||||||
|
onListConnectError: () => {
|
||||||
|
console.log("list connect error")
|
||||||
|
destroySocket()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ws) {
|
||||||
|
ws = new DanmakuWebSocket(opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ws
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取websocket配置信息
|
||||||
|
* @param authBody
|
||||||
|
* @param wssLinks
|
||||||
|
*/
|
||||||
|
function getWebSocketConfig(authBody: string, wssLinks: string[]) {
|
||||||
|
const url = wssLinks[0]
|
||||||
|
const urlList = wssLinks
|
||||||
|
const auth_body = JSON.parse(authBody)
|
||||||
|
return {
|
||||||
|
url,
|
||||||
|
urlList,
|
||||||
|
customAuthParam: [
|
||||||
|
{
|
||||||
|
key: "key",
|
||||||
|
value: auth_body.key,
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "group",
|
||||||
|
value: auth_body.group,
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rid: auth_body.roomid,
|
||||||
|
protover: auth_body.protoover,
|
||||||
|
uid: auth_body.uid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销毁websocket
|
||||||
|
*/
|
||||||
|
function destroySocket() {
|
||||||
|
console.log("destroy1")
|
||||||
|
ws && ws.destroy()
|
||||||
|
ws = undefined
|
||||||
|
console.log("destroy2")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取websocket实例
|
||||||
|
*/
|
||||||
|
function getWsClient() {
|
||||||
|
return ws
|
||||||
|
}
|
||||||
|
|
||||||
|
export { createSocket, destroySocket, getWebSocketConfig, getWsClient }
|
19
js-demo/client/src/types/index.d.ts
vendored
Normal file
19
js-demo/client/src/types/index.d.ts
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
declare interface ISocketData {
|
||||||
|
// ip地址
|
||||||
|
ip: number[]
|
||||||
|
|
||||||
|
// host地址 可能是ip 也可能是域名。
|
||||||
|
host: string[]
|
||||||
|
|
||||||
|
// 长连使用的请求json体 第三方无需关注内容,建立长连时使用即可。
|
||||||
|
auth_body: string
|
||||||
|
|
||||||
|
// tcp 端口号
|
||||||
|
tcp_port: number[]
|
||||||
|
|
||||||
|
// ws 端口号
|
||||||
|
ws_port: number[]
|
||||||
|
|
||||||
|
// wss 端口号
|
||||||
|
wss_port: number[]
|
||||||
|
}
|
20
js-demo/client/tsconfig.json
Normal file
20
js-demo/client/tsconfig.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2016",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"strict": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"sourceMap": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"lib": ["esnext", "dom"],
|
||||||
|
"types": ["node"],
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"forceConsistentCasingInFileNames": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
|
||||||
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
|
}
|
8
js-demo/client/tsconfig.node.json
Normal file
8
js-demo/client/tsconfig.node.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "node"
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
17
js-demo/client/vite.config.ts
Normal file
17
js-demo/client/vite.config.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { defineConfig } from "vite"
|
||||||
|
import vue from "@vitejs/plugin-vue"
|
||||||
|
import { viteCommonjs } from "@originjs/vite-plugin-commonjs"
|
||||||
|
import * as path from "path"
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
server: {
|
||||||
|
// https: true
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": path.resolve(__dirname, "./src")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: [vue(), viteCommonjs()]
|
||||||
|
})
|
5
js-demo/server/README.md
Normal file
5
js-demo/server/README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
### 说明
|
||||||
|
|
||||||
|
基于 npm 16.13.0 构建
|
||||||
|
|
||||||
|
koa server服务
|
22
js-demo/server/interceptor.ts
Normal file
22
js-demo/server/interceptor.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import axios from "axios"
|
||||||
|
import { getEncodeHeader } from "./tool/index"
|
||||||
|
|
||||||
|
// axios 拦截器
|
||||||
|
const api = axios.create({
|
||||||
|
baseURL: "https://live-open.biliapi.com"
|
||||||
|
// baseURL: "http://test-live-open.biliapi.net" //test
|
||||||
|
})
|
||||||
|
|
||||||
|
// 鉴权加密处理headers,下次请求自动带上
|
||||||
|
api.interceptors.request.use(config => {
|
||||||
|
const headers = getEncodeHeader(
|
||||||
|
config.data,
|
||||||
|
global.appKey,
|
||||||
|
global.appSecret
|
||||||
|
)
|
||||||
|
console.log('headers: ', headers)
|
||||||
|
config.headers = headers
|
||||||
|
return config
|
||||||
|
})
|
||||||
|
|
||||||
|
export default api
|
1777
js-demo/server/package-lock.json
generated
Normal file
1777
js-demo/server/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
28
js-demo/server/package.json
Normal file
28
js-demo/server/package.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "poros-demo-server",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "server.ts",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"server": "node --loader ts-node/esm --experimental-specifier-resolution=node server.ts"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "sometimes & re",
|
||||||
|
"type": "module",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"axios": "0.26.1",
|
||||||
|
"http": "0.0.1-security",
|
||||||
|
"https": "1.0.0",
|
||||||
|
"koa": "2.13.4",
|
||||||
|
"koa-bodyparser": "4.3.0",
|
||||||
|
"koa-logger": "3.2.1",
|
||||||
|
"koa-router": "10.1.1",
|
||||||
|
"koa2-cors": "2.0.6",
|
||||||
|
"ts-node": "10.8.0",
|
||||||
|
"tslib": "2.3.1",
|
||||||
|
"typescript": "4.6.3",
|
||||||
|
"@types/node": "17.0.25"
|
||||||
|
}
|
||||||
|
}
|
17
js-demo/server/routes/gameBatchHeartBeat.ts
Normal file
17
js-demo/server/routes/gameBatchHeartBeat.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import api from "../interceptor"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量心跳接口
|
||||||
|
* @param ctx
|
||||||
|
*/
|
||||||
|
export default async function GameBatchHeartBeat(ctx) {
|
||||||
|
const params = ctx.request.body
|
||||||
|
await api
|
||||||
|
.post("/v2/app/batchHeartbeat", params)
|
||||||
|
.then(({ data }) => {
|
||||||
|
ctx.body = data
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
ctx.body = err
|
||||||
|
})
|
||||||
|
}
|
17
js-demo/server/routes/gameEnd.ts
Normal file
17
js-demo/server/routes/gameEnd.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import api from "../interceptor"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 互动玩法游戏结束接口
|
||||||
|
* @param ctx
|
||||||
|
*/
|
||||||
|
export default async function GameEnd(ctx) {
|
||||||
|
const params = ctx.request.body
|
||||||
|
await api
|
||||||
|
.post("/v2/app/end", params)
|
||||||
|
.then(({ data }) => {
|
||||||
|
ctx.body = data
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
ctx.body = err
|
||||||
|
})
|
||||||
|
}
|
17
js-demo/server/routes/gameHeartBeat.ts
Normal file
17
js-demo/server/routes/gameHeartBeat.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import api from "../interceptor"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 心跳接口
|
||||||
|
* @param ctx
|
||||||
|
*/
|
||||||
|
export default async function GameHeartBeat(ctx) {
|
||||||
|
const params = ctx.request.body
|
||||||
|
await api
|
||||||
|
.post("/v2/app/heartbeat", params)
|
||||||
|
.then(({ data }) => {
|
||||||
|
ctx.body = data
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
ctx.body = err
|
||||||
|
})
|
||||||
|
}
|
17
js-demo/server/routes/gameStart.ts
Normal file
17
js-demo/server/routes/gameStart.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import api from "../interceptor"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 互动玩法游戏结束接口
|
||||||
|
* @param ctx
|
||||||
|
*/
|
||||||
|
export default async function GameStart(ctx) {
|
||||||
|
const params = ctx.request.body
|
||||||
|
await api
|
||||||
|
.post("/v2/app/start", params)
|
||||||
|
.then(({ data }) => {
|
||||||
|
ctx.body = data
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
ctx.body = err
|
||||||
|
})
|
||||||
|
}
|
30
js-demo/server/routes/getAuth.ts
Normal file
30
js-demo/server/routes/getAuth.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import api from "../interceptor"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 鉴权签名接口
|
||||||
|
* @param ctx
|
||||||
|
*/
|
||||||
|
export default async function GetAuth(ctx) {
|
||||||
|
const params = ctx.request.body
|
||||||
|
// 为了方便存在全局
|
||||||
|
global.appKey = params.appKey
|
||||||
|
global.appSecret = params.appSecret
|
||||||
|
// [只为验证鉴权,可删除]
|
||||||
|
await api
|
||||||
|
.post("/v2/app/start", {})
|
||||||
|
.then(({ data }) => {
|
||||||
|
// 非文档描述性code,sign success
|
||||||
|
if (data.code === -400) {
|
||||||
|
ctx.body = {
|
||||||
|
msg: "auth success",
|
||||||
|
type: "success",
|
||||||
|
state: 200
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx.body = data
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
ctx.body = err
|
||||||
|
})
|
||||||
|
}
|
17
js-demo/server/routes/index.ts
Normal file
17
js-demo/server/routes/index.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import Router from "koa-router"
|
||||||
|
import GetAuth from "./getAuth"
|
||||||
|
import GameStart from "./gameStart"
|
||||||
|
import GameEnd from "./gameEnd"
|
||||||
|
import GameHeartBeat from "./gameHeartBeat"
|
||||||
|
import GameBatchHeartBeat from "./gameBatchHeartBeat"
|
||||||
|
|
||||||
|
const router = new Router()
|
||||||
|
|
||||||
|
// 开启路由
|
||||||
|
router.post("/getAuth", GetAuth)
|
||||||
|
router.post("/gameStart", GameStart)
|
||||||
|
router.post("/gameEnd", GameEnd)
|
||||||
|
router.post("/gameHeartBeat", GameHeartBeat)
|
||||||
|
router.post("/gameBatchHeartBeat", GameBatchHeartBeat)
|
||||||
|
|
||||||
|
export default router
|
37
js-demo/server/server.ts
Normal file
37
js-demo/server/server.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import Koa from "koa"
|
||||||
|
import logger from "koa-logger"
|
||||||
|
import cors from "koa2-cors"
|
||||||
|
import bodyParser from "koa-bodyparser"
|
||||||
|
|
||||||
|
import router from "./routes/index"
|
||||||
|
|
||||||
|
// axios 拦截
|
||||||
|
import "./interceptor"
|
||||||
|
|
||||||
|
// init
|
||||||
|
const app = new Koa()
|
||||||
|
|
||||||
|
// 开启logger
|
||||||
|
app.use(logger())
|
||||||
|
|
||||||
|
// 开启bodyParser
|
||||||
|
app.use(bodyParser())
|
||||||
|
|
||||||
|
// 开启跨域
|
||||||
|
app.use(cors())
|
||||||
|
|
||||||
|
// 开启router
|
||||||
|
app.use(router.routes())
|
||||||
|
app.use(router.allowedMethods())
|
||||||
|
|
||||||
|
app.use(async ctx => {
|
||||||
|
ctx.body = "bilibili创作者服务中心"
|
||||||
|
})
|
||||||
|
|
||||||
|
const protocol = "http"
|
||||||
|
const host = "127.0.0.1"
|
||||||
|
const port = 3000
|
||||||
|
|
||||||
|
app.listen(port, () => {
|
||||||
|
console.log(`Listening on ${protocol}://${host}:${port}`)
|
||||||
|
})
|
50
js-demo/server/tool/index.ts
Normal file
50
js-demo/server/tool/index.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import crypto from "crypto"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 鉴权加密
|
||||||
|
* @param {*} params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getEncodeHeader(
|
||||||
|
params = {},
|
||||||
|
appKey: string,
|
||||||
|
appSecret: string
|
||||||
|
) {
|
||||||
|
const timestamp = parseInt(Date.now() / 1000 + "")
|
||||||
|
const nonce = parseInt(Math.random() * 100000 + "") + timestamp
|
||||||
|
const header = {
|
||||||
|
"x-bili-accesskeyid": appKey,
|
||||||
|
"x-bili-content-md5": getMd5Content(JSON.stringify(params)),
|
||||||
|
"x-bili-signature-method": "HMAC-SHA256",
|
||||||
|
"x-bili-signature-nonce": nonce + "",
|
||||||
|
"x-bili-signature-version": "1.0",
|
||||||
|
"x-bili-timestamp": timestamp
|
||||||
|
}
|
||||||
|
const data: string[] = []
|
||||||
|
for (const key in header) {
|
||||||
|
data.push(`${key}:${header[key]}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const signature = crypto
|
||||||
|
.createHmac("sha256", appSecret)
|
||||||
|
.update(data.join("\n"))
|
||||||
|
.digest("hex")
|
||||||
|
return {
|
||||||
|
Accept: "application/json",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...header,
|
||||||
|
Authorization: signature
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MD5加密
|
||||||
|
* @param {*} str
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getMd5Content(str) {
|
||||||
|
return crypto
|
||||||
|
.createHash("md5")
|
||||||
|
.update(str)
|
||||||
|
.digest("hex")
|
||||||
|
}
|
48
js-demo/server/tsconfig.json
Normal file
48
js-demo/server/tsconfig.json
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||||
|
|
||||||
|
/* Language and Environment */
|
||||||
|
"target": "es2016",
|
||||||
|
/* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||||
|
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||||
|
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||||
|
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||||
|
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||||
|
|
||||||
|
/* Modules */
|
||||||
|
"module": "ESNext",
|
||||||
|
/* Specify what module code is generated. */
|
||||||
|
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||||
|
"moduleResolution": "node",
|
||||||
|
/* Specify how TypeScript looks up a file from a given module specifier. */
|
||||||
|
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||||
|
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||||
|
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||||
|
"typeRoots": ["./types"],
|
||||||
|
/* Specify multiple folders that act like `./node_modules/@types`. */
|
||||||
|
"types": ["node"],
|
||||||
|
/* Specify type package names to be included without being referenced in a source file. */
|
||||||
|
/* JavaScript Support */
|
||||||
|
// "allowJs": true,
|
||||||
|
/* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"noImplicitAny": false,
|
||||||
|
/* Allow 'import x from y' when a module doesn't have a default export. */
|
||||||
|
"esModuleInterop": true,
|
||||||
|
/* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
|
||||||
|
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
/* Ensure that casing is correct in imports. */
|
||||||
|
/* Type Checking */
|
||||||
|
"strict": true
|
||||||
|
/* Enable all strict type-checking options. */
|
||||||
|
/* Completeness */
|
||||||
|
// "declaration": true // 生成*.d.ts
|
||||||
|
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||||
|
// "skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||||
|
},
|
||||||
|
"ts-node": {
|
||||||
|
"esm": true
|
||||||
|
}
|
||||||
|
}
|
14
package.json
Normal file
14
package.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "bilibili-open-demo",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
|
||||||
|
"license": "MIT",
|
||||||
|
"packageManager": "pnpm@10.6.2",
|
||||||
|
"type": "module"
|
||||||
|
}
|
1968
pnpm-lock.yaml
generated
Normal file
1968
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
3
pnpm-workspace.yaml
Normal file
3
pnpm-workspace.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
packages:
|
||||||
|
- 'packages/*'
|
||||||
|
- 'js-demo/*'
|
Loading…
x
Reference in New Issue
Block a user