feat(api): doc folders curd implemention
This commit is contained in:
parent
28edda5c7a
commit
6851fe95a0
@ -3,9 +3,11 @@ package main
|
||||
import (
|
||||
"octopus/cmd/octopus/scripts"
|
||||
"octopus/cmd/octopus/server"
|
||||
"octopus/internal/config"
|
||||
"octopus/internal/dal"
|
||||
"octopus/internal/dal/query"
|
||||
|
||||
"github.com/casdoor/casdoor-go-sdk/casdoorsdk"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@ -19,8 +21,17 @@ var rootCmd = &cobra.Command{
|
||||
func main() {
|
||||
rootCmd.AddCommand(server.CmdRun)
|
||||
rootCmd.AddCommand(scripts.CmdScripts)
|
||||
query.SetDefault(dal.GetPostgres())
|
||||
db := dal.GetPostgres()
|
||||
|
||||
// migrations.Migrate(db)
|
||||
|
||||
query.SetDefault(db)
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
log.Fatal().Err(err).Msg("Failed to execute root command")
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
c := config.Get().Casdoor
|
||||
casdoorsdk.InitConfig(c.Endpoint, c.ClientID, c.ClientSecret, c.Certificate, c.OrganizationName, c.AppName)
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"octopus"
|
||||
@ -30,8 +29,10 @@ var innerURLs = tools.NewSet(
|
||||
|
||||
var ROUTES = []func(app *soda.Soda){
|
||||
RegisterBase,
|
||||
router.RegisterAuthRouter,
|
||||
router.RegisterDebuggerRouter,
|
||||
router.RegisterDocRouter,
|
||||
router.RegisterDocFolderRouters,
|
||||
}
|
||||
|
||||
// startHttpServer starts configures and starts an HTTP server on the given URL.
|
||||
@ -52,7 +53,7 @@ func InitApp() *soda.Soda {
|
||||
if err == nil {
|
||||
return c.Next()
|
||||
}
|
||||
status := http.StatusInternalServerError //default error status
|
||||
status := 400 //default error status
|
||||
if e, ok := err.(*fiber.Error); ok { // it's a custom error, so use the status in the error
|
||||
status = e.Code
|
||||
}
|
||||
|
@ -1,7 +1,44 @@
|
||||
debug: true
|
||||
http_port: 8080
|
||||
prometheus_port: 18090
|
||||
host: http://localhost:8080
|
||||
databases:
|
||||
oss: minio://admin:uh8Cz62LKFDe9tBHg2PVyDVX7XKqE584xP@10.16.129.140:31141/pangu2-demo
|
||||
storage: minio://admin:uh8Cz62LKFDe9tBHg2PVyDVX7XKqE584xP@10.16.129.140:31141/pangu2-demo
|
||||
postgresql: postgres://postgres:TvpHKxDhXzMsVkXWdzwiwUw9KmaLuGdRJo@10.16.129.140:31258/octopus
|
||||
qdrant: 10.16.129.104:32352
|
||||
casdoor:
|
||||
app_name: octopus
|
||||
endpoint: "http://localhost:8000"
|
||||
client_id: "ef8e250b7eb21bca7fdc"
|
||||
client_secret: "eb4ef3828b94a2de425d56db1245c97b8273a4aa"
|
||||
certificate: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIE3TCCAsWgAwIBAgIDAeJAMA0GCSqGSIb3DQEBCwUAMCgxDjAMBgNVBAoTBWFk
|
||||
bWluMRYwFAYDVQQDEw1jZXJ0LWJ1aWx0LWluMB4XDTIzMDMyMzExMzQ1MloXDTQz
|
||||
MDMyMzExMzQ1MlowKDEOMAwGA1UEChMFYWRtaW4xFjAUBgNVBAMTDWNlcnQtYnVp
|
||||
bHQtaW4wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCsB5WEdp6v6pn+
|
||||
Ny0RrGMSsdZlU0pCjxxuYU4kWq2RuF2nBMWWFN9OHuc6NBscmau6UZzeEmYSI202
|
||||
b3FKHl1mENF8bcyYDkH4ShvSzlhJsPoMtEVhOFfQnn8wyLRR/HfIrYgQFvQ+2P0f
|
||||
1IZ+jzIQL/axmmzkh0rbZ5aait0l115yGyo0zXWSdzqGpec3OJjg2jAWS2U2YdJ4
|
||||
gvGWodmNCQ8ytwQNJefySTklfZCD3isPF0ZYxVVPH0I+Ni5XQ2G7EL0gffFLqFhf
|
||||
iVoU13w1hCrdIRTeJtCOnHoFNolZzqhhXF6/t0o9Fm0f23F4lmoAVtGMZFVY7ghE
|
||||
sHIpTUf4qnf7J5J3GX9BZ8uhrUYqoTvzydk9yEqPIYgg8801nQKs4NnEBqSpktRP
|
||||
TzXt+VZwMHquONg2Y59CIqTlw4Uek7IPpPSlI0eR1y7UB38LqH1r03dA3XLnE/mv
|
||||
FWPiBPcvqaBNDF4TqkSwJv4zDKtTiziSPvdD2w42cPav+IxiOTqQyXWkqNGiIvK0
|
||||
WTbWjdWWkqxrJWjfIGZYOC4v/DowjUz9dtKr9m/MKQZhqoxmVzcr/VWJYyh6EPzu
|
||||
gBtty2QgYRsS6C5vnzY0BPYqOhok9eDYdI7aNO3BOA9CDSa2EDx6RBLDGO3+6XeX
|
||||
XWwxqMqy1dY5x19DyEn1avfSWARGdwIDAQABoxAwDjAMBgNVHRMBAf8EAjAAMA0G
|
||||
CSqGSIb3DQEBCwUAA4ICAQCb1yqlbzXawXd/EGiFvSCKK1I7NzsGx/ZwuIc/Xs0R
|
||||
z1UwJLqdS9DmzRtUGKQNB7PYbThXvt46FLaPaaL1r2fzVvTw4Es4Mym7v12bJEs9
|
||||
jsTRoHrSxFTuBHO5QWYj7wW8et9LzUX+VIE02OvSeD6SGxGJFJKfSyjaEnmvJX5f
|
||||
xYIyI97zXfdFQwIljQu6fhm/jzVF52ysTrBytqOUQ86vxObh27qdkXIXdfetdo4M
|
||||
X/tIbw5YJCULltUP4BQK2bmrMhy2YpRyijLfa/sC+fuX7s5BABcARkAwSpls3QkQ
|
||||
6d1On3jkFdYdN8wRqRVQoeA+LKZ8nrS3znh6/MVvIze4/f8GWIzFRrjPoPrwLdSZ
|
||||
TtEQmM7I36+79edxD1nVdyZOyp4zleCZfAFdr6twN7PNAjg/qonmxh9kaWD67sfs
|
||||
ZehotzDeB5aSSeEfwzswJigqt6a4WYVQoV+IaFTsEJWnS/WiLUIHWbXuBueG192B
|
||||
x+BNtMEvQuG/Y13ukoSpua7vBcO84ZCfuJgVffIHJcRtnaywTF+mBZJLw/BfjWQ/
|
||||
/ezEwJRJo0CX4TzB9UhMk9RKjhQAhyWJ9joZGIsVj1T+ldx4cA5IafiUWrOKwbBJ
|
||||
NKmrQ+oT7U7JhZ6CJZ7qG0sCWb9moj2BrFkZRFPphSbJESefo29sMWe2dfe1Ii9s
|
||||
fA==
|
||||
-----END CERTIFICATE-----
|
||||
organization_name: pangu2
|
||||
|
@ -13,20 +13,30 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type casdoorConfig struct {
|
||||
Endpoint string `validate:"required" mapstructure:"endpoint"`
|
||||
ClientID string `validate:"required" mapstructure:"client_id"`
|
||||
ClientSecret string `validate:"required" mapstructure:"client_secret"`
|
||||
Certificate string `validate:"required" mapstructure:"certificate"`
|
||||
OrganizationName string `validate:"required" mapstructure:"organization_name"`
|
||||
AppName string `validate:"required" mapstructure:"app_name"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
IsLocal bool
|
||||
Debug bool `mapstructure:"debug"`
|
||||
HTTPPort int `mapstructure:"http_port" validate:"required"`
|
||||
|
||||
Host string `mapstructure:"host" validate:"required"`
|
||||
Casdoor casdoorConfig `mapstructure:"casdoor"`
|
||||
Databases struct {
|
||||
OSS string `mapstructure:"oss" validate:"required"`
|
||||
Storage string `mapstructure:"storage" validate:"required"`
|
||||
PostgreSQL string `mapstructure:"postgresql" validate:"required"`
|
||||
Qdrant string `mapstructure:"qdrant" validate:"required"`
|
||||
} `mapstructure:"databases"`
|
||||
Sentry struct {
|
||||
Environment string
|
||||
DSN string
|
||||
}
|
||||
Environment string `mapstructure:"environment"`
|
||||
DSN string `mapstructure:"dsn"`
|
||||
} `mapstructure:"sentry"`
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -3,8 +3,8 @@ package model
|
||||
import "time"
|
||||
|
||||
type Base struct {
|
||||
OrgID string `gorm:"column:org_id;type:varchar;primaryKey;comment:组织ID"` // 组织ID
|
||||
ID string `gorm:"column:id;type:varchar;primaryKey;comment:ID"` // ID
|
||||
OrgID string `gorm:"column:org_id;type:varchar;primaryKey;comment:组织ID"`
|
||||
ID string `gorm:"column:id;type:varchar;primaryKey;comment:ID"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;comment:创建时间"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;comment:更新时间"`
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"net/url"
|
||||
"octopus/internal/dal"
|
||||
"octopus/internal/schema"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/rs/xid"
|
||||
@ -17,21 +18,26 @@ const TableNameDoc = "docs"
|
||||
type DocFolder struct {
|
||||
Base
|
||||
Name string `gorm:"column:name;type:varchar;not null"` // 文件夹名称
|
||||
IsDeletable bool `gorm:"column:is_deletable;type:boolean;not null;default:true"` // 是否允许被删除
|
||||
IsEditable bool `gorm:"column:is_editable;type:boolean;not null;default:true"` // 是否允许被修改
|
||||
Path string `gorm:"column:path;type:varchar;index:idx_path"` // 路径索引
|
||||
IsDefault bool `gorm:"column:is_default;type:boolean;not null;default:false"` // 是否默认分组
|
||||
ParentPath string `gorm:"column:parent_path;type:varchar;index:idx_path"` // 路径索引
|
||||
CreatedBy string `gorm:"column:created_by;type:varchar;not null"` // 创建人
|
||||
}
|
||||
|
||||
func (*DocFolder) TableName() string {
|
||||
return TableNameDocFolder
|
||||
}
|
||||
func (f *DocFolder) ParentID() string {
|
||||
if f.ParentPath == "" {
|
||||
return ""
|
||||
}
|
||||
parts := strings.Split(f.ParentPath, "/")
|
||||
return parts[len(parts)-1]
|
||||
}
|
||||
|
||||
func (df *DocFolder) ToSchema() *schema.DocFolder {
|
||||
return &schema.DocFolder{
|
||||
ID: df.ID,
|
||||
IsDeletable: df.IsDeletable,
|
||||
IsEditable: df.IsEditable,
|
||||
Name: df.Name,
|
||||
CreatedAt: df.CreatedAt,
|
||||
UpdatedAt: df.UpdatedAt,
|
||||
}
|
||||
@ -42,6 +48,28 @@ func (d *DocFolder) BeforeCreate(*gorm.DB) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 在返回树结构的时候有用
|
||||
type DocFolderWithChildren struct {
|
||||
*DocFolder
|
||||
Children []*DocFolderWithChildren // 子文件夹
|
||||
}
|
||||
|
||||
func (df *DocFolderWithChildren) ToTreeSchema() *schema.DocFolderWithChildren {
|
||||
var traverse func(folder *DocFolderWithChildren) *schema.DocFolderWithChildren
|
||||
|
||||
traverse = func(folder *DocFolderWithChildren) *schema.DocFolderWithChildren {
|
||||
children := make([]*schema.DocFolderWithChildren, 0, len(folder.Children))
|
||||
for _, child := range folder.Children {
|
||||
children = append(children, traverse(child))
|
||||
}
|
||||
return &schema.DocFolderWithChildren{
|
||||
DocFolder: folder.ToSchema(),
|
||||
Children: children,
|
||||
}
|
||||
}
|
||||
return traverse(df)
|
||||
}
|
||||
|
||||
type Doc struct {
|
||||
Base
|
||||
Name string `gorm:"column:name;type:varchar;not null"` // 文件夹名称
|
||||
@ -49,7 +77,7 @@ type Doc struct {
|
||||
IsEditable bool `gorm:"column:is_editable;type:boolean;not null;default:true"` // 是否允许编辑名称
|
||||
FolderID string `gorm:"column:folder_id;type:varchar;index:idx_folder_id"` // 文件夹ID
|
||||
ObjectName string `gorm:"column:object_name;type:varchar"` // 对象存储中对应的object_name
|
||||
UploadedAt time.Time `gorm:"column:uploaded_at;type:datetime"` // 上传时间
|
||||
UploadedAt time.Time `gorm:"column:uploaded_at"` // 上传时间
|
||||
CreatedBy string `gorm:"column:created_by;type:varchar;not null"` // 创建人
|
||||
|
||||
Folder *DocFolder `gorm:"foreignKey:FolderID;references:ID"`
|
||||
@ -62,8 +90,6 @@ func (d *Doc) ToSchema(ctx context.Context) *schema.Doc {
|
||||
ID: d.ID,
|
||||
Folder: d.Folder.ToSchema(),
|
||||
PresignedURL: url,
|
||||
IsDeletable: d.IsDeletable,
|
||||
IsEditable: d.IsEditable,
|
||||
UploadedAt: d.UploadedAt,
|
||||
CreatedAt: d.CreatedAt,
|
||||
UpdatedAt: d.UpdatedAt,
|
||||
@ -77,6 +103,7 @@ func (d *Doc) PresignedURL(ctx context.Context) (*url.URL, error) {
|
||||
func (*Doc) TableName() string {
|
||||
return TableNameDoc
|
||||
}
|
||||
|
||||
func (d *Doc) BeforeCreate(*gorm.DB) error {
|
||||
d.ID = xid.New().String()
|
||||
return nil
|
||||
|
@ -31,9 +31,8 @@ func newDocFolder(db *gorm.DB, opts ...gen.DOOption) docFolder {
|
||||
_docFolder.CreatedAt = field.NewTime(tableName, "created_at")
|
||||
_docFolder.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||
_docFolder.Name = field.NewString(tableName, "name")
|
||||
_docFolder.IsDeletable = field.NewBool(tableName, "is_deletable")
|
||||
_docFolder.IsEditable = field.NewBool(tableName, "is_editable")
|
||||
_docFolder.Path = field.NewString(tableName, "path")
|
||||
_docFolder.IsDefault = field.NewBool(tableName, "is_default")
|
||||
_docFolder.ParentPath = field.NewString(tableName, "parent_path")
|
||||
_docFolder.CreatedBy = field.NewString(tableName, "created_by")
|
||||
|
||||
_docFolder.fillFieldMap()
|
||||
@ -50,9 +49,8 @@ type docFolder struct {
|
||||
CreatedAt field.Time
|
||||
UpdatedAt field.Time
|
||||
Name field.String
|
||||
IsDeletable field.Bool
|
||||
IsEditable field.Bool
|
||||
Path field.String
|
||||
IsDefault field.Bool
|
||||
ParentPath field.String
|
||||
CreatedBy field.String
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
@ -75,9 +73,8 @@ func (d *docFolder) updateTableName(table string) *docFolder {
|
||||
d.CreatedAt = field.NewTime(table, "created_at")
|
||||
d.UpdatedAt = field.NewTime(table, "updated_at")
|
||||
d.Name = field.NewString(table, "name")
|
||||
d.IsDeletable = field.NewBool(table, "is_deletable")
|
||||
d.IsEditable = field.NewBool(table, "is_editable")
|
||||
d.Path = field.NewString(table, "path")
|
||||
d.IsDefault = field.NewBool(table, "is_default")
|
||||
d.ParentPath = field.NewString(table, "parent_path")
|
||||
d.CreatedBy = field.NewString(table, "created_by")
|
||||
|
||||
d.fillFieldMap()
|
||||
@ -95,15 +92,14 @@ func (d *docFolder) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
||||
}
|
||||
|
||||
func (d *docFolder) fillFieldMap() {
|
||||
d.fieldMap = make(map[string]field.Expr, 9)
|
||||
d.fieldMap = make(map[string]field.Expr, 8)
|
||||
d.fieldMap["org_id"] = d.OrgID
|
||||
d.fieldMap["id"] = d.ID
|
||||
d.fieldMap["created_at"] = d.CreatedAt
|
||||
d.fieldMap["updated_at"] = d.UpdatedAt
|
||||
d.fieldMap["name"] = d.Name
|
||||
d.fieldMap["is_deletable"] = d.IsDeletable
|
||||
d.fieldMap["is_editable"] = d.IsEditable
|
||||
d.fieldMap["path"] = d.Path
|
||||
d.fieldMap["is_default"] = d.IsDefault
|
||||
d.fieldMap["parent_path"] = d.ParentPath
|
||||
d.fieldMap["created_by"] = d.CreatedBy
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ func GetStorage() *storage.StorageMinio {
|
||||
func initMinio() {
|
||||
log.Info().Msg("loading minio configs")
|
||||
|
||||
s, err := storage.NewObjectStorage(config.Get().Databases.OSS)
|
||||
s, err := storage.NewObjectStorage(config.Get().Databases.Storage)
|
||||
if err != nil {
|
||||
log.Fatal().Msgf("storage client init failed: %v", err)
|
||||
}
|
||||
|
61
internal/router/auth.go
Normal file
61
internal/router/auth.go
Normal file
@ -0,0 +1,61 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/casdoor/casdoor-go-sdk/casdoorsdk"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/neo-f/soda"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func RegisterAuthRouter(app *soda.Soda) {
|
||||
app.Get("/auth/sign-in", GetSignInURL).
|
||||
AddTags("Auth").
|
||||
SetSummary("登录").
|
||||
SetParameters(OauthSchema{}).
|
||||
OK()
|
||||
|
||||
app.Get("/auth/callback", TokenCallback).
|
||||
AddTags("Auth").
|
||||
SetSummary("Oauth回调地址").
|
||||
SetParameters(OauthSchema{}).
|
||||
OK()
|
||||
}
|
||||
|
||||
type OauthSchema struct {
|
||||
Code string `query:"code"`
|
||||
State string `query:"state"`
|
||||
}
|
||||
|
||||
func TokenCallback(c *fiber.Ctx) error {
|
||||
k := c.Locals(soda.KeyParameter).(*OauthSchema)
|
||||
token, err := casdoorsdk.GetOAuthToken(k.Code, k.State)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(token)
|
||||
}
|
||||
|
||||
func GetSignInURL(c *fiber.Ctx) error {
|
||||
url := casdoorsdk.GetSigninUrl("http://localhost:8080/auth/callback")
|
||||
return c.Redirect(url)
|
||||
}
|
||||
|
||||
var userKey = struct{}{}
|
||||
|
||||
func JWTRequired(c *fiber.Ctx) error {
|
||||
jwt := c.Get("Authorization")
|
||||
if jwt == "" {
|
||||
return fiber.ErrUnauthorized
|
||||
}
|
||||
claims, err := casdoorsdk.ParseJwtToken(jwt[7:])
|
||||
if err != nil {
|
||||
log.Ctx(c.UserContext()).Error().Err(err).Msg("Unauthorized user")
|
||||
return fiber.ErrUnauthorized
|
||||
}
|
||||
c.Locals(userKey, &claims.User)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAuth(c *fiber.Ctx) *casdoorsdk.User {
|
||||
return c.Locals(userKey).(*casdoorsdk.User)
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/casdoor/casdoor-go-sdk/casdoorsdk"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
var userKey = struct{}{}
|
||||
|
||||
func JWTRequired(c *fiber.Ctx) error {
|
||||
jwt := c.Get("Authorization")
|
||||
if jwt == "" {
|
||||
return fiber.ErrUnauthorized
|
||||
}
|
||||
claims, err := casdoorsdk.ParseJwtToken(jwt)
|
||||
if err != nil {
|
||||
log.Ctx(c.UserContext()).Error().Err(err).Msg("Unauthorized user")
|
||||
return fiber.ErrUnauthorized
|
||||
}
|
||||
c.Locals(userKey, claims.User)
|
||||
return nil
|
||||
}
|
@ -4,84 +4,50 @@ import (
|
||||
"octopus/internal/schema"
|
||||
"octopus/internal/service"
|
||||
|
||||
"github.com/casdoor/casdoor-go-sdk/casdoorsdk"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/neo-f/soda"
|
||||
)
|
||||
|
||||
func RegisterDocRouter(app *soda.Soda) {
|
||||
registerDocs(app)
|
||||
registerDocFolders(app)
|
||||
}
|
||||
|
||||
func registerDocFolders(app *soda.Soda) {
|
||||
app.Get("/doc-folders", nil).
|
||||
AddTags("文件夹管理").
|
||||
SetSummary("获取文件夹树").
|
||||
AddJWTSecurity(JWTRequired).
|
||||
SetParameters(schema.GetDocFolderTree{}).
|
||||
AddJSONResponse(200, schema.DocFolderWithChildren{}).OK()
|
||||
app.Post("/doc-folders", nil).
|
||||
AddTags("文件夹管理").
|
||||
SetSummary("新建文件夹").
|
||||
AddJWTSecurity(JWTRequired).
|
||||
SetJSONRequestBody(schema.CreateDocFolder{}).
|
||||
AddJSONResponse(200, schema.DocFolder{}).OK()
|
||||
app.Put("/doc-folders/:id", nil).
|
||||
AddTags("文件夹管理").
|
||||
SetSummary("更新文件夹").
|
||||
AddJWTSecurity(JWTRequired).
|
||||
SetParameters(schema.DocFolderID{}).
|
||||
SetJSONRequestBody(schema.UpdateDocFolder{}).
|
||||
AddJSONResponse(200, schema.DocFolder{}).OK()
|
||||
app.Delete("/doc-folders/:id", nil).
|
||||
AddTags("文件夹管理").
|
||||
SetSummary("删除文件夹").
|
||||
AddJWTSecurity(JWTRequired).
|
||||
SetParameters(schema.DocFolderID{}).
|
||||
AddJSONResponse(200, nil).OK()
|
||||
}
|
||||
|
||||
func registerDocs(app *soda.Soda) {
|
||||
app.Get("/docs", nil).
|
||||
app.Get("/docs", ListDocs).
|
||||
AddTags("文档管理").
|
||||
SetSummary("获取文档列表").
|
||||
AddJWTSecurity(JWTRequired).
|
||||
SetParameters(schema.ListDocQuery{}).
|
||||
AddJSONResponse(200, schema.DocList{}).OK()
|
||||
app.Post("/docs", nil).
|
||||
app.Post("/docs", CreateDoc).
|
||||
AddTags("文档管理").
|
||||
SetSummary("新建文档").
|
||||
AddJWTSecurity(JWTRequired).
|
||||
SetJSONRequestBody(schema.CreateDoc{}).
|
||||
AddJSONResponse(200, schema.Doc{}).OK()
|
||||
app.Put("/docs/:id", nil).
|
||||
app.Put("/docs/:id", UpdateDoc).
|
||||
AddTags("文档管理").
|
||||
SetSummary("更新文档").
|
||||
AddJWTSecurity(JWTRequired).
|
||||
SetParameters(schema.DocID{}).
|
||||
SetJSONRequestBody(schema.UpdateDoc{}).
|
||||
AddJSONResponse(200, schema.Doc{}).OK()
|
||||
app.Delete("/docs/:id", nil).
|
||||
app.Delete("/docs/:id", DeleteDoc).
|
||||
AddTags("文档管理").
|
||||
SetSummary("获取文档列表").
|
||||
AddJWTSecurity(JWTRequired).
|
||||
SetParameters(schema.DocID{}).
|
||||
AddJSONResponse(200, nil).OK()
|
||||
app.Post("/docs/batch/delete", nil).
|
||||
app.Post("/docs/batch/delete", DeleteDoc).
|
||||
AddTags("文档管理").
|
||||
SetSummary("批量-文件删除").
|
||||
AddJWTSecurity(JWTRequired).
|
||||
SetJSONRequestBody(schema.DocsBatchDelete{}).
|
||||
AddJSONResponse(200, schema.DocBatchResults{}).OK()
|
||||
app.Post("/docs/batch/update", nil).
|
||||
app.Post("/docs/batch/update", UpdateDoc).
|
||||
AddTags("文档管理").
|
||||
SetSummary("批量-文件更新").
|
||||
AddJWTSecurity(JWTRequired).
|
||||
SetJSONRequestBody(schema.DocsBatchUpdate{}).
|
||||
AddJSONResponse(200, schema.DocBatchResults{}).OK()
|
||||
// get presigned url for tmp file upload
|
||||
app.Get("/docs/upload-url", nil).
|
||||
app.Get("/docs/upload-url", CreateUploadURL).
|
||||
AddTags("文档管理").
|
||||
SetSummary("获取临时上传文件用的预签名URL").
|
||||
AddJWTSecurity(JWTRequired).
|
||||
@ -90,7 +56,7 @@ func registerDocs(app *soda.Soda) {
|
||||
}
|
||||
|
||||
func ListDocs(c *fiber.Ctx) error {
|
||||
auth := c.Locals(userKey).(*casdoorsdk.User)
|
||||
auth := getAuth(c)
|
||||
params := c.Locals(soda.KeyParameter).(*schema.ListDocQuery)
|
||||
docs, total, err := service.ListDocs(c.UserContext(), auth, params)
|
||||
if err != nil {
|
||||
@ -104,7 +70,7 @@ func ListDocs(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
func CreateDoc(c *fiber.Ctx) error {
|
||||
auth := c.Locals(userKey).(*casdoorsdk.User)
|
||||
auth := getAuth(c)
|
||||
body := c.Locals(soda.KeyRequestBody).(*schema.CreateDoc)
|
||||
|
||||
doc, err := service.CreateDoc(c.UserContext(), auth, body)
|
||||
@ -115,7 +81,7 @@ func CreateDoc(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
func UpdateDoc(c *fiber.Ctx) error {
|
||||
auth := c.Locals(userKey).(*casdoorsdk.User)
|
||||
auth := getAuth(c)
|
||||
params := c.Locals(soda.KeyParameter).(*schema.DocID)
|
||||
body := c.Locals(soda.KeyRequestBody).(*schema.UpdateDoc)
|
||||
|
||||
@ -127,7 +93,7 @@ func UpdateDoc(c *fiber.Ctx) error {
|
||||
return c.JSON(doc.ToSchema(ctx))
|
||||
}
|
||||
func DeleteDoc(c *fiber.Ctx) error {
|
||||
auth := c.Locals(userKey).(*casdoorsdk.User)
|
||||
auth := getAuth(c)
|
||||
params := c.Locals(soda.KeyParameter).(*schema.DocID)
|
||||
ctx := c.UserContext()
|
||||
|
||||
@ -138,7 +104,7 @@ func DeleteDoc(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
func DeleteDocBatch(c *fiber.Ctx) error {
|
||||
auth := c.Locals(userKey).(*casdoorsdk.User)
|
||||
auth := getAuth(c)
|
||||
body := c.Locals(soda.KeyRequestBody).(*schema.DocsBatchDelete)
|
||||
ctx := c.UserContext()
|
||||
|
||||
@ -147,7 +113,7 @@ func DeleteDocBatch(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
func UpdateDocBatch(c *fiber.Ctx) error {
|
||||
auth := c.Locals(userKey).(*casdoorsdk.User)
|
||||
auth := getAuth(c)
|
||||
body := c.Locals(soda.KeyRequestBody).(*schema.DocsBatchUpdate)
|
||||
ctx := c.UserContext()
|
||||
|
||||
@ -156,7 +122,7 @@ func UpdateDocBatch(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
func CreateUploadURL(c *fiber.Ctx) error {
|
||||
auth := c.Locals(userKey).(*casdoorsdk.User)
|
||||
auth := getAuth(c)
|
||||
params := c.Locals(soda.KeyParameter).(*schema.CreateUploadURL)
|
||||
ctx := c.UserContext()
|
||||
|
||||
|
96
internal/router/doc_folder.go
Normal file
96
internal/router/doc_folder.go
Normal file
@ -0,0 +1,96 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"octopus/internal/schema"
|
||||
"octopus/internal/service"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/neo-f/soda"
|
||||
)
|
||||
|
||||
func RegisterDocFolderRouters(app *soda.Soda) {
|
||||
app.Get("/doc-folders", GetDocFolderTree).
|
||||
AddTags("文件夹管理").
|
||||
SetSummary("获取文件夹树").
|
||||
AddJWTSecurity(JWTRequired).
|
||||
SetParameters(schema.GetDocFolderTree{}).
|
||||
AddJSONResponse(200, schema.DocFolderWithChildren{}).
|
||||
OK()
|
||||
|
||||
app.Post("/doc-folders", CreateDocFolder).
|
||||
AddTags("文件夹管理").
|
||||
SetSummary("新建文件夹").
|
||||
AddJWTSecurity(JWTRequired).
|
||||
SetJSONRequestBody(schema.CreateDocFolder{}).
|
||||
AddJSONResponse(200, schema.DocFolder{}).
|
||||
OK()
|
||||
|
||||
app.Put("/doc-folders/:id", UpdateDocFolder).
|
||||
AddTags("文件夹管理").
|
||||
SetSummary("更新文件夹").
|
||||
AddJWTSecurity(JWTRequired).
|
||||
SetParameters(schema.DocFolderID{}).
|
||||
SetJSONRequestBody(schema.UpdateDocFolder{}).
|
||||
AddJSONResponse(200, schema.DocFolder{}).
|
||||
OK()
|
||||
|
||||
app.Delete("/doc-folders/:id", DeleteDocFolder).
|
||||
AddTags("文件夹管理").
|
||||
SetSummary("删除文件夹").
|
||||
AddJWTSecurity(JWTRequired).
|
||||
SetParameters(schema.DocFolderID{}).
|
||||
AddJSONResponse(200, nil).
|
||||
OK()
|
||||
}
|
||||
|
||||
func GetDocFolderTree(c *fiber.Ctx) error {
|
||||
auth := getAuth(c)
|
||||
param := c.Locals(soda.KeyParameter).(*schema.GetDocFolderTree)
|
||||
ctx := c.UserContext()
|
||||
|
||||
tree, err := service.GetDocFolderTree(ctx, auth, param)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
schemas := make([]*schema.DocFolderWithChildren, len(tree))
|
||||
for i, t := range tree {
|
||||
schemas[i] = t.ToTreeSchema()
|
||||
}
|
||||
return c.JSON(schemas)
|
||||
}
|
||||
|
||||
func CreateDocFolder(c *fiber.Ctx) error {
|
||||
auth := getAuth(c)
|
||||
body := c.Locals(soda.KeyRequestBody).(*schema.CreateDocFolder)
|
||||
ctx := c.UserContext()
|
||||
|
||||
doc, err := service.CreateDocFolder(ctx, auth, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(doc.ToSchema())
|
||||
}
|
||||
|
||||
func UpdateDocFolder(c *fiber.Ctx) error {
|
||||
auth := getAuth(c)
|
||||
param := c.Locals(soda.KeyParameter).(*schema.DocFolderID)
|
||||
body := c.Locals(soda.KeyRequestBody).(*schema.UpdateDocFolder)
|
||||
ctx := c.UserContext()
|
||||
|
||||
doc, err := service.UpdateDocFolder(ctx, auth, param.ID, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(doc.ToSchema())
|
||||
}
|
||||
|
||||
func DeleteDocFolder(c *fiber.Ctx) error {
|
||||
auth := getAuth(c)
|
||||
param := c.Locals(soda.KeyParameter).(*schema.DocFolderID)
|
||||
ctx := c.UserContext()
|
||||
|
||||
if err := service.DeleteDocFolder(ctx, auth, param.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(nil)
|
||||
}
|
@ -7,8 +7,7 @@ import (
|
||||
|
||||
type DocFolder struct {
|
||||
ID string `json:"id" oai:"description=文件夹ID"`
|
||||
IsDeletable bool `json:"is_deletable" oai:"description=文件夹是否允许被删除"`
|
||||
IsEditable bool `json:"is_editable" oai:"description=文件夹是否允许被修改"`
|
||||
Name string `json:"name" oai:"description=文件夹名称"`
|
||||
CreatedAt time.Time `json:"created_at" oai:"description=创建时间"`
|
||||
UpdatedAt time.Time `json:"updated_at" oai:"description=更新时间"`
|
||||
}
|
||||
@ -32,20 +31,14 @@ type GetDocFolderTree struct {
|
||||
}
|
||||
|
||||
type DocFolderWithChildren struct {
|
||||
ID string `json:"id" oai:"description=文件夹ID,**根文件夹无ID**"`
|
||||
IsDeletable bool `json:"is_deletable" oai:"description=文件夹是否允许被删除"`
|
||||
IsEditable bool `json:"is_editable" oai:"description=文件夹是否允许被修改"`
|
||||
CreatedAt time.Time `json:"created_at" oai:"description=创建时间"`
|
||||
UpdatedAt time.Time `json:"updated_at" oai:"description=更新时间"`
|
||||
Children []*DocFolderWithChildren `json:"children" oai:"description=子文件夹"`
|
||||
*DocFolder
|
||||
Children []*DocFolderWithChildren `json:"children,omitempty" oai:"description=子文件夹;required=false"`
|
||||
}
|
||||
|
||||
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)"`
|
||||
IsDeletable bool `json:"is_deletable" oai:"description=文件夹是否允许被删除"`
|
||||
IsEditable bool `json:"is_editable" oai:"description=文件夹是否允许被修改"`
|
||||
UploadedAt time.Time `json:"uploaded_at" oai:"description=上传时间"`
|
||||
CreatedAt time.Time `json:"created_at" oai:"description=创建时间"`
|
||||
UpdatedAt time.Time `json:"updated_at" oai:"description=更新时间"`
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"octopus/internal/dal/model"
|
||||
"octopus/internal/dal/query"
|
||||
"octopus/internal/schema"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/casdoor/casdoor-go-sdk/casdoorsdk"
|
||||
@ -46,7 +47,7 @@ func CreateDoc(ctx context.Context, auth *casdoorsdk.User, param *schema.CreateD
|
||||
}
|
||||
|
||||
// 将对象从临时地址移动到新地址
|
||||
newPath := fmt.Sprintf("%s/%s/%s", auth.Owner, folder.Path, param.Name)
|
||||
newPath := fmt.Sprintf("%s/%s/%s", auth.Owner, folder.ParentPath, param.Name)
|
||||
if err := storage.MoveObject(ctx, param.ObjectName, newPath); err != nil {
|
||||
return nil, fmt.Errorf("failed to move object: %w", err)
|
||||
}
|
||||
@ -90,17 +91,17 @@ func UpdateDoc(ctx context.Context, auth *casdoorsdk.User, id string, body *sche
|
||||
}
|
||||
|
||||
if body.FolderID != nil {
|
||||
folder, err := GetDocFolder(ctx, auth, *body.FolderID)
|
||||
if err != nil {
|
||||
if _, err := GetDocFolder(ctx, auth, *body.FolderID); err != nil {
|
||||
return nil, fmt.Errorf("failed to get doc folder: %w", err)
|
||||
}
|
||||
|
||||
// 将对象从临时地址移动到新地址
|
||||
newObjectName := fmt.Sprintf("%s/%s/%s", auth.Owner, folder.Path, doc.Name)
|
||||
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))
|
||||
}
|
||||
@ -162,14 +163,3 @@ func CreateUploadURL(ctx context.Context, auth *casdoorsdk.User, param *schema.C
|
||||
}
|
||||
return u, tmpObjectName, nil
|
||||
}
|
||||
|
||||
func GetDocFolder(ctx context.Context, auth *casdoorsdk.User, id string) (*model.DocFolder, error) {
|
||||
folder, err := query.DocFolder.Where(query.DocFolder.OrgID.Eq(auth.Owner), query.DocFolder.ID.Eq(id)).Take()
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fmt.Errorf("specified doc folder not exists")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return folder, nil
|
||||
}
|
||||
|
203
internal/service/doc_folder.go
Normal file
203
internal/service/doc_folder.go
Normal file
@ -0,0 +1,203 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"octopus/internal/dal"
|
||||
"octopus/internal/dal/model"
|
||||
"octopus/internal/dal/query"
|
||||
"octopus/internal/schema"
|
||||
|
||||
"github.com/casdoor/casdoor-go-sdk/casdoorsdk"
|
||||
"gorm.io/gen/field"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func GetDocFolderTree(ctx context.Context, auth *casdoorsdk.User, param *schema.GetDocFolderTree) ([]*model.DocFolderWithChildren, error) {
|
||||
parentPath := ""
|
||||
parentID := ""
|
||||
if param.ParentID != nil {
|
||||
parent, err := GetDocFolder(ctx, auth, *param.ParentID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get parent folder: %w", err)
|
||||
}
|
||||
parentPath = parent.ParentPath
|
||||
parentID = parent.ID
|
||||
}
|
||||
|
||||
folders, err := query.DocFolder.
|
||||
Where(
|
||||
query.DocFolder.OrgID.Eq(auth.Owner),
|
||||
query.DocFolder.ParentPath.Like(parentPath+"%"),
|
||||
).
|
||||
Order(query.DocFolder.ParentPath).
|
||||
Find()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get doc folders: %w", err)
|
||||
}
|
||||
|
||||
// 如果没有的话,就创建一个默认的分组
|
||||
if len(folders) == 0 {
|
||||
folder := model.DocFolder{
|
||||
Base: model.Base{OrgID: auth.Owner},
|
||||
Name: "默认分组",
|
||||
IsDefault: true,
|
||||
ParentPath: parentPath,
|
||||
CreatedBy: auth.Id,
|
||||
}
|
||||
if err := query.DocFolder.Create(&folder); err != nil {
|
||||
return nil, fmt.Errorf("failed to create doc folder: %w", err)
|
||||
}
|
||||
|
||||
folders = append(folders, &folder)
|
||||
}
|
||||
|
||||
return composeFolders(parentID, folders), nil
|
||||
}
|
||||
|
||||
func GetDocFolder(ctx context.Context, auth *casdoorsdk.User, id string) (*model.DocFolder, error) {
|
||||
folder, err := query.DocFolder.Where(query.DocFolder.OrgID.Eq(auth.Owner), query.DocFolder.ID.Eq(id)).Take()
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fmt.Errorf("specified doc folder not exists")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return folder, nil
|
||||
}
|
||||
|
||||
func GetDocFolderChildren(ctx context.Context, auth *casdoorsdk.User, id string) ([]*model.DocFolder, error) {
|
||||
folder, err := GetDocFolder(ctx, auth, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return query.DocFolder.Where(query.DocFolder.OrgID.Eq(auth.Owner), query.DocFolder.ParentPath.Like(folder.ParentPath+"%")).Find()
|
||||
}
|
||||
|
||||
func CreateDocFolder(ctx context.Context, auth *casdoorsdk.User, param *schema.CreateDocFolder) (*model.DocFolder, error) {
|
||||
path := ""
|
||||
if param.ParentID != "" {
|
||||
parent, err := GetDocFolder(ctx, auth, param.ParentID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get parent folder: %w", err)
|
||||
}
|
||||
path = fmt.Sprintf("%s/%s", parent.ParentPath, parent.ID)
|
||||
}
|
||||
|
||||
folder := model.DocFolder{
|
||||
Base: model.Base{OrgID: auth.Owner},
|
||||
Name: param.Name,
|
||||
ParentPath: path,
|
||||
CreatedBy: auth.Id,
|
||||
}
|
||||
if err := query.DocFolder.Create(&folder); err != nil {
|
||||
return nil, fmt.Errorf("failed to create doc folder: %w", err)
|
||||
}
|
||||
return &folder, nil
|
||||
}
|
||||
|
||||
func UpdateDocFolder(ctx context.Context, auth *casdoorsdk.User, id string, param *schema.UpdateDocFolder) (*model.DocFolder, error) {
|
||||
folder, err := GetDocFolder(ctx, auth, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get doc folder: %w", err)
|
||||
}
|
||||
|
||||
var updates []field.AssignExpr
|
||||
|
||||
if param.ParentID != nil {
|
||||
if folder.IsDefault {
|
||||
return nil, fmt.Errorf("cannot move the default folder")
|
||||
}
|
||||
|
||||
parent, err := GetDocFolder(ctx, auth, *param.ParentID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get parent folder: %w", err)
|
||||
}
|
||||
newParentPath := fmt.Sprintf("%s/%s", parent.ParentPath, parent.ID)
|
||||
updates = append(updates, query.DocFolder.ParentPath.Value(newParentPath))
|
||||
}
|
||||
|
||||
if param.Name != nil {
|
||||
updates = append(updates, query.DocFolder.Name.Value(*param.Name))
|
||||
}
|
||||
if _, err := query.DocFolder.Where(query.DocFolder.ID.Eq(id)).UpdateSimple(updates...); err != nil {
|
||||
return nil, fmt.Errorf("failed to update doc folder: %w", err)
|
||||
}
|
||||
return GetDocFolder(ctx, auth, id)
|
||||
}
|
||||
|
||||
func DeleteDocFolder(ctx context.Context, auth *casdoorsdk.User, id string) error {
|
||||
folder, err := GetDocFolder(ctx, auth, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get doc folder: %w", err)
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
allFolderIDs := make([]string, 0, len(subFolders)+1)
|
||||
allFolderIDs = append(allFolderIDs, folder.ID)
|
||||
for _, subFolder := range subFolders {
|
||||
allFolderIDs = append(allFolderIDs, subFolder.ID)
|
||||
}
|
||||
|
||||
// 需要把文件和对应的对象存储中的文件一并删除
|
||||
docs, err := query.Doc.Where(query.Doc.OrgID.Eq(auth.Owner), query.Doc.FolderID.In(allFolderIDs...)).Find()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get docs: %w", err)
|
||||
}
|
||||
|
||||
objectNames := make([]string, 0, len(docs))
|
||||
docIDs := make([]string, 0, len(docs))
|
||||
for _, doc := range docs {
|
||||
objectNames = append(objectNames, doc.ObjectName)
|
||||
docIDs = append(docIDs, doc.ID)
|
||||
}
|
||||
|
||||
if len(objectNames) > 0 {
|
||||
if errs := dal.GetStorage().DeleteObjects(ctx, objectNames); len(errs) != 0 {
|
||||
err := errors.New("failed to delete objects")
|
||||
for _, e := range errs {
|
||||
err = fmt.Errorf("failed to delete object %s: %w", e.ObjectName, e.Err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(docIDs) > 0 {
|
||||
if _, err := query.Doc.Where(query.Doc.ID.In(docIDs...)).Delete(); err != nil {
|
||||
return fmt.Errorf("failed to delete docs: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := query.DocFolder.Where(query.DocFolder.ID.In(allFolderIDs...)).Delete(); err != nil {
|
||||
return fmt.Errorf("failed to delete doc folder: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// composeFolders() 用于把文件夹列表转换成树形结构
|
||||
func composeFolders(topID string, folders []*model.DocFolder) []*model.DocFolderWithChildren {
|
||||
m := make(map[string]*model.DocFolderWithChildren)
|
||||
for _, folder := range folders {
|
||||
m[folder.ID] = &model.DocFolderWithChildren{DocFolder: folder}
|
||||
}
|
||||
|
||||
results := make([]*model.DocFolderWithChildren, 0)
|
||||
for _, folder := range folders {
|
||||
parentID := folder.ParentID()
|
||||
if parentID == topID {
|
||||
results = append(results, m[folder.ID])
|
||||
continue
|
||||
}
|
||||
if parent, ok := m[parentID]; ok {
|
||||
parent.Children = append(parent.Children, m[folder.ID])
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
@ -2,7 +2,6 @@ package logger
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"octopus"
|
||||
"octopus/internal/config"
|
||||
@ -17,11 +16,11 @@ func Setup() {
|
||||
zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
|
||||
|
||||
writers := []io.Writer{}
|
||||
if config.Get().IsLocal {
|
||||
// if config.Get().IsLocal {
|
||||
writers = append(writers, zerolog.NewConsoleWriter())
|
||||
} else {
|
||||
writers = append(writers, os.Stderr)
|
||||
}
|
||||
// } else {
|
||||
// writers = append(writers, os.Stderr)
|
||||
// }
|
||||
if dsn := cfg.Sentry.DSN; dsn != "" {
|
||||
sentryWriter, err := NewSentryWriter(
|
||||
cfg.Sentry.DSN,
|
||||
|
@ -83,3 +83,20 @@ func (m *StorageMinio) DeleteObject(ctx context.Context, objectName string) (err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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{})
|
||||
|
||||
var errors []minio.RemoveObjectError
|
||||
for err := range errCh {
|
||||
if err.Err != nil {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
}
|
||||
return errors
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user