mirror of
https://github.com/serty2005/rmser.git
synced 2026-02-04 19:02:33 -06:00
добавил инвайт-ссылки с ролью оператор для сервера добавил супер-админку для смены владельцев добавил уведомления о смене ролей на серверах добавил модалку для фото прям в черновике добавил UI для редактирования прав
185 lines
6.6 KiB
Go
185 lines
6.6 KiB
Go
package main
|
||
|
||
import (
|
||
"log"
|
||
"os"
|
||
"time"
|
||
|
||
"github.com/gin-contrib/cors"
|
||
"github.com/gin-gonic/gin"
|
||
"github.com/google/uuid"
|
||
"go.uber.org/zap"
|
||
|
||
"rmser/config"
|
||
"rmser/internal/infrastructure/db"
|
||
"rmser/internal/infrastructure/ocr_client"
|
||
|
||
"rmser/internal/transport/http/middleware"
|
||
tgBot "rmser/internal/transport/telegram"
|
||
|
||
// Repositories
|
||
accountPkg "rmser/internal/infrastructure/repository/account"
|
||
catalogPkg "rmser/internal/infrastructure/repository/catalog"
|
||
draftsPkg "rmser/internal/infrastructure/repository/drafts"
|
||
invoicesPkg "rmser/internal/infrastructure/repository/invoices"
|
||
ocrRepoPkg "rmser/internal/infrastructure/repository/ocr"
|
||
opsRepoPkg "rmser/internal/infrastructure/repository/operations"
|
||
recipesPkg "rmser/internal/infrastructure/repository/recipes"
|
||
recRepoPkg "rmser/internal/infrastructure/repository/recommendations"
|
||
suppliersPkg "rmser/internal/infrastructure/repository/suppliers"
|
||
|
||
"rmser/internal/infrastructure/rms"
|
||
|
||
// Services
|
||
draftsServicePkg "rmser/internal/services/drafts"
|
||
ocrServicePkg "rmser/internal/services/ocr"
|
||
recServicePkg "rmser/internal/services/recommend"
|
||
"rmser/internal/services/sync"
|
||
|
||
// Handlers
|
||
"rmser/internal/transport/http/handlers"
|
||
|
||
"rmser/pkg/crypto"
|
||
"rmser/pkg/logger"
|
||
)
|
||
|
||
func main() {
|
||
// 1. Config
|
||
cfg, err := config.LoadConfig(".")
|
||
if err != nil {
|
||
log.Fatalf("Ошибка загрузки конфига: %v", err)
|
||
}
|
||
|
||
// 2. Logger
|
||
logger.Init(cfg.App.Mode)
|
||
defer logger.Log.Sync()
|
||
|
||
if err := os.MkdirAll(cfg.App.StoragePath, 0755); err != nil {
|
||
logger.Log.Fatal("Не удалось создать директорию для загрузок", zap.Error(err), zap.String("path", cfg.App.StoragePath))
|
||
}
|
||
logger.Log.Info("Запуск приложения rmser", zap.String("mode", cfg.App.Mode))
|
||
|
||
// 3. Crypto & DB
|
||
if cfg.Security.SecretKey == "" {
|
||
logger.Log.Fatal("Security.SecretKey не задан в конфиге!")
|
||
}
|
||
cryptoManager := crypto.NewCryptoManager(cfg.Security.SecretKey)
|
||
database := db.NewPostgresDB(cfg.DB.DSN)
|
||
|
||
// 4. Repositories
|
||
accountRepo := accountPkg.NewRepository(database)
|
||
catalogRepo := catalogPkg.NewRepository(database)
|
||
recipesRepo := recipesPkg.NewRepository(database)
|
||
invoicesRepo := invoicesPkg.NewRepository(database)
|
||
opsRepo := opsRepoPkg.NewRepository(database)
|
||
recRepo := recRepoPkg.NewRepository(database)
|
||
ocrRepo := ocrRepoPkg.NewRepository(database)
|
||
draftsRepo := draftsPkg.NewRepository(database)
|
||
supplierRepo := suppliersPkg.NewRepository(database)
|
||
|
||
// 5. RMS Factory
|
||
rmsFactory := rms.NewFactory(accountRepo, cryptoManager)
|
||
|
||
// 6. Services
|
||
pyClient := ocr_client.NewClient(cfg.OCR.ServiceURL)
|
||
|
||
syncService := sync.NewService(rmsFactory, accountRepo, catalogRepo, recipesRepo, invoicesRepo, opsRepo, supplierRepo)
|
||
recService := recServicePkg.NewService(recRepo)
|
||
ocrService := ocrServicePkg.NewService(ocrRepo, catalogRepo, draftsRepo, accountRepo, pyClient, cfg.App.StoragePath)
|
||
draftsService := draftsServicePkg.NewService(draftsRepo, ocrRepo, catalogRepo, accountRepo, supplierRepo, rmsFactory)
|
||
|
||
// 7. Handlers
|
||
draftsHandler := handlers.NewDraftsHandler(draftsService)
|
||
ocrHandler := handlers.NewOCRHandler(ocrService)
|
||
recommendHandler := handlers.NewRecommendationsHandler(recService)
|
||
settingsHandler := handlers.NewSettingsHandler(accountRepo, catalogRepo)
|
||
|
||
// 8. Telegram Bot (Передаем syncService)
|
||
if cfg.Telegram.Token != "" {
|
||
bot, err := tgBot.NewBot(cfg.Telegram, ocrService, syncService, accountRepo, rmsFactory, cryptoManager)
|
||
if err != nil {
|
||
logger.Log.Fatal("Ошибка создания Telegram бота", zap.Error(err))
|
||
}
|
||
settingsHandler.SetNotifier(bot) // Внедряем зависимость
|
||
go bot.Start()
|
||
defer bot.Stop()
|
||
}
|
||
|
||
// 9. HTTP Server
|
||
if cfg.App.Mode == "release" {
|
||
gin.SetMode(gin.ReleaseMode)
|
||
}
|
||
r := gin.Default()
|
||
|
||
corsConfig := cors.DefaultConfig()
|
||
corsConfig.AllowAllOrigins = true
|
||
corsConfig.AllowMethods = []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"}
|
||
corsConfig.AllowHeaders = []string{"Origin", "Content-Length", "Content-Type", "Authorization", "X-Telegram-User-ID"}
|
||
r.Use(cors.New(corsConfig))
|
||
|
||
// --- STATIC FILES SERVING ---
|
||
// Раздаем папку uploads по урлу /api/uploads
|
||
r.Static("/api/uploads", cfg.App.StoragePath)
|
||
|
||
api := r.Group("/api")
|
||
|
||
api.Use(middleware.AuthMiddleware(accountRepo, cfg.Telegram.Token))
|
||
{
|
||
// Drafts & Invoices
|
||
api.GET("/drafts", draftsHandler.GetDrafts)
|
||
api.GET("/drafts/:id", draftsHandler.GetDraft)
|
||
api.DELETE("/drafts/:id", draftsHandler.DeleteDraft)
|
||
// Items CRUD
|
||
api.POST("/drafts/:id/items", draftsHandler.AddDraftItem)
|
||
api.DELETE("/drafts/:id/items/:itemId", draftsHandler.DeleteDraftItem)
|
||
api.PATCH("/drafts/:id/items/:itemId", draftsHandler.UpdateItem)
|
||
api.POST("/drafts/:id/commit", draftsHandler.CommitDraft)
|
||
api.POST("/drafts/container", draftsHandler.AddContainer)
|
||
|
||
// Settings
|
||
api.GET("/settings", settingsHandler.GetSettings)
|
||
api.POST("/settings", settingsHandler.UpdateSettings)
|
||
// User Management
|
||
api.GET("/settings/users", settingsHandler.GetServerUsers)
|
||
api.PATCH("/settings/users/:userId", settingsHandler.UpdateUserRole)
|
||
api.DELETE("/settings/users/:userId", settingsHandler.RemoveUser)
|
||
|
||
// Dictionaries
|
||
api.GET("/dictionaries", draftsHandler.GetDictionaries)
|
||
api.GET("/dictionaries/groups", settingsHandler.GetGroupsTree)
|
||
api.GET("/dictionaries/stores", draftsHandler.GetStores)
|
||
|
||
// Recommendations
|
||
api.GET("/recommendations", recommendHandler.GetRecommendations)
|
||
|
||
// OCR & Matching
|
||
api.GET("/ocr/catalog", ocrHandler.GetCatalog)
|
||
api.GET("/ocr/matches", ocrHandler.GetMatches)
|
||
api.POST("/ocr/match", ocrHandler.SaveMatch)
|
||
api.DELETE("/ocr/match", ocrHandler.DeleteMatch)
|
||
api.GET("/ocr/unmatched", ocrHandler.GetUnmatched)
|
||
api.GET("/ocr/search", ocrHandler.SearchProducts)
|
||
|
||
// Manual Sync Trigger
|
||
api.POST("/sync/all", func(c *gin.Context) {
|
||
userID := c.MustGet("userID").(uuid.UUID)
|
||
// Запускаем в горутине, чтобы не держать соединение
|
||
go func() {
|
||
if err := syncService.SyncAllData(userID); err != nil {
|
||
logger.Log.Error("Manual sync failed", zap.Error(err))
|
||
}
|
||
}()
|
||
c.JSON(200, gin.H{"status": "sync_started", "message": "Синхронизация запущена в фоне"})
|
||
})
|
||
}
|
||
|
||
r.GET("/health", func(c *gin.Context) {
|
||
c.JSON(200, gin.H{"status": "ok", "time": time.Now().Format(time.RFC3339)})
|
||
})
|
||
|
||
logger.Log.Info("Сервер запускается", zap.String("port", cfg.App.Port))
|
||
if err := r.Run(":" + cfg.App.Port); err != nil {
|
||
logger.Log.Fatal("Ошибка запуска сервера", zap.Error(err))
|
||
}
|
||
}
|