move media models
This commit is contained in:
@@ -0,0 +1,162 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
CfgFile string
|
||||||
|
Verbose int
|
||||||
|
Overwrite bool
|
||||||
|
ServerName string
|
||||||
|
Mediatype MediaType
|
||||||
|
Config CliConfig
|
||||||
|
ServerConfig ServerConfiguration
|
||||||
|
DryRun bool
|
||||||
|
FileName string
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
mediaTypeMedia MediaType = "media"
|
||||||
|
mediaTypeVideo MediaType = "video"
|
||||||
|
mediaTypeLofi MediaType = "lofi"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MediaType string
|
||||||
|
|
||||||
|
func (e *MediaType) String() string {
|
||||||
|
return string(*e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *MediaType) Set(v string) error {
|
||||||
|
switch v {
|
||||||
|
case "media", "video", "lofi":
|
||||||
|
*e = MediaType(v)
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return errors.New(`must be one of "media", "video", "lofi"`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *MediaType) Type() string {
|
||||||
|
return "MediaType"
|
||||||
|
}
|
||||||
|
|
||||||
|
func myEnumCompletion(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||||
|
return []string{
|
||||||
|
"media\thelp text for media",
|
||||||
|
"video\thelp text for video",
|
||||||
|
"lofi\thelp text for lofi",
|
||||||
|
}, cobra.ShellCompDirectiveDefault
|
||||||
|
}
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
Url string `mapstructure:"url"`
|
||||||
|
Username string `mapstructure:"username"`
|
||||||
|
Password string `mapstructure:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Database struct {
|
||||||
|
Type string `mapstructure:"type" validate:"required"`
|
||||||
|
Path string `mapstructure:"path" validate:"required"`
|
||||||
|
Host string `mapstructure:"host" validate:"required"`
|
||||||
|
Name string `mapstructure:"name" validate:"required"`
|
||||||
|
Port int `mapstructure:"port" validate:"required"`
|
||||||
|
Password string `mapstructure:"password" validate:"required"`
|
||||||
|
User string `mapstructure:"user" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CliConfig struct {
|
||||||
|
ApiVersion string
|
||||||
|
Server map[string]Server `mapstructure:"server"`
|
||||||
|
DB Database `mapstructure:"db"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfiguration struct {
|
||||||
|
Server struct {
|
||||||
|
Port int `mapstructure:"port" validate:"required"`
|
||||||
|
Host string `mapstructure:"host"`
|
||||||
|
} `mapstructure:"server"`
|
||||||
|
DB Database `mapstructure:"db"`
|
||||||
|
Gin struct {
|
||||||
|
Mode string `mapstructure:"mode" validate:"required"`
|
||||||
|
} `mapstructure:"gin"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitCliConfig() {
|
||||||
|
slog.Info("InitCliConfig")
|
||||||
|
if CfgFile != "" {
|
||||||
|
viper.SetConfigFile(CfgFile)
|
||||||
|
} else {
|
||||||
|
viper.SetConfigName("kli") // without extension
|
||||||
|
viper.SetConfigType("yaml")
|
||||||
|
viper.AddConfigPath(".") // look for config in working directory
|
||||||
|
viper.AddConfigPath("/home/tpeetz/.config")
|
||||||
|
}
|
||||||
|
Mediatype = mediaTypeMedia
|
||||||
|
|
||||||
|
// Read the configuration file
|
||||||
|
if err := viper.ReadInConfig(); err != nil {
|
||||||
|
// Handle the case where config file is not found
|
||||||
|
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
||||||
|
slog.Info("No config file found, using defaults")
|
||||||
|
if err := viper.SafeWriteConfig(); err != nil {
|
||||||
|
slog.Info("error writing config file", "error", err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
slog.Info("error reading config file", "error", err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
slog.Info("InitCliConfig", "Using config file: ", viper.ConfigFileUsed())
|
||||||
|
if err := viper.Unmarshal(&Config); err != nil {
|
||||||
|
slog.Info("parsing of config file failed", "error", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitConfig() error {
|
||||||
|
|
||||||
|
viper.SetConfigName("kontor-gin")
|
||||||
|
viper.SetConfigType("yaml")
|
||||||
|
viper.AddConfigPath(".")
|
||||||
|
viper.AddConfigPath("/home/tpeetz/.config")
|
||||||
|
viper.AddConfigPath("/etc/kontor")
|
||||||
|
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||||
|
viper.AutomaticEnv()
|
||||||
|
|
||||||
|
viper.SetDefault("db.type", "postgres")
|
||||||
|
viper.SetDefault("db.host", "localhost")
|
||||||
|
viper.SetDefault("db.port", 5432)
|
||||||
|
viper.SetDefault("db.name", "kontor")
|
||||||
|
viper.SetDefault("db.user", "kontor")
|
||||||
|
viper.SetDefault("db.password", "kontor")
|
||||||
|
viper.SetDefault("db.path", "data/kontor.db")
|
||||||
|
viper.SetDefault("server.port", 8550)
|
||||||
|
viper.SetDefault("gin.mode", "release")
|
||||||
|
// Read the config file
|
||||||
|
if err := viper.ReadInConfig(); err != nil {
|
||||||
|
// Handle the case where config file is not found
|
||||||
|
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
||||||
|
slog.Info("No config file found, using defaults")
|
||||||
|
if err := viper.SafeWriteConfig(); err != nil {
|
||||||
|
slog.Info("error writing config file", "error", err.Error())
|
||||||
|
return fmt.Errorf("error writing config file: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("error reading config file: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
slog.Info("InitConfig", "Using config file: ", viper.ConfigFileUsed())
|
||||||
|
if err := viper.Unmarshal(&ServerConfig); err != nil {
|
||||||
|
slog.Info("parsing of config file failed", "error", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"gorm.io/driver/postgres"
|
||||||
|
"gorm.io/driver/sqlite"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewDB(db Database) (*gorm.DB, error) {
|
||||||
|
db_type := db.Type
|
||||||
|
slog.Info("using database", "type", db_type)
|
||||||
|
|
||||||
|
switch db_type {
|
||||||
|
case "postgres":
|
||||||
|
dsn := fmt.Sprintf(
|
||||||
|
"host=%s user=%s password=%s dbname=%s port=%d sslmode=disable",
|
||||||
|
db.Host,
|
||||||
|
db.User,
|
||||||
|
db.Password,
|
||||||
|
db.Name,
|
||||||
|
db.Port,
|
||||||
|
)
|
||||||
|
|
||||||
|
return gorm.Open(postgres.Open(dsn), &gorm.Config{
|
||||||
|
Logger: logger.Default.LogMode(logger.Info),
|
||||||
|
})
|
||||||
|
case "sqlite":
|
||||||
|
return gorm.Open(sqlite.Open(db.Path), &gorm.Config{
|
||||||
|
Logger: logger.Default.LogMode(logger.Info),
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown db.type=%s", db_type)
|
||||||
|
}
|
||||||
|
// Configure GORM with custom settings
|
||||||
|
// The logger helps with debugging SQL queries during development
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatal("Failed to connect to database:", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Get the underlying SQL database connection for pool configuration
|
||||||
|
// sqlDB, err := db.DB()
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatal("Failed to get database instance:", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Configure connection pool settings
|
||||||
|
// // MaxIdleConns: number of connections kept open when idle
|
||||||
|
// sqlDB.SetMaxIdleConns(10)
|
||||||
|
// // MaxOpenConns: maximum total connections to the database
|
||||||
|
// sqlDB.SetMaxOpenConns(100)
|
||||||
|
// // ConnMaxLifetime: how long a connection can be reused
|
||||||
|
// sqlDB.SetConnMaxLifetime(time.Hour)
|
||||||
|
|
||||||
|
// slog.Info("Database connected successfully")
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AdminHandler struct {
|
||||||
|
AuthHandler *AuthHandler
|
||||||
|
ProfileHandler *ProfileHandler
|
||||||
|
PermissionHandler *PermissionHandler
|
||||||
|
AssignmentHandler *AssignmentHandler
|
||||||
|
TokenHandler *TokenHandler
|
||||||
|
MailAccountHandler *MailAccountHandler
|
||||||
|
MailHandler *MailHandler
|
||||||
|
Secret []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdminHandler(db *bun.DB, secret []byte) *AdminHandler {
|
||||||
|
adminHandler := &AdminHandler{
|
||||||
|
AuthHandler: &AuthHandler{Service: service.NewAuthService(repository.NewProfileRepository(db), secret)},
|
||||||
|
ProfileHandler: NewProfileHandler(db),
|
||||||
|
PermissionHandler: &PermissionHandler{Service: service.NewPermissionService(repository.NewPermissionRepository(db))},
|
||||||
|
AssignmentHandler: NewAssignmentHandler(db),
|
||||||
|
TokenHandler: NewTokenHandler(db),
|
||||||
|
MailAccountHandler: NewMailAccountHandler(db),
|
||||||
|
MailHandler: NewMailHandler(db),
|
||||||
|
Secret: secret,
|
||||||
|
}
|
||||||
|
return adminHandler
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MediaActor struct {
|
||||||
|
bun.BaseModel `bun:"table:media_actor"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Name string `bun:"name"`
|
||||||
|
WebLink string `bun:"url,unique:url"`
|
||||||
|
|
||||||
|
MediaActorFiles []MediaActorFile `bun:"rel:has-many,join:id=media_actor_id"`
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MediaActorFile struct {
|
||||||
|
bun.BaseModel `bun:"table:media_actor_file"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
MediaActorID *string `bun:"media_actor_id"`
|
||||||
|
MediaActor *MediaActor `bun:"rel:belongs-to,join:media_actor_id=id"`
|
||||||
|
MediaFileID *string `bun:"media_file_id"`
|
||||||
|
MediaFile *MediaFile `bun:"rel:belongs-to,join:media_file_id=id"`
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MediaFile struct {
|
||||||
|
bun.BaseModel `bun:"table:media_file"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
CloudLink string `bun:"cloud_link"`
|
||||||
|
FileName string `bun:"file_name"`
|
||||||
|
Path string `bun:"path"`
|
||||||
|
Review bool `bun:"review"`
|
||||||
|
Title string `bun:"title"`
|
||||||
|
WebLink string `bun:"url,unique:url"`
|
||||||
|
ShouldDownload bool `bun:"should_download"`
|
||||||
|
|
||||||
|
MediaActorFiles []MediaActorFile `bun:"rel:has-many,join:id=media_file_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MediaActor struct {
|
||||||
|
bun.BaseModel `bun:"table:media_actor"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Name string `bun:"name"`
|
||||||
|
WebLink string `bun:"url,unique:url"`
|
||||||
|
|
||||||
|
MediaActorFiles []MediaActorFile `bun:"rel:has-many,join:id=media_actor_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MediaActorFile struct {
|
||||||
|
bun.BaseModel `bun:"table:media_actor_file"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
MediaActorID *string `bun:"media_actor_id"`
|
||||||
|
MediaActor *MediaActor `bun:"rel:belongs-to,join:media_actor_id=id"`
|
||||||
|
MediaFileID *string `bun:"media_file_id"`
|
||||||
|
MediaFile *MediaFile `bun:"rel:belongs-to,join:media_file_id=id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MediaArticle struct {
|
||||||
|
bun.BaseModel `bun:"table:media_article"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
Review bool `bun:"review"`
|
||||||
|
Title string `bun:"title"`
|
||||||
|
WebLink string `bun:"url,unique:url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MediaVideo struct {
|
||||||
|
bun.BaseModel `bun:"table:media_article"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
CloudLink string `bun:"cloud_link"`
|
||||||
|
FileName string `bun:"file_name"`
|
||||||
|
Path string `bun:"path"`
|
||||||
|
Review bool `bun:"review"`
|
||||||
|
Title string `bun:"title"`
|
||||||
|
WebLink string `bun:"url,unique:url"`
|
||||||
|
ShouldDownload bool `bun:"should_download"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MediaFile struct {
|
||||||
|
bun.BaseModel `bun:"table:media_file"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
CloudLink string `bun:"cloud_link"`
|
||||||
|
FileName string `bun:"file_name"`
|
||||||
|
Path string `bun:"path"`
|
||||||
|
Review bool `bun:"review"`
|
||||||
|
Title string `bun:"title"`
|
||||||
|
WebLink string `bun:"url,unique:url"`
|
||||||
|
ShouldDownload bool `bun:"should_download"`
|
||||||
|
|
||||||
|
MediaActorFiles []MediaActorFile `bun:"rel:has-many,join:id=media_file_id"`
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MediaVideo struct {
|
||||||
|
bun.BaseModel `bun:"table:media_article"`
|
||||||
|
|
||||||
|
ID string `bun:"id,pk"`
|
||||||
|
CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"`
|
||||||
|
Version int `bun:"version,default:0"`
|
||||||
|
CloudLink string `bun:"cloud_link"`
|
||||||
|
FileName string `bun:"file_name"`
|
||||||
|
Path string `bun:"path"`
|
||||||
|
Review bool `bun:"review"`
|
||||||
|
Title string `bun:"title"`
|
||||||
|
WebLink string `bun:"url,unique:url"`
|
||||||
|
ShouldDownload bool `bun:"should_download"`
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
echojwt "github.com/labstack/echo-jwt/v5"
|
||||||
|
"github.com/labstack/echo/v5"
|
||||||
|
"github.com/labstack/echo/v5/middleware"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(admin *handlers.AdminHandler, media *handlers.MediaHandler) *echo.Router {
|
||||||
|
e := echo.New()
|
||||||
|
|
||||||
|
skipper := func(c echo.Context) bool {
|
||||||
|
// Skip health check endpoint
|
||||||
|
return c.Request().URL.Path == "/health"
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{
|
||||||
|
LogStatus: true,
|
||||||
|
LogURI: true,
|
||||||
|
LogError: true,
|
||||||
|
HandleError: true,
|
||||||
|
Skipper: skipper,
|
||||||
|
LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error {
|
||||||
|
if v.Error == nil {
|
||||||
|
slog.Info("REQUEST", "uri", v.URI, "status", v.Status)
|
||||||
|
} else {
|
||||||
|
slog.Info("REQUEST-ERROR", "uri",v.URI, "status", v.Status,"err", v.Error.Error())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
return e
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user