UP MDTApi

main
JiXieShi 2024-06-03 16:15:23 +08:00
parent 4b29709791
commit 352666f059
9 changed files with 214 additions and 70 deletions

View File

@ -1,11 +1,9 @@
package api
import (
"net/http"
)
import "net/http"
func InitApi() {
BilibiliInit()
http.HandleFunc("/api/mdt", GetInfo)
http.HandleFunc("/api/memos", GetMemosJson)
http.HandleFunc(Mindustry.Url, GetMindustryInfo)
http.HandleFunc(Memos.Url, GetMemosJson)
}

View File

@ -6,12 +6,18 @@ import (
"net/http"
)
func GetInfo(w http.ResponseWriter, r *http.Request) {
var Mindustry = Api{
Mode: "GET",
Url: "/api/mdt",
Args: []Args{{Name: "host", Required: true, Description: "服务器地址"}},
SampleResponse: "{\n \"host\": \"p4.simpfun.cn\",\n \"port\": 8952,\n \"status\": \"Online\",\n \"name\": \"[#00ff00]镜影若滴の低配备用服\",\n \"maps\": \"未知\",\n \"players\": 0,\n \"version\": 146,\n \"wave\": 1,\n \"vertype\": \"official\",\n \"gamemode\": {\n \"name\": \"生存\",\n \"id\": 0\n },\n \"description\": \"[#00ff00]低配但稳定的备用服,[#ffaaff]欢迎加入QQ群726525226\",\n \"modename\": \"\",\n \"limit\": 0,\n \"ping\": 40\n}",
}
func GetMindustryInfo(w http.ResponseWriter, r *http.Request) {
var (
err error
info utils.ServerInfo
)
if err = r.ParseForm(); err != nil {
http.Error(w, "参数解析错误", http.StatusInternalServerError)
}
@ -25,7 +31,8 @@ func GetInfo(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json;charset=utf-8")
err = json.NewEncoder(w).Encode(info)
} else {
http.Error(w, "参数为空", http.StatusInternalServerError)
Mindustry.ErrorInfoView(w, "host为空")
return
}
if err != nil {
http.Error(w, "无法解析服务器数据", http.StatusInternalServerError)

31
api/info.go Normal file
View File

@ -0,0 +1,31 @@
package api
import (
"blog/config"
"blog/models"
"io"
)
type Api struct {
Mode string
Url string
Args []Args
SampleResponse string
}
type Args struct {
Name string
Required bool
Description string
}
func ApiViewBuild(api Api, err interface{}) map[string]interface{} {
return map[string]interface{}{
"Api": api,
"Err": err,
"Config": config.Cfg,
}
}
func (a Api) ErrorInfoView(w io.Writer, err interface{}) {
models.Api.Info.WriteData(w, ApiViewBuild(a, err))
}

View File

@ -25,37 +25,52 @@ type MemoInfo struct {
RelationList []interface{} `json:"relationList"`
}
var Memos = Api{
Url: "/api/memos",
Mode: "GET",
SampleResponse: "[{\n \"bookSourceName\": \"69书吧自制\",\n \"bookSourceType\": 0,\n \"bookSourceUrl\": \"https://www.69shu.cc\",\n \"customOrder\": 45,\n \"enabled\": true,\n \"enabledCookieJar\": true,\n \"enabledExplore\": true,\n \"lastUpdateTime\": 1713454345818,\n \"respondTime\": 6224,\n \"ruleBookInfo\": {\n \"author\": \"h1@text##(.*) / (.*)##$2\",\n \"coverUrl\": \"img@src\",\n \"intro\": \".bookinfo_intro@text\",\n \"kind\": \".nav-mbx > a:nth-child(3)@text\",\n \"lastChapter\": \".update > a@text\",\n \"name\": \"h1@text##(.*) / (.*)##$1\"\n },\n \"ruleContent\": {\n \"content\": \"id.htmlContent@html##.*最快更新.*\"\n },\n \"ruleExplore\": {},\n \"ruleReview\": {},\n \"ruleSearch\": {\n \"author\": \"td.2@text\",\n \"bookList\": \".grid@tbody@tr!0\",\n \"bookUrl\": \"td.0@a@href\",\n \"checkKeyWord\": \"我的\",\n \"lastChapter\": \"td.1@text\",\n \"name\": \"td.0@text\",\n \"wordCount\": \"td.3@text\"\n },\n \"ruleToc\": {\n \"chapterList\": \"class.chapterlist.1@tag.li!0\",\n \"chapterName\": \"a.0@text\",\n \"chapterUrl\": \"a.0@href\"\n },\n \"searchUrl\": \"/modules/article/search.php?searchkey={{key}},{\\n\\\"charset\\\": \\\"gbk\\\"}\",\n \"weight\": 0\n}\n]",
Args: []Args{{
Name: "id",
Required: true,
Description: "有json代码块的文章id",
}},
}
func GetMemosJson(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
http.Error(w, "参数解析错误", http.StatusInternalServerError)
}
id := r.Form.Get("id")
resp, err := http.Get(config.Cfg.MemosURL + "/api/v1/memo/" + id)
if err != nil {
http.Error(w, "无法获取文章数据", http.StatusInternalServerError)
return
}
defer resp.Body.Close()
var info MemoInfo
err = json.NewDecoder(resp.Body).Decode(&info) // 使用json.NewDecoder解码JSON数据
if err != nil {
http.Error(w, "无法解析文章数据", http.StatusInternalServerError)
return
}
compileRegex := regexp.MustCompile("(?s)```json\n(.*?)```")
matchArr := compileRegex.FindStringSubmatch(info.Content)
if len(matchArr) > 0 {
source := make([]byte, 4096)
source = []byte(fmt.Sprintf("[%v]", matchArr[len(matchArr)-1]))
w.Header().Add("Content-Type", "application/octet-stream;charset=utf-8")
w.Header().Add("Content-Disposition", "attachment;filename="+info.Name+".json")
_, err = w.Write(source)
if id != "" {
resp, err := http.Get(config.Cfg.MemosURL + "/api/v1/memo/" + id)
if err != nil {
http.Error(w, "无法解析源数据", http.StatusInternalServerError)
http.Error(w, "无法获取文章数据", http.StatusInternalServerError)
return
}
return
defer resp.Body.Close()
var info MemoInfo
err = json.NewDecoder(resp.Body).Decode(&info) // 使用json.NewDecoder解码JSON数据
if err != nil {
http.Error(w, "无法解析文章数据", http.StatusInternalServerError)
return
}
compileRegex := regexp.MustCompile("(?s)```json\n(.*?)```")
matchArr := compileRegex.FindStringSubmatch(info.Content)
if len(matchArr) > 0 {
source := make([]byte, 4096)
source = []byte(fmt.Sprintf("[%v]", matchArr[len(matchArr)-1]))
w.Header().Add("Content-Type", "application/octet-stream;charset=utf-8")
w.Header().Add("Content-Disposition", "attachment;filename="+info.Name+".json")
_, err = w.Write(source)
if err != nil {
http.Error(w, "无法解析源数据", http.StatusInternalServerError)
return
}
return
}
Memos.ErrorInfoView(w, "无法解析页面请求"+id)
} else {
Memos.ErrorInfoView(w, "id为空")
}
http.Error(w, "无法解析页面请求"+id, http.StatusInternalServerError)
return
}

View File

@ -9,6 +9,7 @@ var Navigation Navs
var ArticleList Articles
var ArticleShortUrlMap map[string]string //用来保证文章 shortUrl 唯一和快速定位文章
var Template HtmlTemplate
var Api ApiTemplate
func CompiledContent() {
config.Initial() //克隆或者更新文档库
@ -35,6 +36,16 @@ func CompiledContent() {
wg.Done()
}()
//加载Api信息模板
wg.Add(1)
go func() {
Api, err = initApiTemplate(config.Cfg.ThemesDir)
if err != nil {
panic(err)
}
wg.Done()
}()
//文章
wg.Add(1)
go func() {

View File

@ -22,6 +22,11 @@ type HtmlTemplate struct {
Index TemplatePointer
}
type ApiTemplate struct {
Info TemplatePointer
Mindustry TemplatePointer
}
func (t TemplatePointer) WriteData(w io.Writer, data interface{}) {
err := t.Execute(w, data)
@ -125,3 +130,32 @@ func readHtmlTemplate(htmlFileName []string, viewDir string) ([]TemplatePointer,
return htmlTemplate, nil
}
func initApiTemplate(viewDir string) (ApiTemplate, error) {
tp, err := readApiTemplate(
[]string{"info", "mdt"},
viewDir+"/api")
if err != nil {
return ApiTemplate{}, err
}
return ApiTemplate{
Info: tp[0],
Mindustry: tp[1],
}, nil
}
func readApiTemplate(htmlFileName []string, viewDir string) ([]TemplatePointer, error) {
var apiTemplate []TemplatePointer
head := viewDir + "/head.gohtml"
footer := viewDir + "/footer.gohtml"
for _, name := range htmlFileName {
tp, err := template.New(name+".gohtml").
Funcs(template.FuncMap{"SpreadDigit": SpreadDigit}).
ParseFiles(viewDir+"/"+name+".gohtml", head, footer)
if err != nil {
return nil, err
}
apiTemplate = append(apiTemplate, TemplatePointer{tp})
}
return apiTemplate, nil
}

View File

@ -0,0 +1,5 @@
{{define "footer"}}
</div>
</body>
</html>
{{ end }}

View File

@ -0,0 +1,67 @@
{{template "header" .}}
<div class="layui-body">
<!-- 内容主体区域 -->
<div style="padding: 15px;">
{{if ne .Err ""}}
<blockquote class="layui-elem-quote layui-text" style="border-left-color:#ff5722">
<span class="layui-badge layui-bg-red">ERR</span>
{{ .Err }}
</blockquote>
<hr class="layui-border-blue">
{{ end }}
<blockquote class="layui-elem-quote layui-text">
<span class="layui-badge layui-bg-green">{{ .Api.Mode }}</span>
{{ .Api.Url }}
</blockquote>
<div class="layui-collapse" lay-filter="filter-collapse" lay-accordion>
<div class="layui-colla-item">
<div class="layui-colla-title">请求参数</div>
<div class="layui-colla-content">
<table lay-filter="parse-table">
<thead>
<tr>
<th lay-data="{field:'name', width:150}">参数名</th>
<th lay-data="{field:'required', width:50}">必填</th>
<th lay-data="{field:'description', minWidth: 180}">介绍</th>
</tr>
</thead>
<tbody>
{{ range .Api.Args }}
<tr>
<td>{{ .Name }}</td>
<td>{{ if .Required }}√ {{ else }}× {{ end }}</td>
<td>{{ .Description }}</td>
</tr>
{{ end }}
</tbody>
</table>
</div>
</div>
<hr class="layui-border-orange">
<div class="layui-card layui-panel">
<div class="layui-card-header">
响应示例
</div>
<div class="layui-card-body">
<pre class="layui-code code-resp" lay-options="{lang:'json'}">{{ .Api.SampleResponse }}</pre>
</div>
</div>
</div>
</div>
</div>
<script>
layui.use(function () {
var table = layui.table;
var element = layui.element;
element.on('collapse(filter-collapse)', function (data) {
if (data.show) {
table.init('parse-table', {});
}
});
layui.code({
elem: '.code-resp'
});
});
</script>
{{template "footer" .}}

View File

@ -2,9 +2,7 @@ package utils
import (
"bytes"
"context"
"encoding/binary"
"encoding/hex"
"fmt"
"net"
"strconv"
@ -87,43 +85,26 @@ func (r *InfoBuffer) get() byte {
return b
}
func connectServer(host string) (buf InfoBuffer, err error) {
func connectServer(host string) (buf InfoBuffer, ping int64, err error) {
socket, err := net.Dial("udp", host)
if err != nil {
return InfoBuffer{}, err
return InfoBuffer{}, -1, err
}
defer socket.Close()
sendData, _ := hex.DecodeString("FE01")
_, err = socket.Write(sendData)
if err != nil {
return InfoBuffer{}, err
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
defer cancel()
workDone := make(chan struct{}, 1)
data := make([]byte, 2048)
go func() {
_, err = socket.Read(data)
workDone <- struct{}{}
}()
select {
case <-workDone:
buf.New(data)
case <-ctx.Done():
return InfoBuffer{}, fmt.Errorf("超时:%v", err)
}
return buf, nil
}
func ping(host string) (int64, error) {
startTime := time.Now()
conn, err := net.DialTimeout("tcp", host, time.Second*2)
_, err = socket.Write([]byte{0xFE, 0x01})
if err != nil {
return 0, err
return InfoBuffer{}, -1, err
}
conn.Close()
conn.RemoteAddr()
elapsedTime := time.Since(startTime).Milliseconds()
return elapsedTime, nil
data := make([]byte, 500)
socket.SetReadDeadline(time.Now().Add(2 * time.Second))
_, err = socket.Read(data)
if err != nil {
return InfoBuffer{}, -1, err
}
ping = time.Since(startTime).Milliseconds()
buf.New(data)
return buf, ping, nil
}
func GetServerInfo(host string) (ServerInfo, error) {
var info ServerInfo
@ -140,17 +121,12 @@ func GetServerInfo(host string) (ServerInfo, error) {
info.Port = port
}
add := fmt.Sprintf("%s:%d", info.Host, info.Port)
d, err := ping(add)
if err != nil {
info.Status = "Offline"
return info, err
}
info.Ping = int(d)
buf, err := connectServer(add)
buf, ping, err := connectServer(add)
if err != nil {
info.Status = "Offline"
return info, err
}
info.Ping = int(ping)
info.Status = "Online"
info.Name = buf.readString()
info.Maps = buf.readString()