feat(api): test doc upload
This commit is contained in:
parent
6851fe95a0
commit
2d3cf0857a
@ -3,7 +3,7 @@ http_port: 8080
|
||||
prometheus_port: 18090
|
||||
host: http://localhost:8080
|
||||
databases:
|
||||
storage: minio://admin:uh8Cz62LKFDe9tBHg2PVyDVX7XKqE584xP@10.16.129.140:31141/pangu2-demo
|
||||
storage: minio://admin:uh8Cz62LKFDe9tBHg2PVyDVX7XKqE584xP@10.16.129.140:31141?bucket=pangu2-demo
|
||||
postgresql: postgres://postgres:TvpHKxDhXzMsVkXWdzwiwUw9KmaLuGdRJo@10.16.129.140:31258/octopus
|
||||
qdrant: 10.16.129.104:32352
|
||||
casdoor:
|
||||
|
@ -34,10 +34,11 @@ func (f *DocFolder) ParentID() string {
|
||||
return parts[len(parts)-1]
|
||||
}
|
||||
|
||||
func (df *DocFolder) ToSchema() *schema.DocFolder {
|
||||
func (df DocFolder) ToSchema() *schema.DocFolder {
|
||||
return &schema.DocFolder{
|
||||
ID: df.ID,
|
||||
Name: df.Name,
|
||||
IsDefault: df.IsDefault,
|
||||
CreatedAt: df.CreatedAt,
|
||||
UpdatedAt: df.UpdatedAt,
|
||||
}
|
||||
@ -89,7 +90,7 @@ func (d *Doc) ToSchema(ctx context.Context) *schema.Doc {
|
||||
return &schema.Doc{
|
||||
ID: d.ID,
|
||||
Folder: d.Folder.ToSchema(),
|
||||
PresignedURL: url,
|
||||
PresignedURL: url.String(),
|
||||
UploadedAt: d.UploadedAt,
|
||||
CreatedAt: d.CreatedAt,
|
||||
UpdatedAt: d.UpdatedAt,
|
||||
|
@ -34,13 +34,13 @@ func RegisterDocRouter(app *soda.Soda) {
|
||||
AddJWTSecurity(JWTRequired).
|
||||
SetParameters(schema.DocID{}).
|
||||
AddJSONResponse(200, nil).OK()
|
||||
app.Post("/docs/batch/delete", DeleteDoc).
|
||||
app.Post("/docs/batch/delete", DeleteDocBatch).
|
||||
AddTags("文档管理").
|
||||
SetSummary("批量-文件删除").
|
||||
AddJWTSecurity(JWTRequired).
|
||||
SetJSONRequestBody(schema.DocsBatchDelete{}).
|
||||
AddJSONResponse(200, schema.DocBatchResults{}).OK()
|
||||
app.Post("/docs/batch/update", UpdateDoc).
|
||||
app.Post("/docs/batch/update", UpdateDocBatch).
|
||||
AddTags("文档管理").
|
||||
SetSummary("批量-文件更新").
|
||||
AddJWTSecurity(JWTRequired).
|
||||
@ -132,7 +132,7 @@ func CreateUploadURL(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
return c.JSON(schema.UploadURL{
|
||||
URL: *u,
|
||||
URL: u.String(),
|
||||
ObjectName: objectName,
|
||||
})
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
type DocFolder struct {
|
||||
ID string `json:"id" oai:"description=文件夹ID"`
|
||||
Name string `json:"name" oai:"description=文件夹名称"`
|
||||
IsDefault bool `json:"is_default,omitempty" oai:"description=是否默认分组"`
|
||||
CreatedAt time.Time `json:"created_at" oai:"description=创建时间"`
|
||||
UpdatedAt time.Time `json:"updated_at" oai:"description=更新时间"`
|
||||
}
|
||||
@ -38,7 +38,7 @@ type DocFolderWithChildren struct {
|
||||
type Doc struct {
|
||||
ID string `json:"id" oai:"description=文档ID"`
|
||||
Folder *DocFolder `json:"folder" oai:"description=归属文件夹信息"`
|
||||
PresignedURL *url.URL `json:"presigned_url" oai:"description=文档预签名下载URL(临时下载URL)"`
|
||||
PresignedURL string `json:"presigned_url" oai:"description=文档预签名下载URL(临时下载URL)"`
|
||||
UploadedAt time.Time `json:"uploaded_at" oai:"description=上传时间"`
|
||||
CreatedAt time.Time `json:"created_at" oai:"description=创建时间"`
|
||||
UpdatedAt time.Time `json:"updated_at" oai:"description=更新时间"`
|
||||
@ -76,7 +76,7 @@ type DocsBatchDelete struct {
|
||||
|
||||
type DocsBatchUpdate struct {
|
||||
IDs []string `json:"ids" validate:"required" oai:"description=批量选择文件ID列表"`
|
||||
FolderID string `json:"folder_id" validate:"required" oai:"description=更新归属文件夹ID"`
|
||||
FolderID *string `json:"folder_id" oai:"description=更新归属文件夹ID"`
|
||||
}
|
||||
|
||||
type DocBatchResult struct {
|
||||
@ -92,6 +92,6 @@ type CreateUploadURL struct {
|
||||
}
|
||||
|
||||
type UploadURL struct {
|
||||
URL url.URL `json:"url" oai:"description=生成的预签名上传URL"`
|
||||
URL string `json:"url" oai:"description=生成的预签名上传URL"`
|
||||
ObjectName string `json:"object_name" oai:"description=上传文件名"`
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ import (
|
||||
"octopus/internal/dal/model"
|
||||
"octopus/internal/dal/query"
|
||||
"octopus/internal/schema"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/casdoor/casdoor-go-sdk/casdoorsdk"
|
||||
@ -19,7 +18,9 @@ import (
|
||||
)
|
||||
|
||||
func ListDocs(ctx context.Context, auth *casdoorsdk.User, param *schema.ListDocQuery) ([]*model.Doc, int64, error) {
|
||||
base := query.Doc.Where(query.Doc.OrgID.Eq(auth.Owner))
|
||||
base := query.Doc.
|
||||
Where(query.Doc.OrgID.Eq(auth.Owner)).
|
||||
Preload(query.Doc.Folder)
|
||||
if param.FolderIDs != nil {
|
||||
base = base.Where(query.Doc.FolderID.In(*param.FolderIDs...))
|
||||
}
|
||||
@ -47,8 +48,8 @@ func CreateDoc(ctx context.Context, auth *casdoorsdk.User, param *schema.CreateD
|
||||
}
|
||||
|
||||
// 将对象从临时地址移动到新地址
|
||||
newPath := fmt.Sprintf("%s/%s/%s", auth.Owner, folder.ParentPath, param.Name)
|
||||
if err := storage.MoveObject(ctx, param.ObjectName, newPath); err != nil {
|
||||
newObjectName := fmt.Sprintf("%s/%s/%s", auth.Owner, time.Now().UTC().Format(time.DateOnly), param.Name)
|
||||
if err := storage.MoveObject(ctx, param.ObjectName, newObjectName); err != nil {
|
||||
return nil, fmt.Errorf("failed to move object: %w", err)
|
||||
}
|
||||
|
||||
@ -58,9 +59,10 @@ func CreateDoc(ctx context.Context, auth *casdoorsdk.User, param *schema.CreateD
|
||||
IsDeletable: true,
|
||||
IsEditable: true,
|
||||
FolderID: param.FolderID,
|
||||
ObjectName: newPath,
|
||||
ObjectName: newObjectName,
|
||||
UploadedAt: stat.LastModified,
|
||||
CreatedBy: auth.Id,
|
||||
Folder: folder,
|
||||
}
|
||||
if err := query.Doc.Create(&doc); err != nil {
|
||||
return nil, fmt.Errorf("failed to create doc: %w", err)
|
||||
@ -69,7 +71,13 @@ func CreateDoc(ctx context.Context, auth *casdoorsdk.User, param *schema.CreateD
|
||||
}
|
||||
|
||||
func GetDoc(ctx context.Context, auth *casdoorsdk.User, id string) (*model.Doc, error) {
|
||||
doc, err := query.Doc.Where(query.Doc.OrgID.Eq(auth.Owner), query.Doc.ID.Eq(id)).Take()
|
||||
doc, err := query.Doc.
|
||||
Where(
|
||||
query.Doc.OrgID.Eq(auth.Owner),
|
||||
query.Doc.ID.Eq(id),
|
||||
).
|
||||
Preload(query.Doc.Folder).
|
||||
Take()
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fmt.Errorf("specified doc not exists")
|
||||
}
|
||||
@ -80,8 +88,7 @@ func GetDoc(ctx context.Context, auth *casdoorsdk.User, id string) (*model.Doc,
|
||||
}
|
||||
|
||||
func UpdateDoc(ctx context.Context, auth *casdoorsdk.User, id string, body *schema.UpdateDoc) (*model.Doc, error) {
|
||||
doc, err := GetDoc(ctx, auth, id)
|
||||
if err != nil {
|
||||
if _, err := GetDoc(ctx, auth, id); err != nil {
|
||||
return nil, fmt.Errorf("failed to get doc: %w", err)
|
||||
}
|
||||
|
||||
@ -94,15 +101,6 @@ func UpdateDoc(ctx context.Context, auth *casdoorsdk.User, id string, body *sche
|
||||
if _, err := GetDocFolder(ctx, auth, *body.FolderID); err != nil {
|
||||
return nil, fmt.Errorf("failed to get doc folder: %w", err)
|
||||
}
|
||||
|
||||
// 将对象从临时地址移动到新地址
|
||||
parts := strings.Split(doc.ObjectName, "/")
|
||||
objname := parts[len(parts)-1]
|
||||
newObjectName := fmt.Sprintf("%s/%s/%s", auth.Owner, time.Now().UTC().Format(time.DateOnly), objname)
|
||||
if err := dal.GetStorage().MoveObject(ctx, doc.ObjectName, newObjectName); err != nil {
|
||||
return nil, fmt.Errorf("failed to move object: %w", err)
|
||||
}
|
||||
updates = append(updates, query.Doc.ObjectName.Value(newObjectName))
|
||||
updates = append(updates, query.Doc.FolderID.Value(*body.FolderID))
|
||||
}
|
||||
|
||||
@ -145,7 +143,7 @@ func UpdateDocBatch(ctx context.Context, auth *casdoorsdk.User, param *schema.Do
|
||||
results := make(schema.DocBatchResults, 0, len(param.IDs))
|
||||
// NOTE: 后面应该需要优化一下
|
||||
for _, docID := range param.IDs {
|
||||
_, err := UpdateDoc(ctx, auth, docID, &schema.UpdateDoc{FolderID: ¶m.FolderID})
|
||||
_, err := UpdateDoc(ctx, auth, docID, &schema.UpdateDoc{FolderID: param.FolderID})
|
||||
if err != nil {
|
||||
results = append(results, schema.DocBatchResult{ID: docID, Success: false, Error: err.Error()})
|
||||
} else {
|
||||
@ -156,7 +154,7 @@ func UpdateDocBatch(ctx context.Context, auth *casdoorsdk.User, param *schema.Do
|
||||
}
|
||||
|
||||
func CreateUploadURL(ctx context.Context, auth *casdoorsdk.User, param *schema.CreateUploadURL) (u *url.URL, objectName string, err error) {
|
||||
tmpObjectName := fmt.Sprintf("/tmp/%s/%s-%s", auth.Owner, param.FileName, xid.New())
|
||||
tmpObjectName := fmt.Sprintf("/tmp/%s/%s-%s", auth.Owner, xid.New(), param.FileName)
|
||||
u, err = dal.GetStorage().PresignedPutObject(ctx, tmpObjectName, time.Hour)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to create presigned url: %w", err)
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"octopus/internal/dal/model"
|
||||
"octopus/internal/dal/query"
|
||||
"octopus/internal/schema"
|
||||
"octopus/pkg/utils"
|
||||
|
||||
"github.com/casdoor/casdoor-go-sdk/casdoorsdk"
|
||||
"gorm.io/gen/field"
|
||||
@ -38,7 +39,7 @@ func GetDocFolderTree(ctx context.Context, auth *casdoorsdk.User, param *schema.
|
||||
}
|
||||
|
||||
// 如果没有的话,就创建一个默认的分组
|
||||
if len(folders) == 0 {
|
||||
if utils.Unptr(param.ParentID) == "" && len(folders) == 0 {
|
||||
folder := model.DocFolder{
|
||||
Base: model.Base{OrgID: auth.Owner},
|
||||
Name: "默认分组",
|
||||
@ -132,9 +133,9 @@ func DeleteDocFolder(ctx context.Context, auth *casdoorsdk.User, id string) erro
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get doc folder: %w", err)
|
||||
}
|
||||
if folder.IsDefault {
|
||||
return fmt.Errorf("cannot delete the default folder")
|
||||
}
|
||||
// if folder.IsDefault {
|
||||
// return fmt.Errorf("cannot delete the default folder")
|
||||
// }
|
||||
subFolders, err := GetDocFolderChildren(ctx, auth, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get doc folder children: %w", err)
|
||||
|
@ -86,16 +86,14 @@ func (m *StorageMinio) DeleteObject(ctx context.Context, objectName string) (err
|
||||
|
||||
// MoveObject implements ObjectStorage
|
||||
func (m *StorageMinio) DeleteObjects(ctx context.Context, objectNames []string) []minio.RemoveObjectError {
|
||||
objs := make(chan minio.ObjectInfo, len(objectNames))
|
||||
for _, objectName := range objectNames {
|
||||
objs <- minio.ObjectInfo{Key: objectName}
|
||||
}
|
||||
errCh := m.client.RemoveObjects(ctx, m.bucket, objs, minio.RemoveObjectsOptions{})
|
||||
|
||||
// FIXME: why does DeleteObjects not working??
|
||||
var errors []minio.RemoveObjectError
|
||||
for err := range errCh {
|
||||
if err.Err != nil {
|
||||
errors = append(errors, err)
|
||||
for _, objectName := range objectNames {
|
||||
if err := m.DeleteObject(ctx, objectName); err != nil {
|
||||
errors = append(errors, minio.RemoveObjectError{
|
||||
ObjectName: objectName,
|
||||
Err: err,
|
||||
})
|
||||
}
|
||||
}
|
||||
return errors
|
||||
|
Loading…
x
Reference in New Issue
Block a user