feat: docs api design

This commit is contained in:
neo-f
2023-03-22 22:45:17 +08:00
commit 084d0de8bc
52 changed files with 3420 additions and 0 deletions

View File

@@ -0,0 +1,77 @@
package dal
import (
"context"
"errors"
"time"
"github.com/rs/zerolog/log"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"gorm.io/gorm/utils"
)
type GormLogger struct {
SourceField string
SlowThreshold time.Duration
SkipErrRecordNotFound bool
}
// Error implements logger.Interface
func (l *GormLogger) Error(ctx context.Context, s string, args ...interface{}) {
log.Ctx(ctx).Error().Msgf(s, args...)
}
// Warn implements logger.Interface
func (*GormLogger) Warn(ctx context.Context, s string, args ...interface{}) {
log.Ctx(ctx).Warn().Msgf(s, args...)
}
// Info implements logger.Interface
func (*GormLogger) Info(ctx context.Context, s string, args ...interface{}) {
log.Ctx(ctx).Info().Msgf(s, args...)
}
// LogMode implements logger.Interface
func (l *GormLogger) LogMode(logger.LogLevel) logger.Interface {
return l
}
// Trace implements logger.Interface
func (l *GormLogger) Trace(
ctx context.Context,
begin time.Time,
fc func() (string, int64),
err error,
) {
elapsed := time.Since(begin)
sql, rows := fc()
log := log.Ctx(ctx).
With().
Str("sql", sql).
Str("elapsed", elapsed.String()).
Int64("rows", rows).
Logger()
if l.SourceField != "" {
log = log.With().Str(l.SourceField, utils.FileWithLineNum()).Logger()
}
if err != nil && (!errors.Is(err, gorm.ErrRecordNotFound) || !l.SkipErrRecordNotFound) {
log.Error().Err(err).Msg("[GORM] query error")
return
}
if l.SlowThreshold != 0 && elapsed > l.SlowThreshold {
log.Warn().Msg("[GORM] slow query")
return
}
log.Debug().Msg("[GORM] query")
}
var _ logger.Interface = &GormLogger{}
var DefaultGormLogger = &GormLogger{
SlowThreshold: time.Second * 3,
SourceField: "source",
SkipErrRecordNotFound: true,
}

View File

@@ -0,0 +1,18 @@
package migrations
import (
"octopus/internal/dal/model"
"github.com/rs/zerolog/log"
"gorm.io/gorm"
)
func Migrate(db *gorm.DB) {
if err := db.AutoMigrate(
new(model.Doc),
new(model.DocFolder),
); err != nil {
log.Fatal().Err(err).Msg("failed to migrate database")
}
}

View File

@@ -0,0 +1,10 @@
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
CreatedAt time.Time `gorm:"column:created_at;comment:创建时间"`
UpdatedAt time.Time `gorm:"column:updated_at;comment:更新时间"`
}

View File

@@ -0,0 +1,57 @@
package model
import (
"context"
"net/url"
"octopus/internal/config"
"octopus/internal/dal"
"time"
"github.com/rs/xid"
"gorm.io/gorm"
)
const TableNameDocFolder = "doc_folders"
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"` // 路径索引
CreatedBy string `gorm:"column:created_by;type:varchar;not null"` // 创建人
}
func (*DocFolder) TableName() string {
return TableNameDocFolder
}
func (df *DocFolder) BeforeCreate(*gorm.DB) error {
df.ID = xid.New().String()
return nil
}
type Doc 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"` // 是否允许编辑名称
FolderID string `gorm:"column:folder_id;type:varchar;index:idx_folder_id"` // 文件夹ID
OSSObjectID string `gorm:"column:oss_object_id;type:varchar"` // OSS Object ID
CreatedBy string `gorm:"column:created_by;type:varchar;not null"` // 创建人
Folder *DocFolder `gorm:"foreignKey:FolderID;references:ID"`
}
func (df *Doc) PresignedURL(ctx context.Context) (*url.URL, error) {
bucket := config.Get().GetOSSConfig().Bucket
return dal.GetMinio().PresignedGetObject(ctx, bucket, df.OSSObjectID, time.Hour, nil)
}
func (*Doc) TableName() string {
return TableNameDocFolder
}
func (df *Doc) BeforeCreate(*gorm.DB) error {
df.ID = xid.New().String()
return nil
}

50
internal/dal/oss.go Normal file
View File

@@ -0,0 +1,50 @@
package dal
import (
"net/url"
"octopus/internal/config"
"sync"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"github.com/rs/zerolog/log"
// pb "github.com/qdrant/go-client/qdrant"
)
var (
ossOnce sync.Once
ossInstance *minio.Client
)
func GetMinio() *minio.Client {
ossOnce.Do(initMinio)
return ossInstance
}
func initMinio() {
log.Info().Msg("loading minio configs")
ossConfig, err := url.Parse(config.Get().Databases.OSS)
if err != nil {
log.Fatal().Err(err).Msg("parse oss config error")
}
accessSecret, _ := ossConfig.User.Password()
// defaultConfig := &model.StorageConfig{
// Schema: ossConfig.Scheme,
// Endpoint: ossConfig.Host,
// AccessID: ossConfig.User.Username(),
// AccessSecret: accessSecret,
// Bucket: ossConfig.Query().Get("bucket"),
// Region: ossConfig.Query().Get("region"),
// Secure: cast.ToBool(ossConfig.Query().Get("secure")),
// }
minioClient, err := minio.New(ossConfig.Host, &minio.Options{
Creds: credentials.NewStaticV4(ossConfig.User.Username(), accessSecret, ""),
Secure: false,
})
if err != nil {
log.Fatal().Msgf("minio client init error: %v", err)
}
ossInstance = minioClient
}

75
internal/dal/postgres.go Normal file
View File

@@ -0,0 +1,75 @@
package dal
import (
slog "log"
"os"
"sync"
"time"
"octopus/internal/config"
"github.com/rs/zerolog/log"
"gorm.io/driver/postgres"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
var (
sqlOnce sync.Once
sqlInstance *gorm.DB
)
func GetpostgresTest() *gorm.DB {
l := logger.Default
l = l.LogMode(logger.Info)
db, _ := gorm.Open(sqlite.Open("file::memory:?parseTime=True&loc=Local"), &gorm.Config{Logger: l})
return db
}
func GetPostgres() *gorm.DB {
sqlOnce.Do(initPostgres)
return sqlInstance
}
func initPostgres() {
log.Info().Msg("loading postgres configs")
dsn := config.Get().Databases.PostgreSQL
db, err := gorm.Open(
postgres.Open(dsn),
&gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
Logger: logger.New(
slog.New(os.Stdout, "\r\n", slog.LstdFlags), logger.Config{
SlowThreshold: 200 * time.Millisecond,
LogLevel: logger.Info,
IgnoreRecordNotFoundError: false,
Colorful: true,
},
),
},
)
if err != nil {
log.Fatal().Str("dsn", dsn).Err(err).Msg("connected to postgres failed")
}
sqlDB, err := db.DB()
if err != nil {
log.Fatal().Str("dsn", dsn).Err(err).Msg("connected to postgres failed")
}
// SetMaxIdleConns sets the maximum number of connections in the idle connection pool.
sqlDB.SetMaxIdleConns(50)
// SetMaxOpenConns sets the maximum number of open connections to the database.
sqlDB.SetMaxOpenConns(50)
// SetConnMaxLifetime sets the maximum amount of time a connection may be reused.
sqlDB.SetConnMaxLifetime(time.Minute * 2)
if err != nil {
log.Fatal().Str("dsn", dsn).Err(err).Msg("connected to postgres failed")
}
if config.Get().Debug {
db = db.Debug()
}
sqlInstance = db
log.Info().Msg("connected to postgres")
}

23
internal/dal/repo/docs.go Normal file
View File

@@ -0,0 +1,23 @@
package repo
import (
"octopus/internal/dal/model"
"gorm.io/gorm"
)
type DocFolderRepo struct {
db *gorm.DB
}
func NewDocFolderRepo(db *gorm.DB) *DocFolderRepo {
return &DocFolderRepo{db}
}
type DocFolderRepoInterface interface {
CreateFolder(folder *model.DocFolder) error
}
// func (r *DocFolderRepo) CreateFolder(folder *model.DocFolder) error {
// return r.db.Create(folder).Error
// }