go 权限 + websokets

This commit is contained in:
skong 2024-01-23 16:31:27 +08:00
parent 05a5942fca
commit e68e01edc9
36 changed files with 2643 additions and 0 deletions

1
.gitignore vendored
View File

@ -21,3 +21,4 @@
# Go workspace file
go.work
dist/node_modules/**

45
common/log/log.go Normal file
View File

@ -0,0 +1,45 @@
package log
import (
"github.com/Fromsko/gouitls/logs"
"github.com/sirupsen/logrus"
)
var echo *logrus.Logger
func init() {
echo = logs.InitLogger()
}
func Info() func(...any) {
// for _, arg := range args {
// echo.Info(arg)
// }
return echo.Info
}
func Infof(k string, args ...any) {
echo.Infof(k, args...)
}
func Debug(args ...any) {
for _, arg := range args {
echo.Debug(arg)
}
}
func Warn(args ...any) {
for _, arg := range args {
echo.Warn(arg)
}
}
func Error(args ...any) {
for _, arg := range args {
echo.Error(arg)
}
}
func Errorf(f string, args ...any) {
echo.Errorf(f, args...)
}

22
common/tools/map.go Normal file
View File

@ -0,0 +1,22 @@
package tools
// 快速检测[指定字段]是否包含在哈希表中
func IsKeyInMap(key []string, dict any) bool {
for _, k := range key {
if _, ok := dict.(map[string]any)[k]; !ok {
return false
}
}
return true
}
/*
{
d := map[string]any{
"a": "",
"b": "",
"c": "",
}
fmt.Println(IsKeyInMap([]string{"a", "d"}, d))
}
*/

23
dist/.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# 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/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

4
dist/.proxyrc vendored Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
host_ip=$(cat /etc/resolv.conf |grep "nameserver" |cut -f 2 -d " ")
export ALL_PROXY="http://$host_ip:7890"

3
dist/.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
}

7
dist/.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
"emmet.includeLanguages.vue": {
"vue": "html",
"vuex": "html",
"javascript": "html"
}
}

9
dist/LICENSE vendored Normal file
View File

@ -0,0 +1,9 @@
MIT License
Copyright (c) 2024 skong
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

18
dist/README.md vendored Normal file
View File

@ -0,0 +1,18 @@
# Vue 3 + TypeScript + Vite
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
## Recommended IDE Setup
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
## Type Support For `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
1. Disable the built-in TypeScript Extension
1. Run `Extensions: Show Built-in Extensions` from VSCode's command palette
2. Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.

9
dist/auto-imports.d.ts vendored Normal file
View File

@ -0,0 +1,9 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
export {}
declare global {
}

23
dist/components.d.ts vendored Normal file
View File

@ -0,0 +1,23 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
export {}
declare module 'vue' {
export interface GlobalComponents {
ChatBox: typeof import('./src/components/ChatBox.vue')['default']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElInput: typeof import('element-plus/es')['ElInput']
ElLi: typeof import('element-plus/es')['ElLi']
ElUl: typeof import('element-plus/es')['ElUl']
HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
LogCard: typeof import('./src/components/LogCard.vue')['default']
Login: typeof import('./src/components/Login.vue')['default']
}
export interface ComponentCustomProperties {
vInfiniteScroll: typeof import('element-plus/es')['ElInfiniteScroll']
}
}

13
dist/index.html vendored Normal file
View File

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue + TS</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

30
dist/package.json vendored Normal file
View File

@ -0,0 +1,30 @@
{
"name": "mind-map",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"@types/node": "^20.11.5",
"@vitejs/plugin-vue": "^4.5.2",
"axios": "^1.6.5",
"typescript": "^5.2.2",
"vite": "^5.0.8",
"vue": "^3.3.11",
"vue-tsc": "^1.8.25",
"websocket": "^1.0.34"
},
"devDependencies": {
"@types/node": "^20.11.5",
"@vitejs/plugin-vue": "^4.5.2",
"typescript": "^5.2.2",
"unplugin-auto-import": "^0.17.3",
"unplugin-vue-components": "^0.26.0",
"vite": "^5.0.8",
"vue-tsc": "^1.8.25"
}
}

1308
dist/pnpm-lock.yaml vendored Normal file

File diff suppressed because it is too large Load Diff

1
dist/public/vite.svg vendored Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

33
dist/src/App.vue vendored Normal file
View File

@ -0,0 +1,33 @@
<template>
<div id="app">
<LogCard />
<ChatBox />
</div>
</template>
<script lang="ts">
import ChatBox from './components/ChatBox.vue'
import LogCard from './components/LogCard.vue'
export default {
name: 'App',
components: {
LogCard,
ChatBox,
},
}
</script>
<style>
#app {
top: 0;
right: 0;
bottom: 0;
left: 0;
position: fixed;
/* display: flex; */
/* justify-content: center; */
/* align-items: center; */
background: linear-gradient(50deg, #75accb, #cce4d7);
}
</style>

1
dist/src/assets/vue.svg vendored Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

After

Width:  |  Height:  |  Size: 496 B

33
dist/src/components/ChatBox.vue vendored Normal file
View File

@ -0,0 +1,33 @@
<template>
<div class="chat-box">
<h2>发送消息</h2>
<el-input v-model="message" placeholder="输入消息..." />
<el-button @click="sendMessage" type="primary">发送</el-button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const message = ref('')
function sendMessage() {
// WebSocket
if (message.value.trim() !== '') {
//
message.value = ''
}
}
</script>
<style scoped>
.chat-box {
background-color: #f5f5f5;
border-radius: 8px;
padding: 16px;
}
h2 {
margin-top: 0;
}
</style>

38
dist/src/components/HelloWorld.vue vendored Normal file
View File

@ -0,0 +1,38 @@
<script setup lang="ts">
import { ref } from 'vue'
defineProps<{ msg: string }>()
const count = ref(0)
</script>
<template>
<h1>{{ msg }}</h1>
<div class="card">
<button type="button" @click="count++">count is {{ count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test HMR
</p>
</div>
<p>
Check out
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
>create-vue</a
>, the official Vue + Vite starter
</p>
<p>
Install
<a href="https://github.com/vuejs/language-tools" target="_blank">Volar</a>
in your IDE for a better DX
</p>
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
</template>
<style scoped>
.read-the-docs {
color: #888;
}
</style>

192
dist/src/components/LogCard.vue vendored Normal file
View File

@ -0,0 +1,192 @@
<template>
<div class="log-container">
<h2>实时日志</h2>
<ElCard class="log-card">
<ul class="infinite-list" style="overflow: auto">
<li
v-for="(log, index) in logs"
:key="index"
class="infinite-list-item"
>
类型: {{ log.type }} 消息: {{ log.msg }}
</li>
</ul>
</ElCard>
<ElCard class="send-card">
<h3>发送消息</h3>
<ElCard>
<div class="input-group">
<label for="type">type:</label>
<ElInput id="type" v-model="message.type" />
</div>
<div class="input-group">
<label for="msg">msg:</label>
<ElInput id="msg" v-model="message.msg" @keydown.enter="sendData" />
</div>
</ElCard>
<div class="button-group">
<ElButton
type="primary"
:disabled="Boolean(!socket || !message.msg)"
@click="sendData"
>
发送数据
</ElButton>
<ElButton type="success" :disabled="Boolean(socket)" @click="startConn"
>启动连接</ElButton
>
<ElButton type="danger" :disabled="Boolean(!socket)" @click="closeConn"
>关闭连接</ElButton
>
</div>
</ElCard>
</div>
</template>
<script setup lang="ts">
import { ElNotification } from 'element-plus'
import { ref } from 'vue'
const logs = ref<{ type: number; msg: string }[]>([])
const socket = ref<WebSocket | null>(null)
const message = ref<{ type: string; msg: string }>({ type: 'ping', msg: '' })
const sendData = () => {
if (!message.value.type || !message.value.msg) {
ElNotification({
title: '错误',
message: '请填写完整的类型和消息',
type: 'error',
position: 'top-right',
duration: 3000,
})
return
}
const jsonData = JSON.stringify(message.value)
if (socket.value) {
socket.value.send(jsonData)
message.value.msg = ''
}
}
let closeConn = () => {
if (socket.value) {
socket.value.close()
socket.value = null
}
}
const startConn = () => {
socket.value = new WebSocket('ws://localhost:9090/api/ws/')
socket.value.onopen = () => {
ElNotification({
title: 'WebSocket连接已建立!',
type: 'success',
position: 'top-right',
duration: 3000,
})
}
socket.value.onmessage = (event) => {
const log = JSON.parse(event.data)
logs.value.push(log)
if (log.type != 'ping') {
ElNotification({
title: '新的日志',
message: log.msg,
type: 'success',
position: 'top-right',
duration: 3000,
})
}
}
socket.value.onclose = () => {
ElNotification({
title: '连接已经关闭!',
type: 'success',
position: 'top-right',
duration: 3000,
})
}
socket.value.onerror = (error) => {
console.error('WebSocket连接发生错误:', error)
}
}
</script>
<style scoped>
.log-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
}
.log-card {
background-color: #f5f5f5;
border-radius: 8px;
padding: 16px;
margin-bottom: 16px;
/* width: 35vw; */
width: 350px;
}
.send-card {
background-color: #f5f5f5;
border-radius: 8px;
padding: 16px;
margin-bottom: 16px;
width: 420px;
}
h2,
h3 {
margin-top: 0;
text-align: center;
}
.infinite-list {
height: 150px;
padding: 0;
margin: 0;
list-style: none;
overflow: auto;
}
.infinite-list-item {
display: flex;
align-items: center;
justify-content: center;
height: 35px;
margin: 3px;
color: var(--el-color-primary);
background: linear-gradient(50deg, #75accb, #cce4d7);
}
.input-group {
display: flex;
align-items: center;
margin-bottom: 8px;
}
.input-group label {
margin-right: 10px;
}
.button-group {
display: flex;
justify-content: center;
margin-top: 16px;
}
</style>

185
dist/src/components/Login.vue vendored Normal file
View File

@ -0,0 +1,185 @@
<template>
<div class="login-box">
<div class="owl" :class="{ password: isPasswordFocused }">
<div class="hand"></div>
<div class="hand hand-r"></div>
<div class="arms">
<div class="arm"></div>
<div class="arm arm-r"></div>
</div>
</div>
<div class="input-box">
<input
type="text"
placeholder="账号"
v-model="userAuth.account"
:disabled="userAuth.isInput"
/>
<input
type="password"
placeholder="密码"
v-model="userAuth.password"
:disabled="userAuth.isInput"
@focus="handlePasswordFocus"
@blur="handlePasswordBlur"
@keyup.enter="handleLogin"
/>
<button @click="handleLogin" :disabled="userAuth.isInput">登录</button>
</div>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
let userAuth = reactive({
account: '',
password: '',
isInput: false,
})
let isPasswordFocused = ref(false)
let handlePasswordFocus = () => {
isPasswordFocused.value = true
}
let handlePasswordBlur = () => {
isPasswordFocused.value = false
}
let handleLogin = () => {
if (!userAuth.isInput) {
userAuth.isInput = true
//
setTimeout(() => {
console.log('登录成功,重定向到其他页面')
userAuth.isInput = false
}, 2000)
} else {
userAuth.isInput = false
console.log('登录失败,重新启用按钮和回车键')
}
console.log(userAuth.isInput)
}
</script>
<style scoped>
.login-box {
/* 相对定位 */
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 320px;
padding: 20px;
box-shadow: 0 0 10px;
border-radius: 15px;
background: linear-gradient(200deg, #72afd3, #96fbc4);
justify-content: center;
}
.input-box {
/* 弹性布局 垂直排列 */
display: flex;
flex-direction: column;
}
.input-box input {
height: 40px;
border-radius: 3px;
/* 缩进15像素 */
text-indent: 15px;
outline: none;
border: none;
margin-bottom: 15px;
}
.input-box input:focus {
outline: 1px solid rgb(36, 197, 189);
}
.input-box button {
width: 9em;
height: 3em;
border: none;
border-radius: 30em;
font-size: 15px;
font-family: inherit;
position: relative;
overflow: hidden;
z-index: 1;
justify-content: center;
text-align: center;
background-color: lightseagreen;
margin: 0 auto; /* 水平居中 */
display: block; /* 使按钮成为块级元素 */
transition: background-color 0.3s ease; /* 添加简单的背景色过渡效果 */
}
.input-box button:hover {
background-color: #3cb371; /* 悬停时的背景色 */
}
/* 接下来是猫头鹰的样式 */
.owl {
width: 211px;
height: 108px;
/* 背景图片 */
background: url('@/assets/owl-login.png') no-repeat;
/* 绝对定位 */
position: absolute;
top: -100px;
/* 水平居中 */
left: 50%;
transform: translateX(-50%);
}
.owl .hand {
width: 34px;
height: 34px;
border-radius: 40px;
background-color: #472d20;
/* 绝对定位 */
position: absolute;
left: 12px;
bottom: -8px;
/* 沿Y轴缩放0.6倍(压扁) */
transform: scaleY(0.6);
/* 动画过渡 */
transition: 0.3s ease-out;
}
.owl .hand.hand-r {
left: 170px;
}
.owl.password .hand {
transform: translateX(42px) translateY(-15px) scale(0.7);
}
.owl.password .hand.hand-r {
transform: translateX(-42px) translateY(-15px) scale(0.7);
}
.owl .arms {
position: absolute;
top: 58px;
width: 100%;
height: 41px;
overflow: hidden;
}
.owl .arms .arm {
width: 40px;
height: 65px;
position: absolute;
left: 20px;
top: 40px;
background: url('@/assets/owl-login-arm.png') no-repeat;
transform: rotate(-20deg);
transition: 0.3s ease-out;
}
.owl .arms .arm.arm-r {
transform: rotate(20deg) scaleX(-1);
left: 158px;
}
.owl.password .arms .arm {
transform: translateY(-40px) translateX(40px);
}
.owl.password .arms .arm.arm-r {
transform: translateY(-40px) translateX(-40px) scaleX(-1);
}
</style>

10
dist/src/main.ts vendored Normal file
View File

@ -0,0 +1,10 @@
// main.ts
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')

8
dist/src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1,8 @@
/// <reference types="vite/client" />
declare module "*.vue" {
import type { DefineComponent } from "vue";
const vueComponent: DefineComponent<{}, {}, any>;
export default vueComponent;
}

28
dist/tsconfig.json vendored Normal file
View File

@ -0,0 +1,28 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
/* Elm */
"types": ["element-plus/global"],
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}

10
dist/tsconfig.node.json vendored Normal file
View File

@ -0,0 +1,10 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

29
dist/vite.config.ts vendored Normal file
View File

@ -0,0 +1,29 @@
import vue from '@vitejs/plugin-vue';
import { fileURLToPath, URL } from 'node:url';
import AutoImport from 'unplugin-auto-import/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
import Components from 'unplugin-vue-components/vite';
import { defineConfig } from 'vite';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
optimizeDeps: {
include: ['element-plus'] // 添加需要预打包的依赖项
}
})

41
go.mod Normal file
View File

@ -0,0 +1,41 @@
module go-auth
go 1.21.5
require github.com/sirupsen/logrus v1.9.3
require (
github.com/bytedance/sonic v1.10.2 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.0 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.17.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
golang.org/x/arch v0.7.0 // indirect
golang.org/x/crypto v0.18.0 // indirect
golang.org/x/sys v0.16.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
require (
github.com/Fromsko/gouitls v1.2.7
github.com/gin-gonic/gin v1.9.1
github.com/gorilla/websocket v1.5.1
golang.org/x/net v0.20.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/protobuf v1.32.0 // indirect
)

103
go.sum Normal file
View File

@ -0,0 +1,103 @@
github.com/Fromsko/gouitls v1.2.7 h1:clBl9mvELvV0ls6mxhqFPLDhJb5J3Db4+beR5TL93UY=
github.com/Fromsko/gouitls v1.2.7/go.mod h1:pnx9wA17MZUcP8T93DL+CH++RPCVX/SVByCJjW5mJO0=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE=
github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo=
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74=
github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

15
main.go Normal file
View File

@ -0,0 +1,15 @@
package main
import (
"go-auth/routers"
"github.com/gin-gonic/gin"
)
func main() {
engine := gin.New()
routers.InitRouters(engine)
engine.Run(":9090")
}

46
middleware/base/base.go Normal file
View File

@ -0,0 +1,46 @@
package base
import (
"net/http"
"github.com/gin-gonic/gin"
)
/*
定义基础中间件
*/
// Cors 跨域请求
func Cors() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method
origin := c.Request.Header.Get("Origin")
if origin != "" {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
c.Header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization")
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type")
c.Header("Access-Control-Allow-Credentials", "true")
}
if method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
}
c.Next()
}
}
// Add 函数用于计算两个整数的和
//
// 参数:
// - a: 第一个整数
// - b: 第二个整数
//
// 返回:
// - sum: 两个整数的和
func Add(a, b int) int {
router := gin.New()
router.Use(Cors())
return a + b
}

9
routers/class/class.go Normal file
View File

@ -0,0 +1,9 @@
package class
import "github.com/gin-gonic/gin"
func Register(api *gin.RouterGroup) {
user := api.Group("/user")
_ = user
}

30
routers/router.go Normal file
View File

@ -0,0 +1,30 @@
package routers
import (
"go-auth/routers/class"
"go-auth/routers/stream"
"go-auth/routers/user"
"github.com/gin-gonic/gin"
)
// InitRouters 注册基础路由
func InitRouters(engine *gin.Engine) {
v1 := engine.Group("/api/v1")
{
user.Register(v1)
class.Register(v1)
}
ws := engine.Group("/api/ws/")
{
pool := stream.NewConnectionPool(
stream.WithConnected(nil),
stream.WithExamStart(nil),
stream.WithExamStop(nil),
stream.WithDestroy(nil),
)
ws.GET("", stream.MonitorWS(pool)) // 创建WebSocket连接池
}
}

86
routers/stream/handler.go Normal file
View File

@ -0,0 +1,86 @@
package stream
import (
"github.com/gin-gonic/gin"
)
// registerEvent 注册事件
func (pool *ConnectionPool) registerEvent(eventName string, handler ConnEvent) {
pool.Events[eventName] = append(pool.Events[eventName], handler)
}
// triggerEvent 事件触发
func (pool *ConnectionPool) triggerEvent(event string, connection *Connection, data gin.H) {
if handlers, ok := pool.Events[event]; ok {
for _, handler := range handlers {
handler(connection, data)
}
}
}
// defaultEvent 默认事件
func (pool *ConnectionPool) defaultEvent(eventName string) {
pool.Events[eventName] = append(pool.Events[eventName], func(obj *Connection, data gin.H) {
log.Infof("Events: [%s]", eventName)
})
}
// WithExamStart 考试开始
func WithExamStart(event ConnEvent) PoolOption {
if event == nil {
event = func(obj *Connection, data gin.H) {
log.Info("考试开始")
}
}
return func(cp *ConnectionPool) {
cp.registerEvent("exam_start", event)
}
}
// WithExamStop 考试结束
func WithExamStop(event ConnEvent) PoolOption {
if event == nil {
event = func(obj *Connection, data gin.H) {
log.Info("考试结束")
}
}
return func(cp *ConnectionPool) {
cp.registerEvent("exam_stop", event)
}
}
// WithConnected 连接成功
func WithConnected(event ConnEvent) PoolOption {
if event == nil {
event = func(obj *Connection, data gin.H) {
log.Info("连接成功")
}
}
return func(cp *ConnectionPool) {
cp.registerEvent("connected", event)
}
}
// WithDestroy 连接注销
func WithDestroy(event ConnEvent) PoolOption {
if event == nil {
event = func(obj *Connection, data gin.H) {
log.Info("连接注销")
}
}
return func(cp *ConnectionPool) {
cp.registerEvent("destroy", event)
}
}
// WithNotify 通知
func WithNotify(event ConnEvent) PoolOption {
if event == nil {
event = func(obj *Connection, data gin.H) {
log.Info("通知")
}
}
return func(cp *ConnectionPool) {
cp.registerEvent("notify", event)
}
}

View File

@ -0,0 +1,57 @@
package stream
import (
"time"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
)
// NewConnectionPool 初始化一个连接池
func NewConnectionPool(opts ...PoolOption) *ConnectionPool {
pool := &ConnectionPool{
Connections: make(map[*websocket.Conn]*Connection),
Broadcast: make(chan gin.H),
Register: make(chan *Connection),
Unregister: make(chan *Connection),
Events: make(map[string][]ConnEvent),
PingInterval: 5 * time.Second,
}
for _, opt := range opts {
opt(pool)
}
for _, k := range []string{"connected", "destroy"} {
if _, ok := pool.Events[k]; !ok {
pool.defaultEvent(k)
}
}
return pool
}
func MonitorWS(pool *ConnectionPool) gin.HandlerFunc {
// 处理连接池中的连接和事件
go HandleConnections(pool)
return func(ctx *gin.Context) {
ctx.Set("user_id", "111")
// 处理WebSocket连接请求
handleWebSocket(ctx, pool)
}
}
/*
// pool.OnConnected(func(obj *Connection, data gin.H) {
// log.Info("is connected")
// data["type"] = "ping"
// data["msg"] = "ddd"
// obj.Conn.WriteJSON(data)
// })
// pool.OnDestory(func(obj *Connection, data gin.H) {
// log.Info("被注销了!")
// log.Info(len(pool.Connections))
// })
*/

164
routers/stream/stream.go Normal file
View File

@ -0,0 +1,164 @@
package stream
/*
定义 websoket MonitorWS
*/
import (
"encoding/json"
"net/http"
"time"
"github.com/Fromsko/gouitls/logs"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
)
// 定义 WebSocket 连接对象
type Connection struct {
UserID string
Role string
Conn *websocket.Conn
}
// 定义 WebSocket 连接事件处理
type ConnEvent func(obj *Connection, data gin.H)
// 连接池可选参数
type PoolOption func(*ConnectionPool)
// 日志
var log = logs.InitLogger()
// 定义 WebSocket 连接池
type ConnectionPool struct {
Connections map[*websocket.Conn]*Connection
Broadcast chan gin.H
Register chan *Connection
Unregister chan *Connection
Events map[string][]ConnEvent
PingInterval time.Duration
}
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true
},
}
// 处理WebSocket连接请求
func handleWebSocket(ctx *gin.Context, pool *ConnectionPool) {
// 升级HTTP连接为WebSocket连接
conn, err := upgrader.Upgrade(ctx.Writer, ctx.Request, nil)
if err != nil {
log.Error(err)
conn.Close()
return
}
// 处理连接请求
// 从数据库中获取用户信息包括UserID和Role
// 如果信息不存在或者不符合要求,则拒绝连接
_, ok := ctx.Get("user_id")
if !ok {
log.Error("非法连接")
conn.Close()
return
}
// 创建连接对象
connection := &Connection{
UserID: "", //userId.(string),
Role: "", // ctx.MustGet("role").(string)
Conn: conn,
}
// 将连接对象注册到连接池中
pool.Register <- connection
// 处理连接断开事件
defer func() {
pool.Unregister <- connection
}()
// 处理接收到的消息
for {
// 检查连接是否已关闭
if _, r, err := conn.NextReader(); err != nil {
// 判断错误类型是否为websocket: close 1001 (going away)
if websocket.IsCloseError(err, websocket.CloseGoingAway) {
log.Info("连接已关闭")
break
}
if websocket.IsCloseError(err, websocket.CloseNoStatusReceived) {
log.Info("连接已关闭")
break
}
log.Errorf("处理接收到的消息错误: %s", err)
break
} else {
var message gin.H
err := json.NewDecoder(r).Decode(&message)
if err != nil {
log.Info(err)
log.Errorf("处理接收到的消息错误: %s", err)
break
}
log.Info(message)
// 示例:将消息发送给所有连接
pool.Broadcast <- message
}
}
}
// 处理连接池中的连接和事件
func HandleConnections(pool *ConnectionPool) {
for {
select {
// 处理连接注册事件
case connection := <-pool.Register:
pool.Connections[connection.Conn] = connection
// 触发连接加入事件
pool.triggerEvent("connected", connection, gin.H{})
// 处理连接断开事件
case connection := <-pool.Unregister:
if _, ok := pool.Connections[connection.Conn]; ok {
delete(pool.Connections, connection.Conn)
// 触发连接断开事件
pool.triggerEvent("destroy", connection, gin.H{})
}
// 处理广播消息
case message := <-pool.Broadcast:
// 将消息发送给所有连接
for conn := range pool.Connections {
err := conn.WriteJSON(message)
if err != nil {
log.Errorf("广播消息错误: %s", err)
return
}
}
}
}
}
// // 注册事件处理函数
// func (pool *ConnectionPool) On(event string, handler ConnEvent) {
// pool.Events[event] = append(pool.Events[event], handler)
// }
// func (pool *ConnectionPool) OnConnected(handler ConnEvent) {
// pool.Events["connected"] = append(pool.Events["connected"], handler)
// }
// func (pool *ConnectionPool) OnDestory(handler ConnEvent) {
// pool.Events["destroy"] = append(pool.Events["destroy"], handler)
// }

9
routers/user/user.go Normal file
View File

@ -0,0 +1,9 @@
package user
import "github.com/gin-gonic/gin"
func Register(api *gin.RouterGroup) {
// _ = user
}