mirror of
https://github.com/xinliangnote/go-gin-api.git
synced 2024-04-21 12:31:46 +00:00
feature(1.2.8): 新增 websocket 模块
- import gorilla/websocket - 新增 实用工具箱->WebSocket 栏目
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -0,0 +1,32 @@
|
||||
document.write('<script type="text/javascript" src="../../bootstrap/js/bootstrap-notify/bootstrap-notify.min.js"></script>');
|
||||
|
||||
function SuccessNotify(content) {
|
||||
$.notify({
|
||||
icon: "mdi mdi-alert",
|
||||
title: "",
|
||||
message: content,
|
||||
url: "",
|
||||
target: ""
|
||||
}, {
|
||||
type: "success",
|
||||
allow_dismiss: true,
|
||||
newest_on_top: false,
|
||||
placement: {
|
||||
from: "top",
|
||||
align: "right",
|
||||
},
|
||||
offset: {
|
||||
x: "20",
|
||||
y: "20"
|
||||
},
|
||||
spacing: "10",
|
||||
z_index: "1031",
|
||||
delay: "3000",
|
||||
animate: {
|
||||
enter: "animated fadeInDown",
|
||||
exit: "animated fadeOutUp"
|
||||
},
|
||||
onClosed: null,
|
||||
mouse_over: null
|
||||
});
|
||||
}
|
||||
@@ -98,6 +98,9 @@
|
||||
</div>
|
||||
<input type="text" class="form-control" id="mysql_name" value="{{ .Config.MySQL.Write.Name }}"
|
||||
placeholder="请输入数据库名">
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text"><code>请确保此数据库已存在!</code></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
|
||||
@@ -95,6 +95,21 @@
|
||||
<p>5、退出重新登录,即可以看到自己新增的模块。</p>
|
||||
|
||||
<hr/>
|
||||
|
||||
<p class="h6">
|
||||
<span class="badge badge-pill badge-warning">v1.2.7</span>
|
||||
->
|
||||
<span class="badge badge-pill badge-warning">v1.2.8</span>
|
||||
</p>
|
||||
|
||||
<p>1、源代码升级:
|
||||
<mark>拉取最新代码,覆盖旧版本代码即可。</mark>
|
||||
</p>
|
||||
<p>2、系统管理员->菜单管理,新增侧边栏:实用工具箱 -> WebSocket,同时添加菜单栏的功能权限。</p>
|
||||
<p>3、系统管理员->管理员,对管理员进行菜单授权。</p>
|
||||
<p>4、退出重新登录,即可以看到自己新增的模块。</p>
|
||||
|
||||
<hr/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/animate.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<div class="card-title">WebSocket 示例</div>
|
||||
</header>
|
||||
<div class="card-body">
|
||||
|
||||
<ul class="lyear-timeline lyear-timeline-left timeline-content">
|
||||
<li class="lyear-timeline-item">
|
||||
<div class="lyear-timeline-item-dot">
|
||||
<span class="badge"></span>
|
||||
</div>
|
||||
<div class="lyear-timeline-item-content">
|
||||
<p class="mb-1"><strong class="connect-content"></strong></p>
|
||||
<p class="mb-0 connect-url"></p>
|
||||
<p>
|
||||
<time class="mb-3 connect-time"></time>
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">WebSocket 示例</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="tab" href="#send_message"
|
||||
aria-selected="true">发送消息</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade active show" id="send_message">
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">消息内容</span>
|
||||
</div>
|
||||
<input type="text" class="form-control" id="message_content" placeholder="请输入消息内容">
|
||||
</div>
|
||||
|
||||
<button type="button" id="btnSend" class="btn btn-primary">发送</button>
|
||||
<button type="button" id="btnSending" class="btn btn-primary" disabled
|
||||
style="display: none">
|
||||
<span class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true"></span>
|
||||
发送中...
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-notify/notify.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
const ws = new WebSocket("ws://127.0.0.1:9999/socket/system/message");
|
||||
|
||||
//连接打开时触发
|
||||
ws.onopen = function (evt) {
|
||||
if (evt.isTrusted) {
|
||||
$(".badge").addClass("badge-success");
|
||||
$(".connect-content").html("Socket 连接成功");
|
||||
$(".connect-url").html("连接地址:" + evt.currentTarget.url);
|
||||
$(".connect-time").html(getCSTTime());
|
||||
} else {
|
||||
$(".badge").addClass("badge-danger");
|
||||
$(".connect-content").html("Socket 连接失败");
|
||||
$(".connect-url").html("请确认连接地址是否正确");
|
||||
$(".connect-time").html(getCSTTime());
|
||||
}
|
||||
};
|
||||
|
||||
//接收到消息时触发
|
||||
ws.onmessage = function (evt) {
|
||||
const messageData = JSON.parse(evt.data);
|
||||
|
||||
let html = '<li class="lyear-timeline-item">';
|
||||
html += '<div class="lyear-timeline-item-dot"><span class="badge badge-success"></span></div>';
|
||||
html += '<div class="lyear-timeline-item-content">';
|
||||
html += '<p class="mb-1"><strong>' + messageData.username + '</strong></p>';
|
||||
html += '<p class="mb-0">' + messageData.message + '</p>';
|
||||
html += '<p><time class="mb-3">' + messageData.time + '</time></p>';
|
||||
html += '</div></li>';
|
||||
|
||||
$(".timeline-content").prepend(html);
|
||||
};
|
||||
|
||||
//连接关闭时触发
|
||||
ws.onclose = function (evt) {
|
||||
|
||||
let html = '<li class="lyear-timeline-item">';
|
||||
html += '<div class="lyear-timeline-item-dot"><span class="badge badge-muted"></span></div>';
|
||||
html += '<div class="lyear-timeline-item-content">';
|
||||
html += '<p class="mb-1"><strong>Socket 连接关闭</strong></p>';
|
||||
html += '<p class="mb-0">连接已关闭</p>';
|
||||
html += '<p><time class="mb-3">' + getCSTTime() + '</time></p>';
|
||||
html += '</div></li>';
|
||||
|
||||
$(".timeline-content").prepend(html);
|
||||
|
||||
};
|
||||
|
||||
function getCSTTime() {
|
||||
let date = new Date();
|
||||
let datetime = date.getFullYear() + "-" // "年"
|
||||
+ ((date.getMonth() + 1) > 10 ? (date.getMonth() + 1) : "0" + (date.getMonth() + 1)) + "-" // "月"
|
||||
+ (date.getDate() < 10 ? "0" + date.getDate() : date.getDate()) + " " // "日"
|
||||
+ (date.getHours() < 10 ? "0" + date.getHours() : date.getHours()) + ":" // "小时"
|
||||
+ (date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes()) + ":" // "分钟"
|
||||
+ (date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds()); // "秒"
|
||||
|
||||
return datetime;
|
||||
}
|
||||
|
||||
$('#btnSend').on('click', function () {
|
||||
const messageContent = $("#message_content").val();
|
||||
if (messageContent === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入消息内容。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
AjaxForm(
|
||||
"POST",
|
||||
"/api/tool/send_message",
|
||||
{message: messageContent},
|
||||
function () {
|
||||
$(this).hide();
|
||||
$("#btnSendLoading").show();
|
||||
},
|
||||
function (data) {
|
||||
$("#btnSendLoading").hide();
|
||||
$("#btnSend").show();
|
||||
|
||||
if (data.status === 'OK') {
|
||||
$("#message_content").val("");
|
||||
SuccessNotify("消息发送成功");
|
||||
}
|
||||
},
|
||||
function (response) {
|
||||
$("#btnSendLoading").hide();
|
||||
$("#btnSend").show();
|
||||
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -2,7 +2,7 @@ package configs
|
||||
|
||||
const (
|
||||
// ProjectVersion 项目版本
|
||||
ProjectVersion = "v1.2.7"
|
||||
ProjectVersion = "v1.2.8"
|
||||
|
||||
// ProjectName 项目名称
|
||||
ProjectName = "go-gin-api"
|
||||
|
||||
+103
-4
@@ -177,9 +177,9 @@ var doc = `{
|
||||
},
|
||||
"/api/admin/login": {
|
||||
"post": {
|
||||
"description": "管理员登出",
|
||||
"description": "管理员登录",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
"multipart/form-data"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
@@ -187,12 +187,28 @@ var doc = `{
|
||||
"tags": [
|
||||
"API.admin"
|
||||
],
|
||||
"summary": "管理员登出",
|
||||
"summary": "管理员登录",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "用户名",
|
||||
"name": "username",
|
||||
"in": "formData",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "密码",
|
||||
"name": "password",
|
||||
"in": "formData",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/admin_handler.logoutResponse"
|
||||
"$ref": "#/definitions/admin_handler.loginResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
@@ -1102,6 +1118,42 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"patch": {
|
||||
"description": "手动执行单条任务",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"API.cron"
|
||||
],
|
||||
"summary": "手动执行单条任务",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "hashId",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/cron_handler.detailResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/cron/used": {
|
||||
@@ -1907,6 +1959,44 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/tool/send_message": {
|
||||
"post": {
|
||||
"description": "发送消息",
|
||||
"consumes": [
|
||||
"multipart/form-data"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"API.tool"
|
||||
],
|
||||
"summary": "发送消息",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "消息内容",
|
||||
"name": "message",
|
||||
"in": "formData",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/tool_handler.sendMessageResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
@@ -2794,6 +2884,15 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"tool_handler.sendMessageResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"description": "状态",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tool_handler.tableColumn": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
+103
-4
@@ -160,9 +160,9 @@
|
||||
},
|
||||
"/api/admin/login": {
|
||||
"post": {
|
||||
"description": "管理员登出",
|
||||
"description": "管理员登录",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
"multipart/form-data"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
@@ -170,12 +170,28 @@
|
||||
"tags": [
|
||||
"API.admin"
|
||||
],
|
||||
"summary": "管理员登出",
|
||||
"summary": "管理员登录",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "用户名",
|
||||
"name": "username",
|
||||
"in": "formData",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "密码",
|
||||
"name": "password",
|
||||
"in": "formData",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/admin_handler.logoutResponse"
|
||||
"$ref": "#/definitions/admin_handler.loginResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
@@ -1085,6 +1101,42 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"patch": {
|
||||
"description": "手动执行单条任务",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"API.cron"
|
||||
],
|
||||
"summary": "手动执行单条任务",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "hashId",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/cron_handler.detailResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/cron/used": {
|
||||
@@ -1890,6 +1942,44 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/tool/send_message": {
|
||||
"post": {
|
||||
"description": "发送消息",
|
||||
"consumes": [
|
||||
"multipart/form-data"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"API.tool"
|
||||
],
|
||||
"summary": "发送消息",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "消息内容",
|
||||
"name": "message",
|
||||
"in": "formData",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/tool_handler.sendMessageResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/code.Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
@@ -2777,6 +2867,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"tool_handler.sendMessageResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"description": "状态",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tool_handler.tableColumn": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
+70
-4
@@ -615,6 +615,12 @@ definitions:
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
tool_handler.sendMessageResponse:
|
||||
properties:
|
||||
status:
|
||||
description: 状态
|
||||
type: string
|
||||
type: object
|
||||
tool_handler.tableColumn:
|
||||
properties:
|
||||
column_comment:
|
||||
@@ -776,20 +782,31 @@ paths:
|
||||
/api/admin/login:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 管理员登出
|
||||
- multipart/form-data
|
||||
description: 管理员登录
|
||||
parameters:
|
||||
- description: 用户名
|
||||
in: formData
|
||||
name: username
|
||||
required: true
|
||||
type: string
|
||||
- description: 密码
|
||||
in: formData
|
||||
name: password
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/admin_handler.logoutResponse'
|
||||
$ref: '#/definitions/admin_handler.loginResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
summary: 管理员登出
|
||||
summary: 管理员登录
|
||||
tags:
|
||||
- API.admin
|
||||
/api/admin/menu:
|
||||
@@ -1368,6 +1385,30 @@ paths:
|
||||
summary: 获取单条任务详情
|
||||
tags:
|
||||
- API.cron
|
||||
patch:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 手动执行单条任务
|
||||
parameters:
|
||||
- description: hashId
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/cron_handler.detailResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
summary: 手动执行单条任务
|
||||
tags:
|
||||
- API.cron
|
||||
/api/cron/{id}:
|
||||
post:
|
||||
consumes:
|
||||
@@ -1904,4 +1945,29 @@ paths:
|
||||
summary: HashIds 加密
|
||||
tags:
|
||||
- API.tool
|
||||
/api/tool/send_message:
|
||||
post:
|
||||
consumes:
|
||||
- multipart/form-data
|
||||
description: 发送消息
|
||||
parameters:
|
||||
- description: 消息内容
|
||||
in: formData
|
||||
name: message
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/tool_handler.sendMessageResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/code.Failure'
|
||||
summary: 发送消息
|
||||
tags:
|
||||
- API.tool
|
||||
swagger: "2.0"
|
||||
|
||||
@@ -18,6 +18,7 @@ require (
|
||||
github.com/go-playground/validator/v10 v10.4.1
|
||||
github.com/go-redis/redis/v7 v7.4.0
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/jakecoffman/cron v0.0.0-20190106200828-7e2009c226a5
|
||||
github.com/jinzhu/gorm v1.9.16
|
||||
github.com/onsi/ginkgo v1.14.2 // indirect
|
||||
|
||||
@@ -107,6 +107,7 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
|
||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
@@ -146,6 +147,7 @@ github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/gzip v0.0.1 h1:ezvKOL6jH+jlzdHNE4h9h8q8uMpDQjyl0NN0Jd7jozc=
|
||||
github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w=
|
||||
@@ -501,6 +503,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
|
||||
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
|
||||
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||
@@ -511,6 +514,7 @@ github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNX
|
||||
github.com/shirou/gopsutil v3.21.2+incompatible h1:U+YvJfjCh6MslYlIAXvPtzhW3YZEtc9uncueUNpD/0A=
|
||||
github.com/shirou/gopsutil v3.21.2+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
@@ -571,8 +575,10 @@ github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLY
|
||||
github.com/ugorji/go/codec v1.1.13 h1:013LbFhocBoIqgHeIHKlV4JWYhqogATYWZhIcH0WHn4=
|
||||
github.com/ugorji/go/codec v1.1.13/go.mod h1:oNVt3Dq+FO91WNQ/9JnHKQP2QJxTzoN7wCBFCq1OeuU=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U=
|
||||
github.com/vektah/gqlparser/v2 v2.1.0 h1:uiKJ+T5HMGGQM2kRKQ8Pxw8+Zq9qhhZhz/lieYvCMns=
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package tool_handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/code"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/validation"
|
||||
"github.com/xinliangnote/go-gin-api/internal/websocket/socket_conn/system_message"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errno"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/time_parse"
|
||||
)
|
||||
|
||||
type sendMessageRequest struct {
|
||||
Message string `form:"message"` // 消息内容
|
||||
}
|
||||
|
||||
type sendMessageResponse struct {
|
||||
Status string `json:"status"` // 状态
|
||||
}
|
||||
|
||||
// SendMessage 发送消息
|
||||
// @Summary 发送消息
|
||||
// @Description 发送消息
|
||||
// @Tags API.tool
|
||||
// @Accept multipart/form-data
|
||||
// @Produce json
|
||||
// @Param message formData string true "消息内容"
|
||||
// @Success 200 {object} sendMessageResponse
|
||||
// @Failure 400 {object} code.Failure
|
||||
// @Router /api/tool/send_message [post]
|
||||
func (h *handler) SendMessage() core.HandlerFunc {
|
||||
type messageBody struct {
|
||||
Username string `json:"username"`
|
||||
Message string `json:"message"`
|
||||
Time string `json:"time"`
|
||||
}
|
||||
|
||||
return func(ctx core.Context) {
|
||||
req := new(sendMessageRequest)
|
||||
res := new(sendMessageResponse)
|
||||
if err := ctx.ShouldBindForm(req); err != nil {
|
||||
ctx.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
validation.Error(err)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
conn, err := system_message.GetConn()
|
||||
if err != nil {
|
||||
ctx.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.SocketConnectError,
|
||||
code.Text(code.SocketConnectError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
messageData := new(messageBody)
|
||||
messageData.Username = ctx.UserName()
|
||||
messageData.Message = req.Message
|
||||
messageData.Time = time_parse.CSTLayoutString()
|
||||
|
||||
messageJsonData, err := json.Marshal(messageData)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.SocketSendError,
|
||||
code.Text(code.SocketSendError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
err = conn.OnSend(messageJsonData)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(errno.NewError(
|
||||
http.StatusBadRequest,
|
||||
code.SocketSendError,
|
||||
code.Text(code.SocketSendError)).WithErr(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res.Status = "OK"
|
||||
ctx.Payload(res)
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,11 @@ type Handler interface {
|
||||
// @Tags API.tool
|
||||
// @Router /api/tool/data/mysql [post]
|
||||
SearchMySQL() core.HandlerFunc
|
||||
|
||||
// SendMessage 发送消息
|
||||
// @Tags API.tool
|
||||
// @Router /api/tool/send_message [post]
|
||||
SendMessage() core.HandlerFunc
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
|
||||
@@ -28,6 +28,8 @@ const (
|
||||
SendEmailError = 10117
|
||||
MySQLExecError = 10118
|
||||
GoVersionError = 10119
|
||||
SocketConnectError = 10120
|
||||
SocketSendError = 10121
|
||||
|
||||
AuthorizedCreateError = 20101
|
||||
AuthorizedListError = 20102
|
||||
|
||||
@@ -20,6 +20,8 @@ var enUSText = map[int]string{
|
||||
SendEmailError: "Failed to send mail",
|
||||
MySQLExecError: "SQL execution failed",
|
||||
GoVersionError: "Go Version mismatch",
|
||||
SocketConnectError: "Socket not connected",
|
||||
SocketSendError: "Socket message sending failed",
|
||||
|
||||
AuthorizedCreateError: "Failed to create caller",
|
||||
AuthorizedListError: "Failed to get caller list",
|
||||
|
||||
@@ -20,6 +20,8 @@ var zhCNText = map[int]string{
|
||||
SendEmailError: "发送邮件失败",
|
||||
MySQLExecError: "SQL 执行失败",
|
||||
GoVersionError: "Go 版本不满足要求",
|
||||
SocketConnectError: "Socket 未连接",
|
||||
SocketSendError: "Socket 消息发送失败",
|
||||
|
||||
AuthorizedCreateError: "创建调用方失败",
|
||||
AuthorizedListError: "获取调用方列表失败",
|
||||
|
||||
@@ -103,6 +103,9 @@ func NewHTTPServer(logger *zap.Logger, cronLogger *zap.Logger) (*Server, error)
|
||||
// 设置 GraphQL 路由
|
||||
setGraphQLRouter(r)
|
||||
|
||||
// 设置 Socket 路由
|
||||
setSocketRouter(r)
|
||||
|
||||
s := new(Server)
|
||||
s.Mux = mux
|
||||
s.Db = r.db
|
||||
|
||||
@@ -75,6 +75,7 @@ func setApiRouter(r *resource) {
|
||||
api.GET("/tool/data/dbs", toolHandler.Dbs())
|
||||
api.POST("/tool/data/tables", toolHandler.Tables())
|
||||
api.POST("/tool/data/mysql", toolHandler.SearchMySQL())
|
||||
api.POST("/tool/send_message", toolHandler.SendMessage())
|
||||
|
||||
// config
|
||||
configHandler := config_handler.New(r.logger, r.db, r.cache)
|
||||
@@ -84,10 +85,10 @@ func setApiRouter(r *resource) {
|
||||
cronHandler := cron_handler.New(r.logger, r.db, r.cache, r.cronServer)
|
||||
api.POST("/cron", cronHandler.Create())
|
||||
api.GET("/cron", cronHandler.List())
|
||||
api.GET("/cron/:id", cronHandler.Detail())
|
||||
api.POST("/cron/:id", cronHandler.Modify())
|
||||
api.GET("/cron/:id", core.AliasForRecordMetrics("/api/cron/detail"), cronHandler.Detail())
|
||||
api.POST("/cron/:id", core.AliasForRecordMetrics("/api/cron/modify"), cronHandler.Modify())
|
||||
api.PATCH("/cron/used", cronHandler.UpdateUsed())
|
||||
api.PATCH("/cron/exec/:id", cronHandler.Execute())
|
||||
api.PATCH("/cron/exec/:id", core.AliasForRecordMetrics("/api/cron/exec"), cronHandler.Execute())
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/websocket/socket_conn/system_message"
|
||||
)
|
||||
|
||||
func setSocketRouter(r *resource) {
|
||||
systemMessage := system_message.New(r.logger, r.db, r.cache)
|
||||
|
||||
// 无需记录日志
|
||||
socket := r.mux.Group("/socket", r.middles.DisableLog())
|
||||
{
|
||||
// 系统消息
|
||||
socket.GET("/system/message", systemMessage.Connect())
|
||||
}
|
||||
}
|
||||
@@ -81,6 +81,7 @@ func setWebRouter(r *resource) {
|
||||
web.GET("/tool/logs", toolHandler.LogsView())
|
||||
web.GET("/tool/cache", toolHandler.CacheView())
|
||||
web.GET("/tool/data", toolHandler.DataView())
|
||||
web.GET("/tool/websocket", toolHandler.WebsocketView())
|
||||
|
||||
// 后台任务
|
||||
web.GET("/cron/list", cronTaskHandler.ListView())
|
||||
|
||||
@@ -49,7 +49,8 @@ func CreateAdminMenuTableDataSql() (sql string) {
|
||||
sql += "(21, 1, 2, 'init'),"
|
||||
sql += "(22, 1, 22, 'init'),"
|
||||
sql += "(23, 1, 23, 'init'),"
|
||||
sql += "(24, 1, 24, 'init');"
|
||||
sql += "(24, 1, 24, 'init'),"
|
||||
sql += "(25, 1, 25, 'init');"
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -63,7 +63,8 @@ func CreateMenuTableDataSql() (sql string) {
|
||||
sql += "(21, 16, '接口指标', '/metrics', '', 2, 706, 'init'),"
|
||||
sql += "(22, 16, '服务升级', '/upgrade', '', 2, 701, 'init'),"
|
||||
sql += "(23, 0, '后台任务', '', 'mdi-av-timer', 1, 40, 'init'),"
|
||||
sql += "(24, 23, '任务列表', '/cron/list', '', 2, 401, 'init');"
|
||||
sql += "(24, 23, '任务列表', '/cron/list', '', 2, 401, 'init'),"
|
||||
sql += "(25, 16, 'WebSocket', '/tool/websocket', '', 2, 707, 'init');"
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -80,7 +80,8 @@ func CreateMenuActionTableDataSql() (sql string) {
|
||||
sql += "(44, 24, 'GET', '/api/cron', 'init'),"
|
||||
sql += "(45, 24, 'GET', '/api/cron/*', 'init'),"
|
||||
sql += "(46, 24, 'PATCH', '/api/cron/used', 'init'),"
|
||||
sql += "(47, 24, 'PATCH', '/api/cron/exec/*', 'init');"
|
||||
sql += "(47, 24, 'PATCH', '/api/cron/exec/*', 'init'),"
|
||||
sql += "(48, 25, 'POST', '/api/tool/send_message', 'init');"
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package tool_handler
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
)
|
||||
|
||||
func (h *handler) WebsocketView() core.HandlerFunc {
|
||||
return func(c core.Context) {
|
||||
c.HTML("tool_websocket", nil)
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ type Handler interface {
|
||||
LogsView() core.HandlerFunc
|
||||
CacheView() core.HandlerFunc
|
||||
DataView() core.HandlerFunc
|
||||
WebsocketView() core.HandlerFunc
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package system_message
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/websocket/socket_server"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
func (h *handler) Connect() core.HandlerFunc {
|
||||
var upGrader = websocket.Upgrader{
|
||||
HandshakeTimeout: 5 * time.Second,
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
return func(ctx core.Context) {
|
||||
ws, err := upGrader.Upgrade(ctx.ResponseWriter(), ctx.Request(), nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
server, err = socket_server.New(h.logger, h.db, h.cache, ws)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
go server.OnMessage()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package system_message
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
|
||||
"github.com/xinliangnote/go-gin-api/internal/websocket/socket_server"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errors"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var _ Handler = (*handler)(nil)
|
||||
|
||||
var server socket_server.Server
|
||||
|
||||
type Handler interface {
|
||||
i()
|
||||
|
||||
// Connect 建立 Socket 连接
|
||||
Connect() core.HandlerFunc
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
logger *zap.Logger
|
||||
cache cache.Repo
|
||||
db db.Repo
|
||||
}
|
||||
|
||||
func New(logger *zap.Logger, db db.Repo, cache cache.Repo) Handler {
|
||||
return &handler{
|
||||
logger: logger,
|
||||
cache: cache,
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
func GetConn() (socket_server.Server, error) {
|
||||
if server != nil {
|
||||
return server, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("conn is nil")
|
||||
}
|
||||
|
||||
func (h *handler) i() {}
|
||||
@@ -0,0 +1,59 @@
|
||||
package socket_server
|
||||
|
||||
import (
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
|
||||
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
|
||||
"github.com/xinliangnote/go-gin-api/pkg/errors"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var _ Server = (*server)(nil)
|
||||
|
||||
type server struct {
|
||||
logger *zap.Logger
|
||||
db db.Repo
|
||||
cache cache.Repo
|
||||
socket *websocket.Conn
|
||||
}
|
||||
|
||||
type Server interface {
|
||||
i()
|
||||
|
||||
// OnMessage 接收消息
|
||||
OnMessage()
|
||||
|
||||
// OnSend 发送消息
|
||||
OnSend(message []byte) error
|
||||
|
||||
// OnClose 关闭
|
||||
OnClose()
|
||||
}
|
||||
|
||||
func New(logger *zap.Logger, db db.Repo, cache cache.Repo, conn *websocket.Conn) (Server, error) {
|
||||
if logger == nil {
|
||||
return nil, errors.New("logger required")
|
||||
}
|
||||
|
||||
if db == nil {
|
||||
return nil, errors.New("db required")
|
||||
}
|
||||
|
||||
if cache == nil {
|
||||
return nil, errors.New("cache required")
|
||||
}
|
||||
|
||||
if conn == nil {
|
||||
return nil, errors.New("conn required")
|
||||
}
|
||||
|
||||
return &server{
|
||||
logger: logger,
|
||||
db: db,
|
||||
cache: cache,
|
||||
socket: conn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *server) i() {}
|
||||
@@ -0,0 +1,10 @@
|
||||
package socket_server
|
||||
|
||||
import "go.uber.org/zap"
|
||||
|
||||
func (s *server) OnClose() {
|
||||
err := s.socket.Close()
|
||||
if err != nil {
|
||||
s.logger.Error("socket on closed error", zap.Error(err))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package socket_server
|
||||
|
||||
import "go.uber.org/zap"
|
||||
|
||||
func (s *server) OnMessage() {
|
||||
defer func() {
|
||||
s.OnClose()
|
||||
}()
|
||||
|
||||
for {
|
||||
//接收消息
|
||||
_, message, err := s.socket.ReadMessage()
|
||||
if err != nil {
|
||||
s.logger.Error("socket on message error", zap.Error(err))
|
||||
break
|
||||
}
|
||||
|
||||
// 为了便于演示,仅输出到日志文件
|
||||
s.logger.Info("receive message: " + string(message))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package socket_server
|
||||
|
||||
import (
|
||||
"github.com/gorilla/websocket"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func (s *server) OnSend(message []byte) error {
|
||||
err := s.socket.WriteMessage(websocket.TextMessage, message)
|
||||
if err != nil {
|
||||
s.OnClose()
|
||||
s.logger.Error("socket on send error", zap.Error(err))
|
||||
}
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user