diff --git a/api/Init.go b/api/Init.go index 17fcf98..cc906f9 100644 --- a/api/Init.go +++ b/api/Init.go @@ -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) } diff --git a/api/Mindustry.go b/api/Mindustry.go index 69e706b..d849490 100644 --- a/api/Mindustry.go +++ b/api/Mindustry.go @@ -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) diff --git a/api/info.go b/api/info.go new file mode 100644 index 0000000..8b1ed65 --- /dev/null +++ b/api/info.go @@ -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)) +} diff --git a/api/memos.go b/api/memos.go index ca46348..c308542 100644 --- a/api/memos.go +++ b/api/memos.go @@ -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 } diff --git a/models/common.go b/models/common.go index b8b8129..d72e2c3 100644 --- a/models/common.go +++ b/models/common.go @@ -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() { diff --git a/models/html_template.go b/models/html_template.go index 7a067a6..21454ce 100644 --- a/models/html_template.go +++ b/models/html_template.go @@ -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 +} diff --git a/themes/blog/api/footer.gohtml b/themes/blog/api/footer.gohtml new file mode 100644 index 0000000..7be43a5 --- /dev/null +++ b/themes/blog/api/footer.gohtml @@ -0,0 +1,5 @@ +{{define "footer"}} + + + +{{ end }} \ No newline at end of file diff --git a/themes/blog/api/info.gohtml b/themes/blog/api/info.gohtml new file mode 100644 index 0000000..f23c49d --- /dev/null +++ b/themes/blog/api/info.gohtml @@ -0,0 +1,67 @@ +{{template "header" .}} +
+ +
+ {{if ne .Err ""}} +
+ ERR + {{ .Err }} +
+
+ + {{ end }} +
+ {{ .Api.Mode }} + {{ .Api.Url }} +
+
+
+
请求参数
+
+ + + + + + + + + + {{ range .Api.Args }} + + + + + + {{ end }} + +
参数名必填介绍
{{ .Name }}{{ if .Required }}√ {{ else }}× {{ end }}{{ .Description }}
+
+
+
+
+
+ 响应示例 +
+
+
{{ .Api.SampleResponse }}
+
+
+
+
+
+ +{{template "footer" .}} \ No newline at end of file diff --git a/utils/MdtInfo.go b/utils/MdtInfo.go index 8b1f978..58f76b7 100644 --- a/utils/MdtInfo.go +++ b/utils/MdtInfo.go @@ -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()