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