From 6561b99f8fefba164caa34c64419804fe0720cbf Mon Sep 17 00:00:00 2001 From: RockYang Date: Sun, 20 Aug 2023 16:17:42 +0800 Subject: [PATCH] feat: add minio service implementation, download midjourney image to local storage --- api/core/types/config.go | 10 ++++ api/go.mod | 21 ++++++--- api/go.sum | 31 ++++++++++++ api/handler/chat_history_handler.go | 25 +--------- api/handler/chat_item_handler.go | 25 ++++++++++ api/handler/mj_handler.go | 39 +++++++++++++-- api/handler/upload_handler.go | 30 ++---------- api/service/minio_service.go | 54 +++++++++++++++++++++ api/test/test.go | 61 +++++++++++------------- api/utils/{websocket.go => net.go} | 34 ++++++++++++++ api/utils/upload.go | 68 +++++++++++++++++++++++++++ docker/minio/docker-compose.yaml | 12 +++++ web/src/components/ChatMidJourney.vue | 4 +- 13 files changed, 316 insertions(+), 98 deletions(-) create mode 100644 api/service/minio_service.go rename api/utils/{websocket.go => net.go} (56%) create mode 100644 api/utils/upload.go create mode 100644 docker/minio/docker-compose.yaml diff --git a/api/core/types/config.go b/api/core/types/config.go index bf0e4c1..f799cc4 100644 --- a/api/core/types/config.go +++ b/api/core/types/config.go @@ -19,6 +19,7 @@ type AppConfig struct { AesEncryptKey string SmsConfig AliYunSmsConfig // AliYun send message service config ExtConfig ChatPlusExtConfig // ChatPlus extensions callback api config + MinioConfig MinioConfig } type ChatPlusApiConfig struct { @@ -39,6 +40,15 @@ type AliYunSmsConfig struct { Domain string } +type MinioConfig struct { + Endpoint string + AccessKey string + AccessSecret string + Bucket string + UseSSL bool + Domain string +} + type RedisConfig struct { Host string Port int diff --git a/api/go.mod b/api/go.mod index 9ed4c37..507a8a3 100644 --- a/api/go.mod +++ b/api/go.mod @@ -23,6 +23,7 @@ require ( github.com/bytedance/sonic v1.9.1 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/dlclark/regexp2 v1.8.1 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gaukas/godicttls v0.0.3 // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect @@ -31,13 +32,17 @@ require ( github.com/golang/mock v1.6.0 // indirect github.com/gomodule/redigo v2.0.0+incompatible // indirect github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect - github.com/klauspost/compress v1.15.15 // indirect - github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/klauspost/compress v1.16.7 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/minio/md5-simd v1.1.2 // indirect + github.com/minio/minio-go/v7 v7.0.62 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect github.com/onsi/ginkgo/v2 v2.10.0 // indirect github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect @@ -47,16 +52,18 @@ require ( github.com/quic-go/qtls-go1-20 v0.2.2 // indirect github.com/quic-go/quic-go v0.35.1 // indirect github.com/refraction-networking/utls v1.3.2 // indirect + github.com/rs/xid v1.5.0 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect go.uber.org/dig v1.16.1 // indirect golang.org/x/arch v0.3.0 // indirect golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect golang.org/x/mod v0.11.0 // indirect - golang.org/x/net v0.11.0 // indirect - golang.org/x/text v0.10.0 // indirect + golang.org/x/net v0.14.0 // indirect + golang.org/x/text v0.12.0 // indirect golang.org/x/tools v0.10.0 // indirect google.golang.org/protobuf v1.30.0 // indirect - gopkg.in/ini.v1 v1.66.2 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) @@ -78,7 +85,7 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/fx v1.19.3 go.uber.org/multierr v1.6.0 // indirect - golang.org/x/crypto v0.10.0 - golang.org/x/sys v0.9.0 // indirect + golang.org/x/crypto v0.12.0 + golang.org/x/sys v0.11.0 // indirect gorm.io/gorm v1.25.1 ) diff --git a/api/go.sum b/api/go.sum index 94cd6f5..45b58b3 100644 --- a/api/go.sum +++ b/api/go.sum @@ -18,6 +18,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.8.1 h1:6Lcdwya6GjPUNsBct8Lg/yRPwMhABj269AAzdGSiR+0= github.com/dlclark/regexp2 v1.8.1/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= @@ -59,6 +61,8 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 h1:hR7/MlvK23p6+lIw9SN1TigNLn9ZnF3W4SYRKq2gAHs= github.com/google/pprof v0.0.0-20230602150820-91b7bce49751/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= @@ -89,9 +93,14 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= +github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= +github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -103,6 +112,12 @@ github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20230415042440-a5e3d8259 github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20230415042440-a5e3d8259ae0/go.mod h1:C5LA5UO2ZXJrLaPLYtE1wUJMiyd/nwWaCO5cw/2pSHs= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= +github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= +github.com/minio/minio-go/v7 v7.0.62 h1:qNYsFZHEzl+NfH8UxW4jpmlKav1qUAgfY30YNRneVhc= +github.com/minio/minio-go/v7 v7.0.62/go.mod h1:Q6X7Qjb7WMhvG65qKf4gUgA5XaiSox74kR1uAEjxRS4= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -138,6 +153,10 @@ github.com/quic-go/quic-go v0.35.1 h1:b0kzj6b/cQAf05cT0CkQubHM31wiA+xH3IBkxP62po github.com/quic-go/quic-go v0.35.1/go.mod h1:+4CVgVppm0FNjpG3UcX8Joi/frKOH7/ciD5yGcwOO1g= github.com/refraction-networking/utls v1.3.2 h1:o+AkWB57mkcoW36ET7uJ002CpBWHu0KPxi6vzxvPnv8= github.com/refraction-networking/utls v1.3.2/go.mod h1:fmoaOww2bxzzEpIKOebIsnBvjQpqP7L2vcm/9KUfm/E= +github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -180,6 +199,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= +golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -191,6 +212,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= +golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -201,14 +224,20 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -228,6 +257,8 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= diff --git a/api/handler/chat_history_handler.go b/api/handler/chat_history_handler.go index c32972a..065fa89 100644 --- a/api/handler/chat_history_handler.go +++ b/api/handler/chat_history_handler.go @@ -31,30 +31,6 @@ func (h *ChatHandler) Update(c *gin.Context) { resp.SUCCESS(c, types.OkMsg) } -// Remove 删除会话 -func (h *ChatHandler) Remove(c *gin.Context) { - chatId := h.GetTrim(c, "chat_id") - if chatId == "" { - resp.ERROR(c, types.InvalidArgs) - return - } - user, err := utils.GetLoginUser(c, h.db) - if err != nil { - resp.NotAuth(c) - return - } - - res := h.db.Where("user_id = ? AND chat_id = ?", user.Id, chatId).Delete(&model.ChatItem{}) - if res.Error != nil { - resp.ERROR(c, "Failed to update database") - return - } - - // 清空会话上下文 - h.App.ChatContexts.Delete(chatId) - resp.SUCCESS(c, types.OkMsg) -} - // History 获取聊天历史记录 func (h *ChatHandler) History(c *gin.Context) { chatId := c.Query("chat_id") // 会话 ID @@ -108,6 +84,7 @@ func (h *ChatHandler) Clear(c *gin.Context) { // 清空会话上下文 h.App.ChatContexts.Delete(chat.ChatId) } + // 删除所有的会话记录 res = h.db.Where("user_id = ?", user.Id).Delete(&model.ChatItem{}) if res.Error != nil { diff --git a/api/handler/chat_item_handler.go b/api/handler/chat_item_handler.go index 0bf1d75..75c5204 100644 --- a/api/handler/chat_item_handler.go +++ b/api/handler/chat_item_handler.go @@ -1,6 +1,7 @@ package handler import ( + "chatplus/core/types" "chatplus/store/model" "chatplus/store/vo" "chatplus/utils" @@ -46,6 +47,30 @@ func (h *ChatHandler) List(c *gin.Context) { resp.SUCCESS(c, items) } +// Remove 删除会话 +func (h *ChatHandler) Remove(c *gin.Context) { + chatId := h.GetTrim(c, "chat_id") + if chatId == "" { + resp.ERROR(c, types.InvalidArgs) + return + } + user, err := utils.GetLoginUser(c, h.db) + if err != nil { + resp.NotAuth(c) + return + } + + res := h.db.Where("user_id = ? AND chat_id = ?", user.Id, chatId).Delete(&model.ChatItem{}) + if res.Error != nil { + resp.ERROR(c, "Failed to update database") + return + } + + // 清空会话上下文 + h.App.ChatContexts.Delete(chatId) + resp.SUCCESS(c, types.OkMsg) +} + func (h *ChatHandler) Detail(c *gin.Context) { chatId := h.GetTrim(c, "chat_id") if utils.IsEmptyValue(chatId) { diff --git a/api/handler/mj_handler.go b/api/handler/mj_handler.go index 790770b..afc791a 100644 --- a/api/handler/mj_handler.go +++ b/api/handler/mj_handler.go @@ -8,6 +8,7 @@ import ( "chatplus/store/model" "chatplus/utils" "chatplus/utils/resp" + "encoding/base64" "fmt" "github.com/gin-gonic/gin" "gorm.io/gorm" @@ -38,10 +39,20 @@ type MidJourneyHandler struct { leveldb *store.LevelDB db *gorm.DB mjFunc function.FuncMidJourney + //minio *service.MinioService } -func NewMidJourneyHandler(app *core.AppServer, leveldb *store.LevelDB, db *gorm.DB, functions map[string]function.Function) *MidJourneyHandler { - h := MidJourneyHandler{leveldb: leveldb, db: db, mjFunc: functions[types.FuncMidJourney].(function.FuncMidJourney)} +func NewMidJourneyHandler( + app *core.AppServer, + leveldb *store.LevelDB, + db *gorm.DB, + //minio *service.MinioService, + functions map[string]function.Function) *MidJourneyHandler { + h := MidJourneyHandler{ + leveldb: leveldb, + db: db, + //minio: minio, + mjFunc: functions[types.FuncMidJourney].(function.FuncMidJourney)} h.App = app return &h } @@ -87,9 +98,22 @@ func (h *MidJourneyHandler) Notify(c *gin.Context) { resp.SUCCESS(c) return } + // TODO: 下载本地或者 OSS,提供可配置的选项 + // 下载图片到本地服务器 + filePath, err := utils.GenUploadPath(h.App.Config.StaticDir, data.Image.Filename) + if err != nil { + logger.Error("error with generate image dir: ", err) + resp.SUCCESS(c) + return + } + err = utils.DownloadFile(data.Image.URL, filePath, h.App.Config.ProxyURL) + if err != nil { + logger.Error("error with download image: ", err) + resp.SUCCESS(c) + return + } - // TODO: 是否需要把图片下载到本地服务器? - + data.Image.URL = utils.GenUploadUrl(h.App.Config.StaticDir, h.App.Config.StaticUrl, filePath) message := model.HistoryMessage{ UserId: task.UserId, ChatId: task.ChatId, @@ -135,6 +159,13 @@ func (h *MidJourneyHandler) Notify(c *gin.Context) { // delete client h.App.MjTaskClients.Delete(data.Key) } else { + // 使用代理临时转发图片 + if data.Image.URL != "" { + image, err := utils.DownloadImage(data.Image.URL, h.App.Config.ProxyURL) + if err == nil { + data.Image.URL = "data:image/png;base64," + base64.StdEncoding.EncodeToString(image) + } + } utils.ReplyChunkMessage(wsClient, types.WsMessage{Type: types.WsMjImg, Content: data}) } resp.SUCCESS(c, "SUCCESS") diff --git a/api/handler/upload_handler.go b/api/handler/upload_handler.go index 0398e7a..ec52cc1 100644 --- a/api/handler/upload_handler.go +++ b/api/handler/upload_handler.go @@ -2,13 +2,11 @@ package handler import ( "chatplus/core" + "chatplus/utils" "chatplus/utils/resp" "fmt" "github.com/gin-gonic/gin" "gorm.io/gorm" - "os" - "path/filepath" - "time" ) type UploadHandler struct { @@ -29,7 +27,7 @@ func (h *UploadHandler) Upload(c *gin.Context) { return } - filePath, err := h.genFilePath(file.Filename) + filePath, err := utils.GenUploadPath(h.App.Config.StaticDir, file.Filename) if err != nil { resp.ERROR(c, fmt.Sprintf("文件上传失败: %s", err.Error())) return @@ -41,27 +39,5 @@ func (h *UploadHandler) Upload(c *gin.Context) { return } - resp.SUCCESS(c, h.genFileUrl(filePath)) -} - -// 生成上传文件路径 -func (h *UploadHandler) genFilePath(filename string) (string, error) { - now := time.Now() - dir := fmt.Sprintf("%s/upload/%d/%d", h.App.Config.StaticDir, now.Year(), now.Month()) - _, err := os.Stat(dir) - if err != nil { - err = os.MkdirAll(dir, 0755) - if err != nil { - return "", fmt.Errorf("创建上传目录失败:%s", err) - } - } - fileExt := filepath.Ext(filename) - return fmt.Sprintf("%s/%d%s", dir, now.UnixMilli(), fileExt), nil -} - -// 生成上传文件 URL -func (h *UploadHandler) genFileUrl(filePath string) string { - now := time.Now() - filename := filepath.Base(filePath) - return fmt.Sprintf("%s/upload/%d/%d/%s", h.App.Config.StaticUrl, now.Year(), now.Month(), filename) + resp.SUCCESS(c, utils.GenUploadUrl(h.App.Config.StaticDir, h.App.Config.StaticUrl, filePath)) } diff --git a/api/service/minio_service.go b/api/service/minio_service.go new file mode 100644 index 0000000..c35339e --- /dev/null +++ b/api/service/minio_service.go @@ -0,0 +1,54 @@ +package service + +import ( + "chatplus/core/types" + "chatplus/utils" + "context" + "fmt" + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" + "net/url" + "path" + "strings" +) + +type MinioService struct { + config *types.AppConfig + client *minio.Client +} + +func NewMinioService(config *types.AppConfig) (*MinioService, error) { + minioClient, err := minio.New(config.MinioConfig.Endpoint, &minio.Options{ + Creds: credentials.NewStaticV4(config.MinioConfig.AccessKey, config.MinioConfig.AccessSecret, ""), + Secure: config.MinioConfig.UseSSL, + }) + if err != nil { + return nil, err + } + return &MinioService{config: config, client: minioClient}, nil +} + +func (s *MinioService) UploadMjImg(imageURL string) (string, error) { + parsedURL, err := url.Parse(imageURL) + if err != nil { + return "", err + } + + filename := path.Base(parsedURL.Path) + imageBytes, err := utils.DownloadImage(imageURL, s.config.ProxyURL) + if err != nil { + return "", err + } + + info, err := s.client.PutObject( + context.Background(), + s.config.MinioConfig.Bucket, + filename, + strings.NewReader(string(imageBytes)), + int64(len(imageBytes)), + minio.PutObjectOptions{ContentType: "image/png"}) + if err != nil { + return "", err + } + return fmt.Sprintf("%s/%s/%s", s.config.MinioConfig.Domain, s.config.MinioConfig.Bucket, info.Key), nil +} diff --git a/api/test/test.go b/api/test/test.go index d7b2dcb..b707484 100644 --- a/api/test/test.go +++ b/api/test/test.go @@ -2,9 +2,9 @@ package main import ( "bufio" + "chatplus/core" "chatplus/core/types" - "chatplus/store/model" - "chatplus/store/vo" + "chatplus/service" "chatplus/utils" "context" "encoding/json" @@ -20,7 +20,7 @@ import ( ) func main() { - fmt.Println(utils.RandString(32)) + minio() } // Http client 取消操作 @@ -92,37 +92,6 @@ func testIp2Region() { } -func testJson() { - - var role = model.ChatRole{ - Key: "programmer", - Name: "程序员", - Context: "[{\"role\":\"user\",\"content\":\"现在开始你扮演一位程序员,你是一名优秀的程序员,具有很强的逻辑思维能力,总能高效的解决问题。你热爱编程,熟悉多种编程语言,尤其精通 Go 语言,注重代码质量,有创新意识,持续学习,良好的沟通协作。\"},{\"role\"\n:\"assistant\",\"content\":\"好的,现在我将扮演一位程序员,非常感谢您对我的评价。作为一名优秀的程序员,我非常热爱编程,并且注重代码质量。我熟悉多种编程语言,尤其是 Go 语言,可以使用它来高效地解决各种问题。\"}]", - HelloMsg: "Talk is cheap, i will show code!", - Icon: "images/avatar/programmer.jpg", - Enable: true, - Sort: 1, - } - role.Id = 1 - var v vo.ChatRole - - err := utils.CopyObject(role, &v) - if err != nil { - log.Fatal(err) - } - - fmt.Printf("%+v\n", v.Id) - - //var v2 = model.ChatRoles{} - //err = utils.CopyObject(v, &v2) - //if err != nil { - // log.Fatal(err) - //} - // - //fmt.Printf("%+v\n", v2.Id) - -} - func calTokens() { text := "须知少年凌云志,曾许人间第一流" encoding := "cl100k_base" @@ -201,3 +170,27 @@ func extractFunction() error { fmt.Println(strings.Join(contents, "")) return err } + +func minio() { + config := core.NewDefaultConfig() + config.ProxyURL = "http://localhost:7777" + config.MinioConfig = types.MinioConfig{ + Endpoint: "localhost:9010", + AccessKey: "ObWIEyXaQUHOYU26L0oI", + AccessSecret: "AJW3HHhlGrprfPcmiC7jSOSzVCyrlhX4AnOAUzqI", + Bucket: "chatgpt-plus", + UseSSL: false, + Domain: "http://localhost:9010", + } + minioService, err := service.NewMinioService(config) + if err != nil { + panic(err) + } + + url, err := minioService.UploadMjImg("https://cdn.discordapp.com/attachments/1139552247693443184/1141619433752768572/lisamiller4099_A_beautiful_fairy_sister_from_Chinese_mythology__3162726e-5ee4-4f60-932b-6b78b375eaef.png") + if err != nil { + panic(err) + } + + fmt.Println(url) +} diff --git a/api/utils/websocket.go b/api/utils/net.go similarity index 56% rename from api/utils/websocket.go rename to api/utils/net.go index a1d0399..04e7f73 100644 --- a/api/utils/websocket.go +++ b/api/utils/net.go @@ -4,6 +4,9 @@ import ( "chatplus/core/types" logger2 "chatplus/logger" "encoding/json" + "io" + "net/http" + "net/url" ) var logger = logger2.GetLogger() @@ -27,3 +30,34 @@ func ReplyMessage(ws *types.WsClient, message interface{}) { ReplyChunkMessage(ws, types.WsMessage{Type: types.WsMiddle, Content: message}) ReplyChunkMessage(ws, types.WsMessage{Type: types.WsEnd}) } + +func DownloadImage(imageURL string, proxy string) ([]byte, error) { + var client *http.Client + if proxy == "" { + client = http.DefaultClient + } else { + proxyURL, _ := url.Parse(proxy) + client = &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyURL(proxyURL), + }, + } + } + req, err := http.NewRequest("GET", imageURL, nil) + if err != nil { + return nil, err + } + + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + imageBytes, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return imageBytes, nil +} diff --git a/api/utils/upload.go b/api/utils/upload.go new file mode 100644 index 0000000..adac6f1 --- /dev/null +++ b/api/utils/upload.go @@ -0,0 +1,68 @@ +package utils + +import ( + "fmt" + "io" + "net/http" + "net/url" + "os" + "path/filepath" + "strings" + "time" +) + +// GenUploadPath 生成上传文件路径 +func GenUploadPath(basePath, filename string) (string, error) { + now := time.Now() + dir := fmt.Sprintf("%s/upload/%d/%d", basePath, now.Year(), now.Month()) + _, err := os.Stat(dir) + if err != nil { + err = os.MkdirAll(dir, 0755) + if err != nil { + return "", fmt.Errorf("创建上传目录失败:%s", err) + } + } + fileExt := filepath.Ext(filename) + return fmt.Sprintf("%s/%d%s", dir, now.UnixMilli(), fileExt), nil +} + +// GenUploadUrl 生成上传文件 URL +func GenUploadUrl(basePath, baseUrl string, filePath string) string { + return strings.Replace(filePath, basePath, baseUrl, 1) +} + +func DownloadFile(fileURL string, filepath string, proxy string) error { + var client *http.Client + if proxy == "" { + client = http.DefaultClient + } else { + proxyURL, _ := url.Parse(proxy) + client = &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyURL(proxyURL), + }, + } + } + req, err := http.NewRequest("GET", fileURL, nil) + if err != nil { + return err + } + + resp, err := client.Do(req) + if err != nil { + return err + } + + file, err := os.Create(filepath) + if err != nil { + return err + } + defer file.Close() + + _, err = io.Copy(file, resp.Body) + if err != nil { + return err + } + + return nil +} diff --git a/docker/minio/docker-compose.yaml b/docker/minio/docker-compose.yaml new file mode 100644 index 0000000..3e248ac --- /dev/null +++ b/docker/minio/docker-compose.yaml @@ -0,0 +1,12 @@ +version: '3' +services: + minio: + image: minio/minio + volumes: + - ./data:/data + ports: + - 9000:9000 + environment: + MINIO_ROOT_USER: minio + MINIO_ROOT_PASSWORD: minio@pass + command: server /data diff --git a/web/src/components/ChatMidJourney.vue b/web/src/components/ChatMidJourney.vue index 89f98e2..34851c8 100644 --- a/web/src/components/ChatMidJourney.vue +++ b/web/src/components/ChatMidJourney.vue @@ -22,7 +22,7 @@ @@ -62,7 +62,7 @@