mirror of
https://github.com/zhengkai/orca.git
synced 2026-02-04 15:12:26 +08:00
up
This commit is contained in:
16
proto/rsp.proto
Normal file
16
proto/rsp.proto
Normal 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;
|
||||
}
|
||||
1
server/build/.gitignore
vendored
1
server/build/.gitignore
vendored
@@ -1 +1,2 @@
|
||||
/.git-hash
|
||||
/env.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"
|
||||
|
||||
|
||||
@@ -8,4 +8,8 @@ var (
|
||||
StaticDir = `/www/orca/static`
|
||||
|
||||
WebAddr = `localhost:22035`
|
||||
|
||||
RemoteAPI = `https://api.openai.com`
|
||||
|
||||
OpenAIKey = ``
|
||||
)
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
42
server/src/core/rsp.go
Normal 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)
|
||||
}
|
||||
@@ -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
22
server/src/pb/req.go
Normal 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
|
||||
}
|
||||
@@ -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://`)
|
||||
|
||||
@@ -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 ...
|
||||
|
||||
Reference in New Issue
Block a user