mirror of
https://github.com/SamyRai/tercul-backend.git
synced 2025-12-27 05:11:34 +00:00
- Core Go application with GraphQL API using gqlgen - Comprehensive data models for literary works, authors, translations - Repository pattern with caching layer - Authentication and authorization system - Linguistics analysis capabilities with multiple adapters - Vector search integration with Weaviate - Docker containerization support - Python data migration and analysis scripts - Clean architecture with proper separation of concerns - Production-ready configuration and middleware - Proper .gitignore excluding vendor/, database files, and build artifacts
312 lines
8.7 KiB
Go
312 lines
8.7 KiB
Go
package app
|
|
|
|
import (
|
|
"tercul/cache"
|
|
"tercul/config"
|
|
"tercul/db"
|
|
"tercul/linguistics"
|
|
"tercul/logger"
|
|
"tercul/repositories"
|
|
"tercul/services"
|
|
"time"
|
|
|
|
"github.com/hibiken/asynq"
|
|
"github.com/weaviate/weaviate-go-client/v5/weaviate"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// ApplicationBuilder handles the initialization of all application components
|
|
type ApplicationBuilder struct {
|
|
dbConn *gorm.DB
|
|
redisCache cache.Cache
|
|
weaviateClient *weaviate.Client
|
|
asynqClient *asynq.Client
|
|
repositories *RepositoryContainer
|
|
services *ServiceContainer
|
|
linguistics *linguistics.LinguisticsFactory
|
|
}
|
|
|
|
// RepositoryContainer holds all repository instances
|
|
type RepositoryContainer struct {
|
|
WorkRepository repositories.WorkRepository
|
|
UserRepository repositories.UserRepository
|
|
AuthorRepository repositories.AuthorRepository
|
|
TranslationRepository repositories.TranslationRepository
|
|
CommentRepository repositories.CommentRepository
|
|
LikeRepository repositories.LikeRepository
|
|
BookmarkRepository repositories.BookmarkRepository
|
|
CollectionRepository repositories.CollectionRepository
|
|
TagRepository repositories.TagRepository
|
|
CategoryRepository repositories.CategoryRepository
|
|
CopyrightRepository repositories.CopyrightRepository
|
|
}
|
|
|
|
// ServiceContainer holds all service instances
|
|
type ServiceContainer struct {
|
|
WorkService services.WorkService
|
|
CopyrightService services.CopyrightService
|
|
LocalizationService services.LocalizationService
|
|
AuthService services.AuthService
|
|
}
|
|
|
|
// NewApplicationBuilder creates a new ApplicationBuilder
|
|
func NewApplicationBuilder() *ApplicationBuilder {
|
|
return &ApplicationBuilder{}
|
|
}
|
|
|
|
// BuildDatabase initializes the database connection
|
|
func (b *ApplicationBuilder) BuildDatabase() error {
|
|
logger.LogInfo("Initializing database connection")
|
|
|
|
dbConn, err := db.InitDB()
|
|
if err != nil {
|
|
logger.LogFatal("Failed to initialize database - application cannot start without database connection",
|
|
logger.F("error", err),
|
|
logger.F("host", config.Cfg.DBHost),
|
|
logger.F("database", config.Cfg.DBName))
|
|
return err
|
|
}
|
|
|
|
b.dbConn = dbConn
|
|
logger.LogInfo("Database initialized successfully",
|
|
logger.F("host", config.Cfg.DBHost),
|
|
logger.F("database", config.Cfg.DBName))
|
|
|
|
return nil
|
|
}
|
|
|
|
// BuildCache initializes the Redis cache
|
|
func (b *ApplicationBuilder) BuildCache() error {
|
|
logger.LogInfo("Initializing Redis cache")
|
|
|
|
redisCache, err := cache.NewDefaultRedisCache()
|
|
if err != nil {
|
|
logger.LogWarn("Failed to initialize Redis cache, continuing without caching - performance may be degraded",
|
|
logger.F("error", err),
|
|
logger.F("redisAddr", config.Cfg.RedisAddr))
|
|
} else {
|
|
b.redisCache = redisCache
|
|
logger.LogInfo("Redis cache initialized successfully",
|
|
logger.F("redisAddr", config.Cfg.RedisAddr))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// BuildWeaviate initializes the Weaviate client
|
|
func (b *ApplicationBuilder) BuildWeaviate() error {
|
|
logger.LogInfo("Connecting to Weaviate",
|
|
logger.F("host", config.Cfg.WeaviateHost),
|
|
logger.F("scheme", config.Cfg.WeaviateScheme))
|
|
|
|
wClient, err := weaviate.NewClient(weaviate.Config{
|
|
Scheme: config.Cfg.WeaviateScheme,
|
|
Host: config.Cfg.WeaviateHost,
|
|
})
|
|
if err != nil {
|
|
logger.LogFatal("Failed to create Weaviate client - vector search capabilities will not be available",
|
|
logger.F("error", err),
|
|
logger.F("host", config.Cfg.WeaviateHost),
|
|
logger.F("scheme", config.Cfg.WeaviateScheme))
|
|
return err
|
|
}
|
|
|
|
b.weaviateClient = wClient
|
|
logger.LogInfo("Weaviate client initialized successfully")
|
|
|
|
return nil
|
|
}
|
|
|
|
// BuildBackgroundJobs initializes Asynq for background job processing
|
|
func (b *ApplicationBuilder) BuildBackgroundJobs() error {
|
|
logger.LogInfo("Setting up background job processing",
|
|
logger.F("redisAddr", config.Cfg.RedisAddr))
|
|
|
|
redisOpt := asynq.RedisClientOpt{
|
|
Addr: config.Cfg.RedisAddr,
|
|
Password: config.Cfg.RedisPassword,
|
|
DB: config.Cfg.RedisDB,
|
|
}
|
|
|
|
asynqClient := asynq.NewClient(redisOpt)
|
|
b.asynqClient = asynqClient
|
|
|
|
logger.LogInfo("Background job client initialized successfully")
|
|
|
|
return nil
|
|
}
|
|
|
|
// BuildRepositories initializes all repositories
|
|
func (b *ApplicationBuilder) BuildRepositories() error {
|
|
logger.LogInfo("Initializing repositories")
|
|
|
|
// Initialize base repositories
|
|
baseWorkRepo := repositories.NewWorkRepository(b.dbConn)
|
|
userRepo := repositories.NewUserRepository(b.dbConn)
|
|
authorRepo := repositories.NewAuthorRepository(b.dbConn)
|
|
translationRepo := repositories.NewTranslationRepository(b.dbConn)
|
|
commentRepo := repositories.NewCommentRepository(b.dbConn)
|
|
likeRepo := repositories.NewLikeRepository(b.dbConn)
|
|
bookmarkRepo := repositories.NewBookmarkRepository(b.dbConn)
|
|
collectionRepo := repositories.NewCollectionRepository(b.dbConn)
|
|
tagRepo := repositories.NewTagRepository(b.dbConn)
|
|
categoryRepo := repositories.NewCategoryRepository(b.dbConn)
|
|
copyrightRepo := repositories.NewCopyrightRepository(b.dbConn)
|
|
|
|
// Wrap work repository with cache if available
|
|
var workRepo repositories.WorkRepository
|
|
if b.redisCache != nil {
|
|
workRepo = repositories.NewCachedWorkRepository(
|
|
baseWorkRepo,
|
|
b.redisCache,
|
|
nil,
|
|
30*time.Minute, // Cache work data for 30 minutes
|
|
)
|
|
logger.LogInfo("Using cached work repository")
|
|
} else {
|
|
workRepo = baseWorkRepo
|
|
logger.LogInfo("Using non-cached work repository")
|
|
}
|
|
|
|
b.repositories = &RepositoryContainer{
|
|
WorkRepository: workRepo,
|
|
UserRepository: userRepo,
|
|
AuthorRepository: authorRepo,
|
|
TranslationRepository: translationRepo,
|
|
CommentRepository: commentRepo,
|
|
LikeRepository: likeRepo,
|
|
BookmarkRepository: bookmarkRepo,
|
|
CollectionRepository: collectionRepo,
|
|
TagRepository: tagRepo,
|
|
CategoryRepository: categoryRepo,
|
|
CopyrightRepository: copyrightRepo,
|
|
}
|
|
|
|
logger.LogInfo("Repositories initialized successfully")
|
|
|
|
return nil
|
|
}
|
|
|
|
// BuildLinguistics initializes the linguistics components
|
|
func (b *ApplicationBuilder) BuildLinguistics() error {
|
|
logger.LogInfo("Initializing linguistic analyzer")
|
|
|
|
b.linguistics = linguistics.NewLinguisticsFactory(
|
|
b.dbConn,
|
|
b.redisCache,
|
|
4, // Default concurrency
|
|
true, // Cache enabled
|
|
)
|
|
|
|
logger.LogInfo("Linguistics components initialized successfully")
|
|
|
|
return nil
|
|
}
|
|
|
|
// BuildServices initializes all services
|
|
func (b *ApplicationBuilder) BuildServices() error {
|
|
logger.LogInfo("Initializing service layer")
|
|
|
|
workService := services.NewWorkService(b.repositories.WorkRepository, b.linguistics.GetAnalyzer())
|
|
copyrightService := services.NewCopyrightService(b.repositories.CopyrightRepository)
|
|
localizationService := services.NewLocalizationService(b.repositories.TranslationRepository)
|
|
authService := services.NewAuthService(b.repositories.UserRepository)
|
|
|
|
b.services = &ServiceContainer{
|
|
WorkService: workService,
|
|
CopyrightService: copyrightService,
|
|
LocalizationService: localizationService,
|
|
AuthService: authService,
|
|
}
|
|
|
|
logger.LogInfo("Services initialized successfully")
|
|
|
|
return nil
|
|
}
|
|
|
|
// Build initializes all components in the correct order
|
|
func (b *ApplicationBuilder) Build() error {
|
|
// Build components in dependency order
|
|
if err := b.BuildDatabase(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := b.BuildCache(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := b.BuildWeaviate(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := b.BuildBackgroundJobs(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := b.BuildRepositories(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := b.BuildLinguistics(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := b.BuildServices(); err != nil {
|
|
return err
|
|
}
|
|
|
|
logger.LogInfo("Application builder completed successfully")
|
|
return nil
|
|
}
|
|
|
|
// GetDatabase returns the database connection
|
|
func (b *ApplicationBuilder) GetDatabase() *gorm.DB {
|
|
return b.dbConn
|
|
}
|
|
|
|
// GetCache returns the cache instance
|
|
func (b *ApplicationBuilder) GetCache() cache.Cache {
|
|
return b.redisCache
|
|
}
|
|
|
|
// GetWeaviateClient returns the Weaviate client
|
|
func (b *ApplicationBuilder) GetWeaviateClient() *weaviate.Client {
|
|
return b.weaviateClient
|
|
}
|
|
|
|
// GetAsynqClient returns the Asynq client
|
|
func (b *ApplicationBuilder) GetAsynqClient() *asynq.Client {
|
|
return b.asynqClient
|
|
}
|
|
|
|
// GetRepositories returns the repository container
|
|
func (b *ApplicationBuilder) GetRepositories() *RepositoryContainer {
|
|
return b.repositories
|
|
}
|
|
|
|
// GetServices returns the service container
|
|
func (b *ApplicationBuilder) GetServices() *ServiceContainer {
|
|
return b.services
|
|
}
|
|
|
|
// GetLinguistics returns the linguistics factory
|
|
func (b *ApplicationBuilder) GetLinguistics() *linguistics.LinguisticsFactory {
|
|
return b.linguistics
|
|
}
|
|
|
|
// Close closes all resources
|
|
func (b *ApplicationBuilder) Close() error {
|
|
if b.asynqClient != nil {
|
|
b.asynqClient.Close()
|
|
}
|
|
|
|
if b.dbConn != nil {
|
|
sqlDB, err := b.dbConn.DB()
|
|
if err == nil {
|
|
sqlDB.Close()
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|