mirror of
https://gitee.com/blackfox/geekai.git
synced 2024-12-01 19:57:37 +08:00
feat: added delete file function
This commit is contained in:
parent
3d159a833e
commit
c83ac48bd2
@ -5,6 +5,11 @@
|
||||
* Bug修复:修复 issue [
|
||||
管理界面操作用户存在的两个问题](https://github.com/yangjian102621/chatgpt-plus/issues/117#issuecomment-1909201532)
|
||||
* 功能优化:在对话和聊天记录表中新增冗余字段 model,存储对话模型
|
||||
* Bug修复:IPhone 手机验证码触摸事件坐标错位 [issue 144](https://github.com/yangjian102621/chatgpt-plus/issues/144)
|
||||
* Bug修复:重新生成按钮功能失效问题
|
||||
* Bug修复:对话输入HTML标签不显示的问题
|
||||
* 功能优化:gpt-4-all/gpts/midjourney-plus 支持第三方平台的 API KEY
|
||||
* 功能新增:新增删除文件功能
|
||||
|
||||
## v3.2.6
|
||||
* 功能优化:恢复关闭注册系统配置项,管理员可以在后台关闭用户注册,只允许内部添加账号
|
||||
|
@ -462,11 +462,7 @@ func (h *ChatHandler) doRequest(ctx context.Context, req types.ApiRequest, platf
|
||||
req.Messages = nil
|
||||
break
|
||||
default:
|
||||
if req.Model == "gpt-4-all" || strings.HasPrefix(req.Model, "gpt-4-gizmo-g-") {
|
||||
apiURL = "https://gpt.bemore.lol/v1/chat/completions"
|
||||
} else {
|
||||
apiURL = apiKey.ApiURL
|
||||
}
|
||||
apiURL = apiKey.ApiURL
|
||||
}
|
||||
// 更新 API KEY 的最后使用时间
|
||||
h.db.Model(apiKey).UpdateColumn("last_used_at", time.Now().Unix())
|
||||
|
@ -35,6 +35,7 @@ func (h *UploadHandler) Upload(c *gin.Context) {
|
||||
res := h.db.Create(&model.File{
|
||||
UserId: userId,
|
||||
Name: file.Name,
|
||||
ObjKey: file.ObjKey,
|
||||
URL: file.URL,
|
||||
Ext: file.Ext,
|
||||
Size: file.Size,
|
||||
@ -52,7 +53,7 @@ func (h *UploadHandler) List(c *gin.Context) {
|
||||
userId := h.GetLoginUserId(c)
|
||||
var items []model.File
|
||||
var files = make([]vo.File, 0)
|
||||
h.db.Debug().Where("user_id = ?", userId).Find(&items)
|
||||
h.db.Where("user_id = ?", userId).Find(&items)
|
||||
if len(items) > 0 {
|
||||
for _, v := range items {
|
||||
var file vo.File
|
||||
@ -68,3 +69,29 @@ func (h *UploadHandler) List(c *gin.Context) {
|
||||
|
||||
resp.SUCCESS(c, files)
|
||||
}
|
||||
|
||||
// Remove remove files
|
||||
func (h *UploadHandler) Remove(c *gin.Context) {
|
||||
userId := h.GetLoginUserId(c)
|
||||
id := h.GetInt(c, "id", 0)
|
||||
var file model.File
|
||||
tx := h.db.Where("user_id = ? AND id = ?", userId, id).First(&file)
|
||||
if tx.Error != nil || file.Id == 0 {
|
||||
resp.ERROR(c, "file not existed")
|
||||
return
|
||||
}
|
||||
|
||||
// remove database
|
||||
tx = h.db.Model(&model.File{}).Delete("id = ?", id)
|
||||
if tx.Error != nil || tx.RowsAffected == 0 {
|
||||
resp.ERROR(c, "failed to update database")
|
||||
return
|
||||
}
|
||||
// remove files
|
||||
objectKey := file.ObjKey
|
||||
if objectKey == "" {
|
||||
objectKey = file.URL
|
||||
}
|
||||
_ = h.uploaderManager.GetUploadHandler().Delete(objectKey)
|
||||
resp.SUCCESS(c)
|
||||
}
|
||||
|
@ -218,6 +218,7 @@ func main() {
|
||||
fx.Invoke(func(s *core.AppServer, h *handler.UploadHandler) {
|
||||
s.Engine.POST("/api/upload", h.Upload)
|
||||
s.Engine.GET("/api/upload/list", h.List)
|
||||
s.Engine.GET("/api/upload/remove", h.Remove)
|
||||
}),
|
||||
fx.Invoke(func(s *core.AppServer, h *handler.SmsHandler) {
|
||||
group := s.Engine.Group("/api/sms/")
|
||||
|
@ -33,8 +33,6 @@ func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderMa
|
||||
if config.Enabled == false {
|
||||
continue
|
||||
}
|
||||
// rewrite api key
|
||||
config.ApiURL = "https://api.chat-plus.net"
|
||||
client := plus.NewClient(config)
|
||||
name := fmt.Sprintf("mj-service-plus-%d", k)
|
||||
servicePlus := plus.NewService(name, taskQueue, notifyQueue, 10, 600, db, client)
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||
@ -67,10 +68,11 @@ func (s AliYunOss) PutFile(ctx *gin.Context, name string) (File, error) {
|
||||
}
|
||||
|
||||
return File{
|
||||
Name: file.Filename,
|
||||
URL: fmt.Sprintf("%s/%s", s.config.Domain, objectKey),
|
||||
Ext: fileExt,
|
||||
Size: file.Size,
|
||||
Name: file.Filename,
|
||||
ObjKey: objectKey,
|
||||
URL: fmt.Sprintf("%s/%s", s.config.Domain, objectKey),
|
||||
Ext: fileExt,
|
||||
Size: file.Size,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -100,9 +102,14 @@ func (s AliYunOss) PutImg(imageURL string, useProxy bool) (string, error) {
|
||||
}
|
||||
|
||||
func (s AliYunOss) Delete(fileURL string) error {
|
||||
objectName := filepath.Base(fileURL)
|
||||
key := fmt.Sprintf("%s/%s", s.config.SubDir, objectName)
|
||||
return s.bucket.DeleteObject(key)
|
||||
var objectKey string
|
||||
if strings.HasPrefix(fileURL, "http") {
|
||||
filename := filepath.Base(fileURL)
|
||||
objectKey = fmt.Sprintf("%s/%s", s.config.SubDir, filename)
|
||||
} else {
|
||||
objectKey = fileURL
|
||||
}
|
||||
return s.bucket.DeleteObject(objectKey)
|
||||
}
|
||||
|
||||
var _ Uploader = AliYunOss{}
|
||||
|
@ -42,10 +42,11 @@ func (s LocalStorage) PutFile(ctx *gin.Context, name string) (File, error) {
|
||||
|
||||
ext := filepath.Ext(file.Filename)
|
||||
return File{
|
||||
Name: file.Filename,
|
||||
URL: utils.GenUploadUrl(s.config.BasePath, s.config.BaseURL, path),
|
||||
Ext: ext,
|
||||
Size: file.Size,
|
||||
Name: file.Filename,
|
||||
ObjKey: path,
|
||||
URL: utils.GenUploadUrl(s.config.BasePath, s.config.BaseURL, path),
|
||||
Ext: ext,
|
||||
Size: file.Size,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -73,6 +74,9 @@ func (s LocalStorage) PutImg(imageURL string, useProxy bool) (string, error) {
|
||||
}
|
||||
|
||||
func (s LocalStorage) Delete(fileURL string) error {
|
||||
if _, err := os.Stat(fileURL); err == nil {
|
||||
return os.Remove(fileURL)
|
||||
}
|
||||
filePath := strings.Replace(fileURL, s.config.BaseURL, s.config.BasePath, 1)
|
||||
return os.Remove(filePath)
|
||||
}
|
||||
|
@ -88,17 +88,23 @@ func (s MiniOss) PutFile(ctx *gin.Context, name string) (File, error) {
|
||||
}
|
||||
|
||||
return File{
|
||||
Name: file.Filename,
|
||||
URL: fmt.Sprintf("%s/%s/%s", s.config.Domain, s.config.Bucket, info.Key),
|
||||
Ext: fileExt,
|
||||
Size: file.Size,
|
||||
Name: file.Filename,
|
||||
ObjKey: info.Key,
|
||||
URL: fmt.Sprintf("%s/%s/%s", s.config.Domain, s.config.Bucket, info.Key),
|
||||
Ext: fileExt,
|
||||
Size: file.Size,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s MiniOss) Delete(fileURL string) error {
|
||||
objectName := filepath.Base(fileURL)
|
||||
key := fmt.Sprintf("%s/%s", s.config.SubDir, objectName)
|
||||
return s.client.RemoveObject(context.Background(), s.config.Bucket, key, minio.RemoveObjectOptions{})
|
||||
var objectKey string
|
||||
if strings.HasPrefix(fileURL, "http") {
|
||||
filename := filepath.Base(fileURL)
|
||||
objectKey = fmt.Sprintf("%s/%s", s.config.SubDir, filename)
|
||||
} else {
|
||||
objectKey = fileURL
|
||||
}
|
||||
return s.client.RemoveObject(context.Background(), s.config.Bucket, objectKey, minio.RemoveObjectOptions{})
|
||||
}
|
||||
|
||||
var _ Uploader = MiniOss{}
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@ -75,10 +76,11 @@ func (s QinNiuOss) PutFile(ctx *gin.Context, name string) (File, error) {
|
||||
}
|
||||
|
||||
return File{
|
||||
Name: file.Filename,
|
||||
URL: fmt.Sprintf("%s/%s", s.config.Domain, ret.Key),
|
||||
Ext: fileExt,
|
||||
Size: file.Size,
|
||||
Name: file.Filename,
|
||||
ObjKey: key,
|
||||
URL: fmt.Sprintf("%s/%s", s.config.Domain, ret.Key),
|
||||
Ext: fileExt,
|
||||
Size: file.Size,
|
||||
}, nil
|
||||
|
||||
}
|
||||
@ -111,9 +113,15 @@ func (s QinNiuOss) PutImg(imageURL string, useProxy bool) (string, error) {
|
||||
}
|
||||
|
||||
func (s QinNiuOss) Delete(fileURL string) error {
|
||||
objectName := filepath.Base(fileURL)
|
||||
key := fmt.Sprintf("%s/%s", s.config.SubDir, objectName)
|
||||
return s.manager.Delete(s.config.Bucket, key)
|
||||
var objectKey string
|
||||
if strings.HasPrefix(fileURL, "http") {
|
||||
filename := filepath.Base(fileURL)
|
||||
objectKey = fmt.Sprintf("%s/%s", s.config.SubDir, filename)
|
||||
} else {
|
||||
objectKey = fileURL
|
||||
}
|
||||
|
||||
return s.manager.Delete(s.config.Bucket, objectKey)
|
||||
}
|
||||
|
||||
var _ Uploader = QinNiuOss{}
|
||||
|
@ -8,10 +8,11 @@ const QiNiu = "QINIU"
|
||||
const AliYun = "ALIYUN"
|
||||
|
||||
type File struct {
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
URL string `json:"url"`
|
||||
Ext string `json:"ext"`
|
||||
Name string `json:"name"`
|
||||
ObjKey string `json:"obj_key"`
|
||||
Size int64 `json:"size"`
|
||||
URL string `json:"url"`
|
||||
Ext string `json:"ext"`
|
||||
}
|
||||
type Uploader interface {
|
||||
PutFile(ctx *gin.Context, name string) (File, error)
|
||||
|
@ -6,6 +6,7 @@ type File struct {
|
||||
Id uint `gorm:"primarykey;column:id"`
|
||||
UserId uint
|
||||
Name string
|
||||
ObjKey string
|
||||
URL string
|
||||
Ext string
|
||||
Size int64
|
||||
|
@ -1,9 +1,10 @@
|
||||
package vo
|
||||
|
||||
type File struct {
|
||||
Id uint
|
||||
Id uint `json:"id"`
|
||||
UserId uint `json:"user_id"`
|
||||
Name string `json:"name"`
|
||||
ObjKey string `json:"obj_key"`
|
||||
URL string `json:"url"`
|
||||
Ext string `json:"ext"`
|
||||
Size int64 `json:"size"`
|
||||
|
@ -10,4 +10,6 @@ UPDATE chatgpt_chat_items s SET model=(SELECT value FROM chatgpt_chat_models WHE
|
||||
UPDATE chatgpt_chat_history s SET model=(SELECT model FROM chatgpt_chat_items WHERE chat_id = s.chat_id);
|
||||
|
||||
-- 清理对话已删除的聊天记录(可选)
|
||||
-- DELETE FROM `chatgpt_chat_history` WHERE model is NULL;
|
||||
-- DELETE FROM `chatgpt_chat_history` WHERE model is NULL;
|
||||
|
||||
ALTER TABLE `chatgpt_files` ADD `obj_key` VARCHAR(100) NULL COMMENT '文件标识' AFTER `name`;
|
@ -39,9 +39,13 @@
|
||||
effect="dark"
|
||||
:content="file.name"
|
||||
placement="top">
|
||||
<el-image :src="file.url" fit="fill" v-if="isImage(file.ext)" @click="insertURL(file.url)"/>
|
||||
<el-image :src="getFileIcon(file.ext)" fit="fill" v-else @click="insertURL(file.url)"/>
|
||||
<el-image :src="file.url" fit="cover" v-if="isImage(file.ext)" @click="insertURL(file.url)"/>
|
||||
<el-image :src="getFileIcon(file.ext)" fit="cover" v-else @click="insertURL(file.url)"/>
|
||||
</el-tooltip>
|
||||
|
||||
<div class="opt">
|
||||
<el-button type="danger" size="small" :icon="Delete" @click="removeFile(file)" circle/>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@ -54,8 +58,8 @@
|
||||
import {ref} from "vue";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import {PictureFilled, Plus} from "@element-plus/icons-vue";
|
||||
import {isImage} from "@/utils/libs";
|
||||
import {Delete, PictureFilled, Plus} from "@element-plus/icons-vue";
|
||||
import {isImage, removeArrayItem} from "@/utils/libs";
|
||||
|
||||
const props = defineProps({
|
||||
userId: String,
|
||||
@ -103,6 +107,17 @@ const afterRead = (file) => {
|
||||
})
|
||||
};
|
||||
|
||||
const removeFile = (file) => {
|
||||
httpGet('/api/upload/remove?id=' + file.id).then(() => {
|
||||
fileList.value = removeArrayItem(fileList.value, file, (v1, v2) => {
|
||||
return v1.id === v2.id
|
||||
})
|
||||
ElMessage.success("文件删除成功!")
|
||||
}).catch((e) => {
|
||||
ElMessage.error('文件删除失败:' + e.message)
|
||||
})
|
||||
}
|
||||
|
||||
const insertURL = (url) => {
|
||||
show.value = false
|
||||
emits('selected', url)
|
||||
@ -129,6 +144,7 @@ const insertURL = (url) => {
|
||||
|
||||
.grid-content {
|
||||
margin-bottom 10px
|
||||
position relative
|
||||
|
||||
.avatar-uploader {
|
||||
width 100%
|
||||
@ -145,6 +161,7 @@ const insertURL = (url) => {
|
||||
}
|
||||
|
||||
.el-image {
|
||||
width 100%
|
||||
height 80px
|
||||
border 1px solid #ffffff
|
||||
border-radius 6px
|
||||
@ -160,6 +177,19 @@ const insertURL = (url) => {
|
||||
color #20a0ff
|
||||
font-size 40px
|
||||
}
|
||||
|
||||
.opt {
|
||||
display none
|
||||
position absolute
|
||||
top 5px
|
||||
right 5px
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.opt {
|
||||
display block
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user