feat(api): doc folders curd implemention
This commit is contained in:
parent
28edda5c7a
commit
6851fe95a0
@ -3,9 +3,11 @@ package main
|
|||||||
import (
|
import (
|
||||||
"octopus/cmd/octopus/scripts"
|
"octopus/cmd/octopus/scripts"
|
||||||
"octopus/cmd/octopus/server"
|
"octopus/cmd/octopus/server"
|
||||||
|
"octopus/internal/config"
|
||||||
"octopus/internal/dal"
|
"octopus/internal/dal"
|
||||||
"octopus/internal/dal/query"
|
"octopus/internal/dal/query"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor-go-sdk/casdoorsdk"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@ -19,8 +21,17 @@ var rootCmd = &cobra.Command{
|
|||||||
func main() {
|
func main() {
|
||||||
rootCmd.AddCommand(server.CmdRun)
|
rootCmd.AddCommand(server.CmdRun)
|
||||||
rootCmd.AddCommand(scripts.CmdScripts)
|
rootCmd.AddCommand(scripts.CmdScripts)
|
||||||
query.SetDefault(dal.GetPostgres())
|
db := dal.GetPostgres()
|
||||||
|
|
||||||
|
// migrations.Migrate(db)
|
||||||
|
|
||||||
|
query.SetDefault(db)
|
||||||
if err := rootCmd.Execute(); err != nil {
|
if err := rootCmd.Execute(); err != nil {
|
||||||
log.Fatal().Err(err).Msg("Failed to execute root command")
|
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
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"octopus"
|
"octopus"
|
||||||
@ -30,8 +29,10 @@ var innerURLs = tools.NewSet(
|
|||||||
|
|
||||||
var ROUTES = []func(app *soda.Soda){
|
var ROUTES = []func(app *soda.Soda){
|
||||||
RegisterBase,
|
RegisterBase,
|
||||||
|
router.RegisterAuthRouter,
|
||||||
router.RegisterDebuggerRouter,
|
router.RegisterDebuggerRouter,
|
||||||
router.RegisterDocRouter,
|
router.RegisterDocRouter,
|
||||||
|
router.RegisterDocFolderRouters,
|
||||||
}
|
}
|
||||||
|
|
||||||
// startHttpServer starts configures and starts an HTTP server on the given URL.
|
// startHttpServer starts configures and starts an HTTP server on the given URL.
|
||||||
@ -52,8 +53,8 @@ func InitApp() *soda.Soda {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
return c.Next()
|
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
|
if e, ok := err.(*fiber.Error); ok { // it's a custom error, so use the status in the error
|
||||||
status = e.Code
|
status = e.Code
|
||||||
}
|
}
|
||||||
msg := map[string]interface{}{"code": status, "message": err.Error()}
|
msg := map[string]interface{}{"code": status, "message": err.Error()}
|
||||||
|
@ -1,7 +1,44 @@
|
|||||||
debug: true
|
debug: true
|
||||||
http_port: 8080
|
http_port: 8080
|
||||||
prometheus_port: 18090
|
prometheus_port: 18090
|
||||||
|
host: http://localhost:8080
|
||||||
databases:
|
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
|
postgresql: postgres://postgres:TvpHKxDhXzMsVkXWdzwiwUw9KmaLuGdRJo@10.16.129.140:31258/octopus
|
||||||
qdrant: 10.16.129.104:32352
|
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"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type casdoorConfig struct {
|
||||||
IsLocal bool
|
Endpoint string `validate:"required" mapstructure:"endpoint"`
|
||||||
Debug bool `mapstructure:"debug"`
|
ClientID string `validate:"required" mapstructure:"client_id"`
|
||||||
HTTPPort int `mapstructure:"http_port" validate:"required"`
|
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 {
|
Databases struct {
|
||||||
OSS string `mapstructure:"oss" validate:"required"`
|
Storage string `mapstructure:"storage" validate:"required"`
|
||||||
PostgreSQL string `mapstructure:"postgresql" validate:"required"`
|
PostgreSQL string `mapstructure:"postgresql" validate:"required"`
|
||||||
Qdrant string `mapstructure:"qdrant" validate:"required"`
|
Qdrant string `mapstructure:"qdrant" validate:"required"`
|
||||||
} `mapstructure:"databases"`
|
} `mapstructure:"databases"`
|
||||||
Sentry struct {
|
Sentry struct {
|
||||||
Environment string
|
Environment string `mapstructure:"environment"`
|
||||||
DSN string
|
DSN string `mapstructure:"dsn"`
|
||||||
}
|
} `mapstructure:"sentry"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -3,8 +3,8 @@ package model
|
|||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type Base struct {
|
type Base struct {
|
||||||
OrgID string `gorm:"column:org_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"` // ID
|
ID string `gorm:"column:id;type:varchar;primaryKey;comment:ID"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;comment:创建时间"`
|
CreatedAt time.Time `gorm:"column:created_at;comment:创建时间"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;comment:更新时间"`
|
UpdatedAt time.Time `gorm:"column:updated_at;comment:更新时间"`
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"octopus/internal/dal"
|
"octopus/internal/dal"
|
||||||
"octopus/internal/schema"
|
"octopus/internal/schema"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rs/xid"
|
"github.com/rs/xid"
|
||||||
@ -16,24 +17,29 @@ const TableNameDoc = "docs"
|
|||||||
|
|
||||||
type DocFolder struct {
|
type DocFolder struct {
|
||||||
Base
|
Base
|
||||||
Name string `gorm:"column:name;type:varchar;not null"` // 文件夹名称
|
Name string `gorm:"column:name;type:varchar;not null"` // 文件夹名称
|
||||||
IsDeletable bool `gorm:"column:is_deletable;type:boolean;not null;default:true"` // 是否允许被删除
|
IsDefault bool `gorm:"column:is_default;type:boolean;not null;default:false"` // 是否默认分组
|
||||||
IsEditable bool `gorm:"column:is_editable;type:boolean;not null;default:true"` // 是否允许被修改
|
ParentPath string `gorm:"column:parent_path;type:varchar;index:idx_path"` // 路径索引
|
||||||
Path string `gorm:"column:path;type:varchar;index:idx_path"` // 路径索引
|
CreatedBy string `gorm:"column:created_by;type:varchar;not null"` // 创建人
|
||||||
CreatedBy string `gorm:"column:created_by;type:varchar;not null"` // 创建人
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*DocFolder) TableName() string {
|
func (*DocFolder) TableName() string {
|
||||||
return TableNameDocFolder
|
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 {
|
func (df *DocFolder) ToSchema() *schema.DocFolder {
|
||||||
return &schema.DocFolder{
|
return &schema.DocFolder{
|
||||||
ID: df.ID,
|
ID: df.ID,
|
||||||
IsDeletable: df.IsDeletable,
|
Name: df.Name,
|
||||||
IsEditable: df.IsEditable,
|
CreatedAt: df.CreatedAt,
|
||||||
CreatedAt: df.CreatedAt,
|
UpdatedAt: df.UpdatedAt,
|
||||||
UpdatedAt: df.UpdatedAt,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,6 +48,28 @@ func (d *DocFolder) BeforeCreate(*gorm.DB) error {
|
|||||||
return nil
|
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 {
|
type Doc struct {
|
||||||
Base
|
Base
|
||||||
Name string `gorm:"column:name;type:varchar;not null"` // 文件夹名称
|
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"` // 是否允许编辑名称
|
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
|
FolderID string `gorm:"column:folder_id;type:varchar;index:idx_folder_id"` // 文件夹ID
|
||||||
ObjectName string `gorm:"column:object_name;type:varchar"` // 对象存储中对应的object_name
|
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"` // 创建人
|
CreatedBy string `gorm:"column:created_by;type:varchar;not null"` // 创建人
|
||||||
|
|
||||||
Folder *DocFolder `gorm:"foreignKey:FolderID;references:ID"`
|
Folder *DocFolder `gorm:"foreignKey:FolderID;references:ID"`
|
||||||
@ -62,8 +90,6 @@ func (d *Doc) ToSchema(ctx context.Context) *schema.Doc {
|
|||||||
ID: d.ID,
|
ID: d.ID,
|
||||||
Folder: d.Folder.ToSchema(),
|
Folder: d.Folder.ToSchema(),
|
||||||
PresignedURL: url,
|
PresignedURL: url,
|
||||||
IsDeletable: d.IsDeletable,
|
|
||||||
IsEditable: d.IsEditable,
|
|
||||||
UploadedAt: d.UploadedAt,
|
UploadedAt: d.UploadedAt,
|
||||||
CreatedAt: d.CreatedAt,
|
CreatedAt: d.CreatedAt,
|
||||||
UpdatedAt: d.UpdatedAt,
|
UpdatedAt: d.UpdatedAt,
|
||||||
@ -77,6 +103,7 @@ func (d *Doc) PresignedURL(ctx context.Context) (*url.URL, error) {
|
|||||||
func (*Doc) TableName() string {
|
func (*Doc) TableName() string {
|
||||||
return TableNameDoc
|
return TableNameDoc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Doc) BeforeCreate(*gorm.DB) error {
|
func (d *Doc) BeforeCreate(*gorm.DB) error {
|
||||||
d.ID = xid.New().String()
|
d.ID = xid.New().String()
|
||||||
return nil
|
return nil
|
||||||
|
@ -31,9 +31,8 @@ func newDocFolder(db *gorm.DB, opts ...gen.DOOption) docFolder {
|
|||||||
_docFolder.CreatedAt = field.NewTime(tableName, "created_at")
|
_docFolder.CreatedAt = field.NewTime(tableName, "created_at")
|
||||||
_docFolder.UpdatedAt = field.NewTime(tableName, "updated_at")
|
_docFolder.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||||
_docFolder.Name = field.NewString(tableName, "name")
|
_docFolder.Name = field.NewString(tableName, "name")
|
||||||
_docFolder.IsDeletable = field.NewBool(tableName, "is_deletable")
|
_docFolder.IsDefault = field.NewBool(tableName, "is_default")
|
||||||
_docFolder.IsEditable = field.NewBool(tableName, "is_editable")
|
_docFolder.ParentPath = field.NewString(tableName, "parent_path")
|
||||||
_docFolder.Path = field.NewString(tableName, "path")
|
|
||||||
_docFolder.CreatedBy = field.NewString(tableName, "created_by")
|
_docFolder.CreatedBy = field.NewString(tableName, "created_by")
|
||||||
|
|
||||||
_docFolder.fillFieldMap()
|
_docFolder.fillFieldMap()
|
||||||
@ -44,16 +43,15 @@ func newDocFolder(db *gorm.DB, opts ...gen.DOOption) docFolder {
|
|||||||
type docFolder struct {
|
type docFolder struct {
|
||||||
docFolderDo
|
docFolderDo
|
||||||
|
|
||||||
ALL field.Asterisk
|
ALL field.Asterisk
|
||||||
OrgID field.String
|
OrgID field.String
|
||||||
ID field.String
|
ID field.String
|
||||||
CreatedAt field.Time
|
CreatedAt field.Time
|
||||||
UpdatedAt field.Time
|
UpdatedAt field.Time
|
||||||
Name field.String
|
Name field.String
|
||||||
IsDeletable field.Bool
|
IsDefault field.Bool
|
||||||
IsEditable field.Bool
|
ParentPath field.String
|
||||||
Path field.String
|
CreatedBy field.String
|
||||||
CreatedBy field.String
|
|
||||||
|
|
||||||
fieldMap map[string]field.Expr
|
fieldMap map[string]field.Expr
|
||||||
}
|
}
|
||||||
@ -75,9 +73,8 @@ func (d *docFolder) updateTableName(table string) *docFolder {
|
|||||||
d.CreatedAt = field.NewTime(table, "created_at")
|
d.CreatedAt = field.NewTime(table, "created_at")
|
||||||
d.UpdatedAt = field.NewTime(table, "updated_at")
|
d.UpdatedAt = field.NewTime(table, "updated_at")
|
||||||
d.Name = field.NewString(table, "name")
|
d.Name = field.NewString(table, "name")
|
||||||
d.IsDeletable = field.NewBool(table, "is_deletable")
|
d.IsDefault = field.NewBool(table, "is_default")
|
||||||
d.IsEditable = field.NewBool(table, "is_editable")
|
d.ParentPath = field.NewString(table, "parent_path")
|
||||||
d.Path = field.NewString(table, "path")
|
|
||||||
d.CreatedBy = field.NewString(table, "created_by")
|
d.CreatedBy = field.NewString(table, "created_by")
|
||||||
|
|
||||||
d.fillFieldMap()
|
d.fillFieldMap()
|
||||||
@ -95,15 +92,14 @@ func (d *docFolder) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *docFolder) fillFieldMap() {
|
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["org_id"] = d.OrgID
|
||||||
d.fieldMap["id"] = d.ID
|
d.fieldMap["id"] = d.ID
|
||||||
d.fieldMap["created_at"] = d.CreatedAt
|
d.fieldMap["created_at"] = d.CreatedAt
|
||||||
d.fieldMap["updated_at"] = d.UpdatedAt
|
d.fieldMap["updated_at"] = d.UpdatedAt
|
||||||
d.fieldMap["name"] = d.Name
|
d.fieldMap["name"] = d.Name
|
||||||
d.fieldMap["is_deletable"] = d.IsDeletable
|
d.fieldMap["is_default"] = d.IsDefault
|
||||||
d.fieldMap["is_editable"] = d.IsEditable
|
d.fieldMap["parent_path"] = d.ParentPath
|
||||||
d.fieldMap["path"] = d.Path
|
|
||||||
d.fieldMap["created_by"] = d.CreatedBy
|
d.fieldMap["created_by"] = d.CreatedBy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ func GetStorage() *storage.StorageMinio {
|
|||||||
func initMinio() {
|
func initMinio() {
|
||||||
log.Info().Msg("loading minio configs")
|
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 {
|
if err != nil {
|
||||||
log.Fatal().Msgf("storage client init failed: %v", err)
|
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/schema"
|
||||||
"octopus/internal/service"
|
"octopus/internal/service"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor-go-sdk/casdoorsdk"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/neo-f/soda"
|
"github.com/neo-f/soda"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterDocRouter(app *soda.Soda) {
|
func RegisterDocRouter(app *soda.Soda) {
|
||||||
registerDocs(app)
|
app.Get("/docs", ListDocs).
|
||||||
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).
|
|
||||||
AddTags("文档管理").
|
AddTags("文档管理").
|
||||||
SetSummary("获取文档列表").
|
SetSummary("获取文档列表").
|
||||||
AddJWTSecurity(JWTRequired).
|
AddJWTSecurity(JWTRequired).
|
||||||
SetParameters(schema.ListDocQuery{}).
|
SetParameters(schema.ListDocQuery{}).
|
||||||
AddJSONResponse(200, schema.DocList{}).OK()
|
AddJSONResponse(200, schema.DocList{}).OK()
|
||||||
app.Post("/docs", nil).
|
app.Post("/docs", CreateDoc).
|
||||||
AddTags("文档管理").
|
AddTags("文档管理").
|
||||||
SetSummary("新建文档").
|
SetSummary("新建文档").
|
||||||
AddJWTSecurity(JWTRequired).
|
AddJWTSecurity(JWTRequired).
|
||||||
SetJSONRequestBody(schema.CreateDoc{}).
|
SetJSONRequestBody(schema.CreateDoc{}).
|
||||||
AddJSONResponse(200, schema.Doc{}).OK()
|
AddJSONResponse(200, schema.Doc{}).OK()
|
||||||
app.Put("/docs/:id", nil).
|
app.Put("/docs/:id", UpdateDoc).
|
||||||
AddTags("文档管理").
|
AddTags("文档管理").
|
||||||
SetSummary("更新文档").
|
SetSummary("更新文档").
|
||||||
AddJWTSecurity(JWTRequired).
|
AddJWTSecurity(JWTRequired).
|
||||||
SetParameters(schema.DocID{}).
|
SetParameters(schema.DocID{}).
|
||||||
SetJSONRequestBody(schema.UpdateDoc{}).
|
SetJSONRequestBody(schema.UpdateDoc{}).
|
||||||
AddJSONResponse(200, schema.Doc{}).OK()
|
AddJSONResponse(200, schema.Doc{}).OK()
|
||||||
app.Delete("/docs/:id", nil).
|
app.Delete("/docs/:id", DeleteDoc).
|
||||||
AddTags("文档管理").
|
AddTags("文档管理").
|
||||||
SetSummary("获取文档列表").
|
SetSummary("获取文档列表").
|
||||||
AddJWTSecurity(JWTRequired).
|
AddJWTSecurity(JWTRequired).
|
||||||
SetParameters(schema.DocID{}).
|
SetParameters(schema.DocID{}).
|
||||||
AddJSONResponse(200, nil).OK()
|
AddJSONResponse(200, nil).OK()
|
||||||
app.Post("/docs/batch/delete", nil).
|
app.Post("/docs/batch/delete", DeleteDoc).
|
||||||
AddTags("文档管理").
|
AddTags("文档管理").
|
||||||
SetSummary("批量-文件删除").
|
SetSummary("批量-文件删除").
|
||||||
AddJWTSecurity(JWTRequired).
|
AddJWTSecurity(JWTRequired).
|
||||||
SetJSONRequestBody(schema.DocsBatchDelete{}).
|
SetJSONRequestBody(schema.DocsBatchDelete{}).
|
||||||
AddJSONResponse(200, schema.DocBatchResults{}).OK()
|
AddJSONResponse(200, schema.DocBatchResults{}).OK()
|
||||||
app.Post("/docs/batch/update", nil).
|
app.Post("/docs/batch/update", UpdateDoc).
|
||||||
AddTags("文档管理").
|
AddTags("文档管理").
|
||||||
SetSummary("批量-文件更新").
|
SetSummary("批量-文件更新").
|
||||||
AddJWTSecurity(JWTRequired).
|
AddJWTSecurity(JWTRequired).
|
||||||
SetJSONRequestBody(schema.DocsBatchUpdate{}).
|
SetJSONRequestBody(schema.DocsBatchUpdate{}).
|
||||||
AddJSONResponse(200, schema.DocBatchResults{}).OK()
|
AddJSONResponse(200, schema.DocBatchResults{}).OK()
|
||||||
// get presigned url for tmp file upload
|
// get presigned url for tmp file upload
|
||||||
app.Get("/docs/upload-url", nil).
|
app.Get("/docs/upload-url", CreateUploadURL).
|
||||||
AddTags("文档管理").
|
AddTags("文档管理").
|
||||||
SetSummary("获取临时上传文件用的预签名URL").
|
SetSummary("获取临时上传文件用的预签名URL").
|
||||||
AddJWTSecurity(JWTRequired).
|
AddJWTSecurity(JWTRequired).
|
||||||
@ -90,7 +56,7 @@ func registerDocs(app *soda.Soda) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ListDocs(c *fiber.Ctx) error {
|
func ListDocs(c *fiber.Ctx) error {
|
||||||
auth := c.Locals(userKey).(*casdoorsdk.User)
|
auth := getAuth(c)
|
||||||
params := c.Locals(soda.KeyParameter).(*schema.ListDocQuery)
|
params := c.Locals(soda.KeyParameter).(*schema.ListDocQuery)
|
||||||
docs, total, err := service.ListDocs(c.UserContext(), auth, params)
|
docs, total, err := service.ListDocs(c.UserContext(), auth, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -104,7 +70,7 @@ func ListDocs(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CreateDoc(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)
|
body := c.Locals(soda.KeyRequestBody).(*schema.CreateDoc)
|
||||||
|
|
||||||
doc, err := service.CreateDoc(c.UserContext(), auth, body)
|
doc, err := service.CreateDoc(c.UserContext(), auth, body)
|
||||||
@ -115,7 +81,7 @@ func CreateDoc(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func UpdateDoc(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)
|
params := c.Locals(soda.KeyParameter).(*schema.DocID)
|
||||||
body := c.Locals(soda.KeyRequestBody).(*schema.UpdateDoc)
|
body := c.Locals(soda.KeyRequestBody).(*schema.UpdateDoc)
|
||||||
|
|
||||||
@ -127,7 +93,7 @@ func UpdateDoc(c *fiber.Ctx) error {
|
|||||||
return c.JSON(doc.ToSchema(ctx))
|
return c.JSON(doc.ToSchema(ctx))
|
||||||
}
|
}
|
||||||
func DeleteDoc(c *fiber.Ctx) error {
|
func DeleteDoc(c *fiber.Ctx) error {
|
||||||
auth := c.Locals(userKey).(*casdoorsdk.User)
|
auth := getAuth(c)
|
||||||
params := c.Locals(soda.KeyParameter).(*schema.DocID)
|
params := c.Locals(soda.KeyParameter).(*schema.DocID)
|
||||||
ctx := c.UserContext()
|
ctx := c.UserContext()
|
||||||
|
|
||||||
@ -138,7 +104,7 @@ func DeleteDoc(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DeleteDocBatch(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)
|
body := c.Locals(soda.KeyRequestBody).(*schema.DocsBatchDelete)
|
||||||
ctx := c.UserContext()
|
ctx := c.UserContext()
|
||||||
|
|
||||||
@ -147,7 +113,7 @@ func DeleteDocBatch(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func UpdateDocBatch(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)
|
body := c.Locals(soda.KeyRequestBody).(*schema.DocsBatchUpdate)
|
||||||
ctx := c.UserContext()
|
ctx := c.UserContext()
|
||||||
|
|
||||||
@ -156,7 +122,7 @@ func UpdateDocBatch(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CreateUploadURL(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)
|
params := c.Locals(soda.KeyParameter).(*schema.CreateUploadURL)
|
||||||
ctx := c.UserContext()
|
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)
|
||||||
|
}
|
@ -6,11 +6,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type DocFolder struct {
|
type DocFolder struct {
|
||||||
ID string `json:"id" oai:"description=文件夹ID"`
|
ID string `json:"id" oai:"description=文件夹ID"`
|
||||||
IsDeletable bool `json:"is_deletable" oai:"description=文件夹是否允许被删除"`
|
Name string `json:"name" oai:"description=文件夹名称"`
|
||||||
IsEditable bool `json:"is_editable" oai:"description=文件夹是否允许被修改"`
|
CreatedAt time.Time `json:"created_at" oai:"description=创建时间"`
|
||||||
CreatedAt time.Time `json:"created_at" oai:"description=创建时间"`
|
UpdatedAt time.Time `json:"updated_at" oai:"description=更新时间"`
|
||||||
UpdatedAt time.Time `json:"updated_at" oai:"description=更新时间"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type DocFolderID struct {
|
type DocFolderID struct {
|
||||||
@ -32,20 +31,14 @@ type GetDocFolderTree struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DocFolderWithChildren struct {
|
type DocFolderWithChildren struct {
|
||||||
ID string `json:"id" oai:"description=文件夹ID,**根文件夹无ID**"`
|
*DocFolder
|
||||||
IsDeletable bool `json:"is_deletable" oai:"description=文件夹是否允许被删除"`
|
Children []*DocFolderWithChildren `json:"children,omitempty" oai:"description=子文件夹;required=false"`
|
||||||
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=子文件夹"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Doc struct {
|
type Doc struct {
|
||||||
ID string `json:"id" oai:"description=文档ID"`
|
ID string `json:"id" oai:"description=文档ID"`
|
||||||
Folder *DocFolder `json:"folder" oai:"description=归属文件夹信息"`
|
Folder *DocFolder `json:"folder" oai:"description=归属文件夹信息"`
|
||||||
PresignedURL *url.URL `json:"presigned_url" oai:"description=文档预签名下载URL(临时下载URL)"`
|
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=上传时间"`
|
UploadedAt time.Time `json:"uploaded_at" oai:"description=上传时间"`
|
||||||
CreatedAt time.Time `json:"created_at" oai:"description=创建时间"`
|
CreatedAt time.Time `json:"created_at" oai:"description=创建时间"`
|
||||||
UpdatedAt time.Time `json:"updated_at" oai:"description=更新时间"`
|
UpdatedAt time.Time `json:"updated_at" oai:"description=更新时间"`
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"octopus/internal/dal/model"
|
"octopus/internal/dal/model"
|
||||||
"octopus/internal/dal/query"
|
"octopus/internal/dal/query"
|
||||||
"octopus/internal/schema"
|
"octopus/internal/schema"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor-go-sdk/casdoorsdk"
|
"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 {
|
if err := storage.MoveObject(ctx, param.ObjectName, newPath); err != nil {
|
||||||
return nil, fmt.Errorf("failed to move object: %w", err)
|
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 {
|
if body.FolderID != nil {
|
||||||
folder, err := GetDocFolder(ctx, auth, *body.FolderID)
|
if _, err := GetDocFolder(ctx, auth, *body.FolderID); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get doc folder: %w", err)
|
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 {
|
if err := dal.GetStorage().MoveObject(ctx, doc.ObjectName, newObjectName); err != nil {
|
||||||
return nil, fmt.Errorf("failed to move object: %w", err)
|
return nil, fmt.Errorf("failed to move object: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
updates = append(updates, query.Doc.ObjectName.Value(newObjectName))
|
updates = append(updates, query.Doc.ObjectName.Value(newObjectName))
|
||||||
updates = append(updates, query.Doc.FolderID.Value(*body.FolderID))
|
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
|
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 (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
|
||||||
|
|
||||||
"octopus"
|
"octopus"
|
||||||
"octopus/internal/config"
|
"octopus/internal/config"
|
||||||
@ -17,11 +16,11 @@ func Setup() {
|
|||||||
zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
|
zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
|
||||||
|
|
||||||
writers := []io.Writer{}
|
writers := []io.Writer{}
|
||||||
if config.Get().IsLocal {
|
// if config.Get().IsLocal {
|
||||||
writers = append(writers, zerolog.NewConsoleWriter())
|
writers = append(writers, zerolog.NewConsoleWriter())
|
||||||
} else {
|
// } else {
|
||||||
writers = append(writers, os.Stderr)
|
// writers = append(writers, os.Stderr)
|
||||||
}
|
// }
|
||||||
if dsn := cfg.Sentry.DSN; dsn != "" {
|
if dsn := cfg.Sentry.DSN; dsn != "" {
|
||||||
sentryWriter, err := NewSentryWriter(
|
sentryWriter, err := NewSentryWriter(
|
||||||
cfg.Sentry.DSN,
|
cfg.Sentry.DSN,
|
||||||
|
@ -83,3 +83,20 @@ func (m *StorageMinio) DeleteObject(ctx context.Context, objectName string) (err
|
|||||||
}
|
}
|
||||||
return nil
|
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