add code
This commit is contained in:
commit
64e48a4094
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 kingecg
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
@ -0,0 +1,185 @@
|
||||||
|
# Gosh
|
||||||
|
|
||||||
|
Gosh 是一个轻量级的 HTTP 服务器类库,允许用户通过注册 [Anko](https://github.com/mattn/anko) 脚本和 Go 函数来快速构建 RESTful API。
|
||||||
|
|
||||||
|
## 特性
|
||||||
|
|
||||||
|
- 简单易用的 HTTP 路由注册
|
||||||
|
- 支持 GET、POST、PUT、DELETE、PATCH 等 HTTP 方法
|
||||||
|
- 通过 Anko 脚本语言实现动态 API 处理
|
||||||
|
- 内置请求和响应辅助工具
|
||||||
|
- 支持文件系统、网络、进程和 Shell 命令操作
|
||||||
|
- 跨平台支持(Linux、Windows)
|
||||||
|
|
||||||
|
## 安装
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get git.kingecg.top/kingecg/gosh
|
||||||
|
```
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
创建一个简单的 HTTP 服务器:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.kingecg.top/kingecg/gosh"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// 创建服务器实例
|
||||||
|
serverMux := gosh.NewSHMux()
|
||||||
|
|
||||||
|
// 注册 Go 函数,可在脚本中调用
|
||||||
|
serverMux.RegistFunction("hello", func(name string) string {
|
||||||
|
return fmt.Sprintf("Hello, %s", name)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 注册 GET 路由,使用 Anko 脚本处理请求
|
||||||
|
serverMux.GET("/hello", `
|
||||||
|
name = Req.GetQuery("name")
|
||||||
|
Res.WriteStr(hello(name))
|
||||||
|
`)
|
||||||
|
|
||||||
|
// 启动服务器
|
||||||
|
http.ListenAndServe(":8089", serverMux)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## API 参考
|
||||||
|
|
||||||
|
### 服务器创建
|
||||||
|
|
||||||
|
```go
|
||||||
|
// 创建新的服务器实例
|
||||||
|
serverMux := gosh.NewSHMux()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 路由注册
|
||||||
|
|
||||||
|
```go
|
||||||
|
// 注册 HTTP 方法路由
|
||||||
|
serverMux.GET(pattern, script)
|
||||||
|
serverMux.POST(pattern, script)
|
||||||
|
serverMux.PUT(pattern, script)
|
||||||
|
serverMux.DELETE(pattern, script)
|
||||||
|
serverMux.PATCH(pattern, script)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 函数注册
|
||||||
|
|
||||||
|
```go
|
||||||
|
// 注册 Go 函数,可在脚本中调用
|
||||||
|
serverMux.RegistFunction(name, function)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 请求处理
|
||||||
|
|
||||||
|
在 Anko 脚本中,可以使用以下对象和方法:
|
||||||
|
|
||||||
|
- `Req` - 请求对象
|
||||||
|
- `Req.GetQuery(key)` - 获取查询参数
|
||||||
|
- `Req.GetBodyStr()` - 获取请求体字符串
|
||||||
|
- `Req.GetHeader(key)` - 获取请求头
|
||||||
|
- `Req.GetBody()` - 获取解析后的请求体(JSON 或表单)
|
||||||
|
|
||||||
|
- `Res` - 响应对象
|
||||||
|
- `Res.SetHeader(key, value)` - 设置响应头
|
||||||
|
- `Res.WriteStr(str)` - 写入字符串响应
|
||||||
|
- `Res.WriteJSON(value)` - 写入 JSON 响应
|
||||||
|
- `Res.Status(code)` - 设置状态码
|
||||||
|
- `Res.StatusOk()` - 设置 200 状态码
|
||||||
|
- `Res.StatusBadRequest()` - 设置 400 状态码
|
||||||
|
- `Res.StatusNotFound()` - 设置 404 状态码
|
||||||
|
- `Res.StatusInternalServerError()` - 设置 500 状态码
|
||||||
|
- `Res.StatusForbidden()` - 设置 403 状态码
|
||||||
|
|
||||||
|
### 内置模块
|
||||||
|
|
||||||
|
在 Anko 脚本中,可以使用 `Require` 函数导入以下模块:
|
||||||
|
|
||||||
|
- `fs` - 文件系统模块
|
||||||
|
- 提供文件读写操作
|
||||||
|
|
||||||
|
- `net` - 网络模块
|
||||||
|
- 提供网络相关操作,如端口检测
|
||||||
|
|
||||||
|
- `process` - 进程模块
|
||||||
|
- 提供进程信息和环境变量访问
|
||||||
|
- 获取和设置环境变量
|
||||||
|
- 获取进程 ID 和主机名等信息
|
||||||
|
|
||||||
|
- `shell` - Shell 命令模块
|
||||||
|
- 提供执行 Shell 命令的功能
|
||||||
|
- 支持 Linux bash、Windows cmd 和 PowerShell
|
||||||
|
- 命令输出解析功能
|
||||||
|
|
||||||
|
## 示例
|
||||||
|
|
||||||
|
### 处理 JSON 请求
|
||||||
|
|
||||||
|
```go
|
||||||
|
serverMux.POST("/api/user", `
|
||||||
|
body = Req.GetBody()
|
||||||
|
name = body["name"]
|
||||||
|
age = body["age"]
|
||||||
|
|
||||||
|
response = {
|
||||||
|
"message": "User created",
|
||||||
|
"user": {
|
||||||
|
"name": name,
|
||||||
|
"age": age
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Res.WriteJSON(response)
|
||||||
|
`)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用文件系统模块
|
||||||
|
|
||||||
|
```go
|
||||||
|
serverMux.GET("/api/file", `
|
||||||
|
fs = Require("fs")
|
||||||
|
content = fs.ReadFile("/path/to/file.txt", "utf8")
|
||||||
|
Res.WriteStr(content)
|
||||||
|
`)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用进程模块
|
||||||
|
|
||||||
|
```go
|
||||||
|
serverMux.GET("/api/env", `
|
||||||
|
process = Require("process")
|
||||||
|
env = process.GetAllEnv()
|
||||||
|
Res.WriteJSON(env)
|
||||||
|
`)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用 Shell 模块
|
||||||
|
|
||||||
|
```go
|
||||||
|
serverMux.GET("/api/ls", `
|
||||||
|
shell = Require("shell")
|
||||||
|
result, err = shell.Exec("ls -la")
|
||||||
|
if err == nil {
|
||||||
|
Res.WriteStr(result.Stdout)
|
||||||
|
} else {
|
||||||
|
Res.StatusInternalServerError()
|
||||||
|
Res.WriteStr("Error executing command")
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 贡献
|
||||||
|
|
||||||
|
欢迎提交问题和拉取请求!
|
||||||
|
|
||||||
|
## 许可证
|
||||||
|
|
||||||
|
本项目采用 MIT 许可证 - 详见 [LICENSE](LICENSE) 文件。
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.kingecg.top/kingecg/gosh"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
serverMux := gosh.NewSHMux()
|
||||||
|
serverMux.RegistFunction("hello", func(name string) string {
|
||||||
|
return fmt.Sprintf("Hello, %s", name)
|
||||||
|
})
|
||||||
|
serverMux.GET("/hello", `
|
||||||
|
name = Req.GetQuery("name")
|
||||||
|
Res.WriteStr(hello(name))
|
||||||
|
`)
|
||||||
|
http.ListenAndServe(":8089", serverMux)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
module git.kingecg.top/kingecg/gosh
|
||||||
|
|
||||||
|
go 1.23.1
|
||||||
|
|
||||||
|
require github.com/mattn/anko v0.1.10 // indirect
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
github.com/mattn/anko v0.1.10 h1:3QcIxCLirIxOZhIVtvo9eWz8tym/iZ9Nb29VCnzaMvc=
|
||||||
|
github.com/mattn/anko v0.1.10/go.mod h1:gjrudvzf1t7FWTZo1Nbywnr75g3uDnGjXdp2nkguBjQ=
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
package gosh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/mattn/anko/env"
|
||||||
|
"github.com/mattn/anko/vm"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
GET = "GET"
|
||||||
|
POST = "POST"
|
||||||
|
PUT = "PUT"
|
||||||
|
DEL = "DELETE"
|
||||||
|
PATCH = "PATCH"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SHMux struct {
|
||||||
|
*http.ServeMux
|
||||||
|
ankoEnv *env.Env
|
||||||
|
getScripts map[string]string
|
||||||
|
postScripts map[string]string
|
||||||
|
putScripts map[string]string
|
||||||
|
delScripts map[string]string
|
||||||
|
patchScripts map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSHMux() *SHMux {
|
||||||
|
return &SHMux{
|
||||||
|
ServeMux: http.NewServeMux(),
|
||||||
|
ankoEnv: env.NewEnv(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SHMux) RegistFunction(name string, function interface{}) {
|
||||||
|
if s.ankoEnv != nil {
|
||||||
|
s.ankoEnv.Define(name, function)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SHMux) register(method, pattern, script string) {
|
||||||
|
switch method {
|
||||||
|
case GET:
|
||||||
|
if s.getScripts == nil {
|
||||||
|
s.getScripts = make(map[string]string)
|
||||||
|
}
|
||||||
|
s.getScripts[pattern] = script
|
||||||
|
case POST:
|
||||||
|
if s.postScripts == nil {
|
||||||
|
s.postScripts = make(map[string]string)
|
||||||
|
}
|
||||||
|
s.postScripts[pattern] = script
|
||||||
|
case PUT:
|
||||||
|
if s.putScripts == nil {
|
||||||
|
s.putScripts = make(map[string]string)
|
||||||
|
}
|
||||||
|
s.putScripts[pattern] = script
|
||||||
|
case DEL:
|
||||||
|
if s.delScripts == nil {
|
||||||
|
s.delScripts = make(map[string]string)
|
||||||
|
}
|
||||||
|
s.delScripts[pattern] = script
|
||||||
|
case PATCH:
|
||||||
|
if s.patchScripts == nil {
|
||||||
|
s.patchScripts = make(map[string]string)
|
||||||
|
}
|
||||||
|
s.patchScripts[pattern] = script
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (s *SHMux) GET(pattern, script string) {
|
||||||
|
s.register(GET, pattern, script)
|
||||||
|
}
|
||||||
|
func (s *SHMux) POST(pattern, script string) {
|
||||||
|
s.register(POST, pattern, script)
|
||||||
|
}
|
||||||
|
func (s *SHMux) PUT(pattern, script string) {
|
||||||
|
s.register(PUT, pattern, script)
|
||||||
|
}
|
||||||
|
func (s *SHMux) DELETE(pattern, script string) {
|
||||||
|
s.register(DEL, pattern, script)
|
||||||
|
}
|
||||||
|
func (s *SHMux) PATCH(pattern, script string) {
|
||||||
|
s.register(PATCH, pattern, script)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SHMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var script string
|
||||||
|
switch r.Method {
|
||||||
|
case GET:
|
||||||
|
script = s.getScripts[r.URL.Path]
|
||||||
|
case POST:
|
||||||
|
script = s.postScripts[r.URL.Path]
|
||||||
|
case PUT:
|
||||||
|
script = s.putScripts[r.URL.Path]
|
||||||
|
case DEL:
|
||||||
|
script = s.delScripts[r.URL.Path]
|
||||||
|
case PATCH:
|
||||||
|
script = s.patchScripts[r.URL.Path]
|
||||||
|
}
|
||||||
|
s.handle(w, r, script)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SHMux) handle(w http.ResponseWriter, r *http.Request, script string) {
|
||||||
|
if script == "" {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
reqHelper := &RequestHelper{
|
||||||
|
r: r,
|
||||||
|
}
|
||||||
|
resHelper := &ResponseHelper{
|
||||||
|
w: w,
|
||||||
|
}
|
||||||
|
env := s.ankoEnv.Copy()
|
||||||
|
env.Define("Req", reqHelper)
|
||||||
|
env.Define("Res", resHelper)
|
||||||
|
_, err := vm.Execute(env, nil, script)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// w.Write([]byte(script))
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
package gosh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RequestHelper struct {
|
||||||
|
r *http.Request
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RequestHelper) GetQuery(key string) string {
|
||||||
|
return r.r.URL.Query().Get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RequestHelper) GetBodyStr() string {
|
||||||
|
body, err := ioutil.ReadAll(r.r.Body)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RequestHelper) GetHeader(key string) string {
|
||||||
|
return r.r.Header.Get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RequestHelper) getJSONBody() (map[string]interface{}, error) {
|
||||||
|
body, err := ioutil.ReadAll(r.r.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m := make(map[string]interface{})
|
||||||
|
err = json.Unmarshal(body, &m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RequestHelper) getFormBody() (map[string]interface{}, error) {
|
||||||
|
err := r.r.ParseForm()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m := make(map[string]interface{})
|
||||||
|
for key, values := range r.r.Form {
|
||||||
|
if len(values) == 1 {
|
||||||
|
m[key] = values[0]
|
||||||
|
} else {
|
||||||
|
m[key] = values
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RequestHelper) GetBody() map[string]interface{} {
|
||||||
|
contentType := r.r.Header.Get("Content-Type")
|
||||||
|
switch contentType {
|
||||||
|
case "application/json":
|
||||||
|
m, err := r.getJSONBody()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
case "application/x-www-form-urlencoded":
|
||||||
|
m, err := r.getFormBody()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResponseHelper struct {
|
||||||
|
w http.ResponseWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ResponseHelper) SetHeader(key, value string) {
|
||||||
|
r.w.Header().Set(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ResponseHelper) WriteStr(s string) {
|
||||||
|
r.w.Write([]byte(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ResponseHelper) WriteJSON(v interface{}) error {
|
||||||
|
r.w.Header().Set("Content-Type", "application/json")
|
||||||
|
data, err := json.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = r.w.Write(data)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ResponseHelper) Status(code int) {
|
||||||
|
r.w.WriteHeader(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ResponseHelper) StatusOk() {
|
||||||
|
r.w.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ResponseHelper) StatusBadRequest() {
|
||||||
|
r.w.WriteHeader(http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ResponseHelper) StatusNotFound() {
|
||||||
|
r.w.WriteHeader(http.StatusNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ResponseHelper) StatusInternalServerError() {
|
||||||
|
r.w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ResponseHelper) StatusForbidden() {
|
||||||
|
r.w.WriteHeader(http.StatusForbidden)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
package interceptor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FileModule struct{}
|
||||||
|
|
||||||
|
func (f *FileModule) ReadFile(path string, encode string) interface{} {
|
||||||
|
fileContent, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if encode == "binary" {
|
||||||
|
return fileContent
|
||||||
|
}
|
||||||
|
return string(fileContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileModule) WriteFile(path string, content interface{}, encode string) {
|
||||||
|
fileContent, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if encode == "binary" {
|
||||||
|
os.WriteFile(path, fileContent, 0644)
|
||||||
|
}
|
||||||
|
os.WriteFile(path, []byte(content.(string)), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileModule) RemoveFile(path string) {
|
||||||
|
os.Remove(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileModule) Exists(path string) bool {
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return os.IsExist(err)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileModule) IsDir(path string) bool {
|
||||||
|
fileInfo, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return fileInfo.IsDir()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileModule) MakeDir(path string, rescursive bool) {
|
||||||
|
os.MkdirAll(path, 0755)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileModule) RemoveDir(path string) {
|
||||||
|
os.RemoveAll(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileModule) ListDir(path string) []string {
|
||||||
|
files, err := os.ReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
fileNames := make([]string, 0, len(files))
|
||||||
|
for _, file := range files {
|
||||||
|
fileNames = append(fileNames, file.Name())
|
||||||
|
}
|
||||||
|
return fileNames
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileModule) Copy(src, dst string) {
|
||||||
|
if !f.IsDir(dst) {
|
||||||
|
if f.IsDir(filepath.Dir(dst)) {
|
||||||
|
bcontent := f.ReadFile(src, "binary")
|
||||||
|
f.WriteFile(dst, bcontent, "binary")
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
panic(fmt.Errorf("destination directory not exists"))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if f.IsDir(src) {
|
||||||
|
if strings.HasSuffix(dst, "/") {
|
||||||
|
dst = dst + filepath.Base(src)
|
||||||
|
f.MakeDir(dst, true)
|
||||||
|
}
|
||||||
|
os.CopyFS(dst, os.DirFS(src))
|
||||||
|
}
|
||||||
|
// 文件拷贝, 需要先读取文件内容, 然后写入到目标文件中
|
||||||
|
bcontent := f.ReadFile(src, "binary")
|
||||||
|
f.WriteFile(dst, bcontent, "binary")
|
||||||
|
// 拷贝文件属性
|
||||||
|
fileInfo, err := os.Stat(src)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
os.Chmod(dst, fileInfo.Mode())
|
||||||
|
os.Chtimes(dst, fileInfo.ModTime(), fileInfo.ModTime())
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
package interceptor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/mattn/anko/env"
|
||||||
|
"github.com/mattn/anko/vm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Ankointerceptor struct {
|
||||||
|
importMap map[string]interface{}
|
||||||
|
ankoEnv *env.Env
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAnkointerceptor(ankoEnv *env.Env) *Ankointerceptor {
|
||||||
|
|
||||||
|
a := &Ankointerceptor{
|
||||||
|
importMap: make(map[string]interface{}),
|
||||||
|
ankoEnv: ankoEnv,
|
||||||
|
}
|
||||||
|
a.ankoEnv.Define("println", fmt.Println)
|
||||||
|
a.importMap["fs"] = &FileModule{}
|
||||||
|
a.importMap["net"] = &NetModule{}
|
||||||
|
a.importMap["process"] = &ProcessModule{}
|
||||||
|
a.importMap["shell"] = &ShellModule{}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Ankointerceptor) Exec(script string) (interface{}, error) {
|
||||||
|
e := a.ankoEnv.Copy()
|
||||||
|
e.Define("Require", a.genRequireMethod(script))
|
||||||
|
e.Define("__filename", script)
|
||||||
|
e.Define("__dirname", filepath.Dir(script))
|
||||||
|
scriptbytes, frr := os.ReadFile(script)
|
||||||
|
if frr != nil {
|
||||||
|
return nil, frr
|
||||||
|
}
|
||||||
|
scriptcode := string(scriptbytes)
|
||||||
|
result, err := vm.Execute(e, nil, scriptcode)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Ankointerceptor) genRequireMethod(basePath string) interface{} {
|
||||||
|
return func(s string) interface{} {
|
||||||
|
if r, ok := a.importMap[s]; ok {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
if !filepath.IsAbs(s) {
|
||||||
|
s = filepath.Join(filepath.Dir(basePath), s)
|
||||||
|
}
|
||||||
|
if _, ok := a.importMap[s]; ok {
|
||||||
|
return a.importMap[s]
|
||||||
|
}
|
||||||
|
result, err := a.Exec(s)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
a.importMap[s] = result
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package interceptor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mattn/anko/env"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAnkointerceptor_ExecRequire(t *testing.T) {
|
||||||
|
// 创建测试环境
|
||||||
|
e := env.NewEnv()
|
||||||
|
|
||||||
|
// 创建拦截器实例
|
||||||
|
interceptor := NewAnkointerceptor(e)
|
||||||
|
_, file, _, _ := runtime.Caller(0)
|
||||||
|
scriptPath := filepath.Join(filepath.Dir(file), "testdata/test_caller.ank")
|
||||||
|
v, _ := interceptor.Exec(scriptPath)
|
||||||
|
if v != "Hello, tom from test script" {
|
||||||
|
t.Errorf("Not equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package interceptor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NetModule struct{}
|
||||||
|
|
||||||
|
func (n *NetModule) IsPortOpen(host string, port int) bool {
|
||||||
|
conn, err := net.Dial("tcp", net.JoinHostPort(host, fmt.Sprintf("%d", port)))
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
conn.Close()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
package interceptor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessModule 提供对进程信息和环境变量的访问
|
||||||
|
type ProcessModule struct{}
|
||||||
|
|
||||||
|
// GetEnv 获取环境变量
|
||||||
|
func (p *ProcessModule) GetEnv(key string) string {
|
||||||
|
return os.Getenv(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEnv 设置环境变量
|
||||||
|
func (p *ProcessModule) SetEnv(key, value string) error {
|
||||||
|
return os.Setenv(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllEnv 获取所有环境变量
|
||||||
|
func (p *ProcessModule) GetAllEnv() map[string]string {
|
||||||
|
envMap := make(map[string]string)
|
||||||
|
for _, env := range os.Environ() {
|
||||||
|
pair := strings.SplitN(env, "=", 2)
|
||||||
|
if len(pair) == 2 {
|
||||||
|
envMap[pair[0]] = pair[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return envMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPid 获取当前进程ID
|
||||||
|
func (p *ProcessModule) GetPid() int {
|
||||||
|
return os.Getpid()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPpid 获取父进程ID
|
||||||
|
func (p *ProcessModule) GetPpid() int {
|
||||||
|
return os.Getppid()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHostname 获取主机名
|
||||||
|
func (p *ProcessModule) GetHostname() (string, error) {
|
||||||
|
return os.Hostname()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOS 获取操作系统类型
|
||||||
|
func (p *ProcessModule) GetOS() string {
|
||||||
|
return runtime.GOOS
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetArch 获取系统架构
|
||||||
|
func (p *ProcessModule) GetArch() string {
|
||||||
|
return runtime.GOARCH
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCwd 获取当前工作目录
|
||||||
|
func (p *ProcessModule) GetCwd() (string, error) {
|
||||||
|
return os.Getwd()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chdir 改变当前工作目录
|
||||||
|
func (p *ProcessModule) Chdir(dir string) error {
|
||||||
|
return os.Chdir(dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetArgs 获取命令行参数
|
||||||
|
func (p *ProcessModule) GetArgs() []string {
|
||||||
|
return os.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit 退出程序
|
||||||
|
func (p *ProcessModule) Exit(code int) {
|
||||||
|
os.Exit(code)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,171 @@
|
||||||
|
package interceptor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ShellModule 提供执行shell命令的功能
|
||||||
|
type ShellModule struct{}
|
||||||
|
|
||||||
|
// CommandResult 表示命令执行结果
|
||||||
|
type CommandResult struct {
|
||||||
|
Stdout string
|
||||||
|
Stderr string
|
||||||
|
ExitCode int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec 执行shell命令并返回结果
|
||||||
|
func (s *ShellModule) Exec(command string) (*CommandResult, error) {
|
||||||
|
var cmd *exec.Cmd
|
||||||
|
var shell, flag string
|
||||||
|
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "windows":
|
||||||
|
shell = "cmd"
|
||||||
|
flag = "/c"
|
||||||
|
default: // linux, darwin, etc.
|
||||||
|
shell = "bash"
|
||||||
|
flag = "-c"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = exec.Command(shell, flag, command)
|
||||||
|
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
|
||||||
|
err := cmd.Run()
|
||||||
|
|
||||||
|
result := &CommandResult{
|
||||||
|
Stdout: stdout.String(),
|
||||||
|
Stderr: stderr.String(),
|
||||||
|
ExitCode: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if exitError, ok := err.(*exec.ExitError); ok {
|
||||||
|
result.ExitCode = exitError.ExitCode()
|
||||||
|
} else {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecPowerShell 在Windows上执行PowerShell命令
|
||||||
|
func (s *ShellModule) ExecPowerShell(command string) (*CommandResult, error) {
|
||||||
|
if runtime.GOOS != "windows" {
|
||||||
|
return nil, errors.New("PowerShell is only available on Windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("powershell", "-Command", command)
|
||||||
|
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
|
||||||
|
err := cmd.Run()
|
||||||
|
|
||||||
|
result := &CommandResult{
|
||||||
|
Stdout: stdout.String(),
|
||||||
|
Stderr: stderr.String(),
|
||||||
|
ExitCode: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if exitError, ok := err.(*exec.ExitError); ok {
|
||||||
|
result.ExitCode = exitError.ExitCode()
|
||||||
|
} else {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecBash 专门执行Bash命令(Linux/macOS)
|
||||||
|
func (s *ShellModule) ExecBash(command string) (*CommandResult, error) {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
return nil, errors.New("Bash is not available on Windows by default")
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("bash", "-c", command)
|
||||||
|
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
|
||||||
|
err := cmd.Run()
|
||||||
|
|
||||||
|
result := &CommandResult{
|
||||||
|
Stdout: stdout.String(),
|
||||||
|
Stderr: stderr.String(),
|
||||||
|
ExitCode: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if exitError, ok := err.(*exec.ExitError); ok {
|
||||||
|
result.ExitCode = exitError.ExitCode()
|
||||||
|
} else {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecCmd 专门执行Windows CMD命令
|
||||||
|
func (s *ShellModule) ExecCmd(command string) (*CommandResult, error) {
|
||||||
|
if runtime.GOOS != "windows" {
|
||||||
|
return nil, errors.New("CMD is only available on Windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("cmd", "/c", command)
|
||||||
|
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
|
||||||
|
err := cmd.Run()
|
||||||
|
|
||||||
|
result := &CommandResult{
|
||||||
|
Stdout: stdout.String(),
|
||||||
|
Stderr: stderr.String(),
|
||||||
|
ExitCode: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if exitError, ok := err.(*exec.ExitError); ok {
|
||||||
|
result.ExitCode = exitError.ExitCode()
|
||||||
|
} else {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseOutput 解析命令输出为字符串数组(按行分割)
|
||||||
|
func (s *ShellModule) ParseOutput(output string) []string {
|
||||||
|
return strings.Split(strings.TrimSpace(output), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseKeyValue 解析键值对格式的输出(如 KEY=VALUE)
|
||||||
|
func (s *ShellModule) ParseKeyValue(output string) map[string]string {
|
||||||
|
result := make(map[string]string)
|
||||||
|
lines := s.ParseOutput(output)
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
parts := strings.SplitN(line, "=", 2)
|
||||||
|
if len(parts) == 2 {
|
||||||
|
result[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
sp=Require("./test_script.ank")
|
||||||
|
fs=Require("fs")
|
||||||
|
if fs.Exists(__filename) {
|
||||||
|
println("fs module is ok")
|
||||||
|
}
|
||||||
|
ret=sp("tom")
|
||||||
|
ret
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
// 这是一个测试脚本文件,用于测试 Require 功能
|
||||||
|
message = "Hello from test script"
|
||||||
|
c = func(name) {
|
||||||
|
return "Hello, " + name + " from test script"
|
||||||
|
}
|
||||||
|
println(__filename)
|
||||||
|
c
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# gosh
|
||||||
|
|
||||||
|
创建一个http服务器类库。让用户可以通过注册anko脚本和函数来快速构建RESTful API。
|
||||||
Loading…
Reference in New Issue