This commit is contained in:
Zheng Kai
2023-03-30 16:41:44 +08:00
parent 049277f1f6
commit af429a393a
13 changed files with 155 additions and 29 deletions

16
proto/rsp.proto Normal file
View File

@@ -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;
}

View File

@@ -1 +1,2 @@
/.git-hash
/env.sh

View File

@@ -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"

View File

@@ -8,4 +8,8 @@ var (
StaticDir = `/www/orca/static`
WebAddr = `localhost:22035`
RemoteAPI = `https://api.openai.com`
OpenAIKey = ``
)

View File

@@ -10,6 +10,7 @@ func init() {
Dir, _ = filepath.Abs(filepath.Dir(os.Args[0]))
list := map[string]*string{
`OPENAI_API_KEY`: &OpenAIKey,
`STATIC_DIR`: &StaticDir,
`WEB_ADDR`: &WebAddr,
}

View File

@@ -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(),

View File

@@ -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
}

View File

@@ -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() {
}

42
server/src/core/rsp.go Normal file
View File

@@ -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)
}

View File

@@ -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()

22
server/src/pb/req.go Normal file
View File

@@ -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
}

View File

@@ -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://`)

View File

@@ -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 ...