From af429a393a487ba1377d9ea6d32801ffc783b763 Mon Sep 17 00:00:00 2001 From: Zheng Kai Date: Thu, 30 Mar 2023 16:41:44 +0800 Subject: [PATCH] up --- proto/rsp.proto | 16 ++++++++++++++ server/build/.gitignore | 1 + server/build/start-server.sh | 3 +++ server/src/config/config.go | 4 ++++ server/src/config/init.go | 5 +++-- server/src/core/pool.go | 7 +++++- server/src/core/req.go | 21 ++++++------------ server/src/core/row.go | 19 +++++++++++++++- server/src/core/rsp.go | 42 ++++++++++++++++++++++++++++++++++++ server/src/core/web.go | 12 +++++++++-- server/src/pb/req.go | 22 +++++++++++++++++++ server/src/util/file.go | 15 +++++++++++++ server/src/util/ip.go | 17 +++++++-------- 13 files changed, 155 insertions(+), 29 deletions(-) create mode 100644 proto/rsp.proto create mode 100644 server/src/core/rsp.go create mode 100644 server/src/pb/req.go diff --git a/proto/rsp.proto b/proto/rsp.proto new file mode 100644 index 0000000..5793b89 --- /dev/null +++ b/proto/rsp.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; +option go_package = "/pb"; +package pb; + +message Rsp { + uint32 id = 1; + string object = 2; + fixed32 created = 3; + string model = 4; + RspUsage usage = 5; +} + +message RspUsage { + uint32 prompt_tokens = 1; + uint32 total_tokens = 2; +} diff --git a/server/build/.gitignore b/server/build/.gitignore index 6f6dd46..90fd23e 100644 --- a/server/build/.gitignore +++ b/server/build/.gitignore @@ -1 +1,2 @@ /.git-hash +/env.sh diff --git a/server/build/start-server.sh b/server/build/start-server.sh index b98cd4a..2305667 100755 --- a/server/build/start-server.sh +++ b/server/build/start-server.sh @@ -2,6 +2,9 @@ DIR=$(readlink -f "$0") && DIR=$(dirname "$DIR") && cd "$DIR" || exit 1 +if [ -e ./env.sh ]; then + . ./env.sh +fi . ./common.sh LOG="log file: $LOG_FILE" diff --git a/server/src/config/config.go b/server/src/config/config.go index 9ccc8ee..a86d48e 100644 --- a/server/src/config/config.go +++ b/server/src/config/config.go @@ -8,4 +8,8 @@ var ( StaticDir = `/www/orca/static` WebAddr = `localhost:22035` + + RemoteAPI = `https://api.openai.com` + + OpenAIKey = `` ) diff --git a/server/src/config/init.go b/server/src/config/init.go index 2c28d32..031ea24 100644 --- a/server/src/config/init.go +++ b/server/src/config/init.go @@ -10,8 +10,9 @@ func init() { Dir, _ = filepath.Abs(filepath.Dir(os.Args[0])) list := map[string]*string{ - `STATIC_DIR`: &StaticDir, - `WEB_ADDR`: &WebAddr, + `OPENAI_API_KEY`: &OpenAIKey, + `STATIC_DIR`: &StaticDir, + `WEB_ADDR`: &WebAddr, } for k, v := range list { s := os.Getenv(k) diff --git a/server/src/core/pool.go b/server/src/core/pool.go index 0e45fee..9d161c6 100644 --- a/server/src/core/pool.go +++ b/server/src/core/pool.go @@ -1,12 +1,16 @@ package core import ( + "net/http" "project/pb" "project/zj" "time" ) -func (c *Core) add(hash [16]byte, req *pb.Req) (pr *row) { +func (c *Core) add(req *pb.Req, hr *http.Request) (pr *row) { + + hash := req.Hash() + c.mux.Lock() pr, ok := c.pool[hash] if ok { @@ -16,6 +20,7 @@ func (c *Core) add(hash [16]byte, req *pb.Req) (pr *row) { } pr = &row{ + hr: hr, hash: hash, req: req, t: time.Now(), diff --git a/server/src/core/req.go b/server/src/core/req.go index 24bf1ff..ffc383f 100644 --- a/server/src/core/req.go +++ b/server/src/core/req.go @@ -1,21 +1,21 @@ package core import ( - "crypto/md5" - "io/ioutil" + "io" "net/http" "project/pb" "project/util" - - "google.golang.org/protobuf/proto" + "project/zj" ) -func req(w http.ResponseWriter, r *http.Request) (p *pb.Req, hash [16]byte, err error) { +func req(w http.ResponseWriter, r *http.Request) (p *pb.Req, err error) { path := r.URL.Path method := r.Method - ab, err := ioutil.ReadAll(http.MaxBytesReader(w, r.Body, 1024*1024)) + zj.J(method, path) + + ab, err := io.ReadAll(http.MaxBytesReader(w, r.Body, 1024*1024)) if err != nil { return } @@ -27,14 +27,7 @@ func req(w http.ResponseWriter, r *http.Request) (p *pb.Req, hash [16]byte, err Method: method, Body: ab, } - - pab, err := proto.Marshal(p) - if err != nil { - err500(w) - return - } - - hash = md5.Sum(pab) + zj.F(`%x %s %s`, p.Hash(), method, path) return } diff --git a/server/src/core/row.go b/server/src/core/row.go index a6170c9..f5d911a 100644 --- a/server/src/core/row.go +++ b/server/src/core/row.go @@ -2,7 +2,9 @@ package core import ( "fmt" + "net/http" "project/pb" + "project/util" "project/zj" "sync" "time" @@ -10,6 +12,7 @@ import ( type row struct { hash [16]byte + hr *http.Request req *pb.Req rsp []byte err error @@ -25,7 +28,10 @@ func (pr *row) run() { s := fmt.Sprintf(`%x, %s`, pr.hash, pr.t.Format(`2006-01-02 15:04:05`)) zj.J(`new`, s) - pr.rsp = []byte(s) + pr.rsp, pr.err = fetchRemote(pr.req) + go pr.saveFile() + go pr.metrics() + pr.done = true pr.mux.Unlock() } @@ -36,3 +42,14 @@ func (pr *row) wait() { pr.mux.RUnlock() } } + +func (pr *row) saveFile() { + rspFile := util.CacheName(pr.req.Hash()) + `-rsp.json` + if !util.FileExists(rspFile) { + util.WriteFile(rspFile, pr.rsp) + zj.J(rspFile) + } +} + +func (pr *row) metrics() { +} diff --git a/server/src/core/rsp.go b/server/src/core/rsp.go new file mode 100644 index 0000000..29ff5d5 --- /dev/null +++ b/server/src/core/rsp.go @@ -0,0 +1,42 @@ +package core + +import ( + "bytes" + "io" + "net/http" + "net/url" + "project/config" + "project/pb" + "project/zj" + "time" +) + +func fetchRemote(r *pb.Req) (ab []byte, err error) { + + u, err := url.Parse(config.RemoteAPI) + if err != nil { + zj.W(`url fail`, config.RemoteAPI, err) + return + } + u.Path = r.Path + + req, err := http.NewRequest(r.Method, u.String(), bytes.NewReader(r.Body)) + if err != nil { + return + } + + req.Header.Set(`Content-Type`, `application/json`) + req.Header.Set(`Authorization`, `Bearer `+config.OpenAIKey) + + client := &http.Client{ + Timeout: 30 * time.Second, + } + rsp, err := client.Do(req) + if err != nil { + return + } + + defer rsp.Body.Close() + + return io.ReadAll(rsp.Body) +} diff --git a/server/src/core/web.go b/server/src/core/web.go index d7889da..2a754b7 100644 --- a/server/src/core/web.go +++ b/server/src/core/web.go @@ -3,19 +3,27 @@ package core import ( "net/http" "project/metrics" + "project/util" ) // WebHandle ... func (c *Core) WebHandle(w http.ResponseWriter, r *http.Request) { - p, hash, err := req(w, r) + p, err := req(w, r) if err != nil { metrics.ReqFailCount() return } metrics.ReqBytes(len(p.Body)) - pr := c.add(hash, p) + pr := c.add(p, r) + + go func() { + reqFile := util.CacheName(p.Hash()) + `-req.json` + if !util.FileExists(reqFile) { + util.WriteFile(reqFile, p.Body) + } + }() pr.wait() diff --git a/server/src/pb/req.go b/server/src/pb/req.go new file mode 100644 index 0000000..86df348 --- /dev/null +++ b/server/src/pb/req.go @@ -0,0 +1,22 @@ +package pb + +import ( + "crypto/md5" + "io" +) + +// Hash ... +func (x *Req) Hash() [16]byte { + + m := md5.New() + + io.WriteString(m, x.Method) + m.Write([]byte{0x00}) + io.WriteString(m, x.Method) + m.Write([]byte{0x00}) + m.Write(x.Body) + + var h [16]byte + copy(h[:], m.Sum(nil)[:]) + return h +} diff --git a/server/src/util/file.go b/server/src/util/file.go index e843406..848ef8f 100644 --- a/server/src/util/file.go +++ b/server/src/util/file.go @@ -4,16 +4,31 @@ import ( "encoding/json" "fmt" "os" + "path/filepath" "project/config" "project/zj" "strings" + "github.com/zhengkai/zu" "google.golang.org/protobuf/proto" ) // DownloadFunc ... type DownloadFunc func(url string) (ab []byte, err error) +// CacheName ... +func CacheName(hash [16]byte) string { + s := fmt.Sprintf(`cache/%x/%x/%x`, hash[0:2], hash[2:4], hash[4:]) + os.MkdirAll(StaticFile(filepath.Dir(s)), 0755) + return s +} + +// FileExists ... +func FileExists(filename string) bool { + filename = fmt.Sprintf(`%s/%s`, config.StaticDir, filename) + return zu.FileExists(filename) +} + // IsURL ... func IsURL(s string) bool { return strings.HasPrefix(s, `https://`) || strings.HasPrefix(s, `http://`) diff --git a/server/src/util/ip.go b/server/src/util/ip.go index 6d0df52..7f9b1e8 100644 --- a/server/src/util/ip.go +++ b/server/src/util/ip.go @@ -8,16 +8,16 @@ import ( "strings" ) -// GetUint32IP ... -func GetUint32IP(r *http.Request) (uint32, error) { +// GetIP ... +func GetIP(r *http.Request) (net.IP, error) { var ipStr string - if realIP := r.Header.Get("X-Real-IP"); realIP != "" { + if realIP := r.Header.Get(`X-Real-IP`); realIP != `` { ipStr = realIP } else { ip, _, err := net.SplitHostPort(r.RemoteAddr) if err != nil { - return 0, err + return nil, err } ipStr = ip } @@ -25,22 +25,21 @@ func GetUint32IP(r *http.Request) (uint32, error) { parsedIP := net.ParseIP(ipStr) if parsedIP == nil { - return 0, errors.New("Invalid IP address") + return nil, errors.New(`Invalid IP address`) } // 检查是否是IPv4 parsedIPv4 := parsedIP.To4() if parsedIPv4 == nil { - return 0, errors.New("IP address not IPv4") + return nil, errors.New(`IP address not IPv4`) } // 检查是否为局域网IP if !parsedIP.IsPrivate() { - return 0, errors.New("Public IP address not allowed") + return nil, errors.New(`Public IP address not allowed`) } - // ip to uint32 - return binary.BigEndian.Uint32(parsedIPv4), nil + return parsedIPv4, nil } // IPString ...