Compare commits
1 Commits
2c682c5123
...
96fb472497
| Author | SHA1 | Date | |
|---|---|---|---|
| 96fb472497 |
@@ -15,26 +15,18 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Инициализируем соединение с БД
|
|
||||||
dbPool := database.Connect()
|
dbPool := database.Connect()
|
||||||
defer dbPool.Close()
|
defer dbPool.Close()
|
||||||
|
|
||||||
|
// --- Инициализация репозиториев ---
|
||||||
userRepo := &database.UserRepository{DB: dbPool}
|
userRepo := &database.UserRepository{DB: dbPool}
|
||||||
serverRepo := &database.ServerRepository{DB: dbPool}
|
serverRepo := &database.ServerRepository{DB: dbPool}
|
||||||
modpackRepo := &database.ModpackRepository{DB: dbPool}
|
modpackRepo := &database.ModpackRepository{DB: dbPool}
|
||||||
|
|
||||||
serverPoller := &core.ServerPoller{Repo: serverRepo}
|
// --- Инициализация сервисов ---
|
||||||
|
|
||||||
modpackHandler := &api.ModpackHandler{ModpackRepo: modpackRepo}
|
|
||||||
launcherHandler := &api.LauncherHandler{ModpackRepo: modpackRepo}
|
|
||||||
adminUserHandler := &api.AdminUserHandler{UserRepo: userRepo}
|
|
||||||
|
|
||||||
// Запускаем поллер в фоновой горутине
|
|
||||||
go serverPoller.Start(context.Background())
|
|
||||||
|
|
||||||
// Сервисы
|
|
||||||
userService := &core.UserService{Repo: userRepo}
|
userService := &core.UserService{Repo: userRepo}
|
||||||
authService := &core.AuthService{UserRepo: userRepo}
|
authService := &core.AuthService{UserRepo: userRepo}
|
||||||
|
serverPoller := &core.ServerPoller{Repo: serverRepo}
|
||||||
|
|
||||||
keyPath := os.Getenv("RSA_PRIVATE_KEY_PATH")
|
keyPath := os.Getenv("RSA_PRIVATE_KEY_PATH")
|
||||||
if keyPath == "" {
|
if keyPath == "" {
|
||||||
@@ -48,12 +40,29 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to create profile service: %v", err)
|
log.Fatalf("Failed to create profile service: %v", err)
|
||||||
}
|
}
|
||||||
// Хендлеры
|
|
||||||
|
modpacksStoragePath := os.Getenv("MODPACKS_STORAGE_PATH")
|
||||||
|
if modpacksStoragePath == "" {
|
||||||
|
log.Fatal("MODPACKS_STORAGE_PATH environment variable is not set")
|
||||||
|
}
|
||||||
|
janitorService := core.NewFileJanitorService(modpackRepo, modpacksStoragePath)
|
||||||
|
|
||||||
|
// --- Запуск фоновых задач ---
|
||||||
|
go serverPoller.Start(context.Background())
|
||||||
|
|
||||||
|
// --- Инициализация хендлеров ---
|
||||||
userHandler := &api.UserHandler{Service: userService}
|
userHandler := &api.UserHandler{Service: userService}
|
||||||
authHandler := &api.AuthHandler{Service: authService}
|
authHandler := &api.AuthHandler{Service: authService}
|
||||||
profileHandler := &api.ProfileHandler{Service: profileService}
|
profileHandler := &api.ProfileHandler{Service: profileService}
|
||||||
serverHandler := &api.ServerHandler{Repo: serverRepo}
|
serverHandler := &api.ServerHandler{Repo: serverRepo}
|
||||||
// --- Настраиваем роутер ---
|
launcherHandler := &api.LauncherHandler{ModpackRepo: modpackRepo}
|
||||||
|
modpackHandler := &api.ModpackHandler{
|
||||||
|
ModpackRepo: modpackRepo,
|
||||||
|
JanitorService: janitorService,
|
||||||
|
}
|
||||||
|
adminUserHandler := &api.AdminUserHandler{UserRepo: userRepo} // Этот хендлер мы создали для админских функций
|
||||||
|
|
||||||
|
// --- Настройка роутера ---
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.Use(middleware.Logger)
|
r.Use(middleware.Logger)
|
||||||
r.Use(middleware.Recoverer)
|
r.Use(middleware.Recoverer)
|
||||||
@@ -63,6 +72,10 @@ func main() {
|
|||||||
r.Post("/register", userHandler.Register)
|
r.Post("/register", userHandler.Register)
|
||||||
r.Post("/login", authHandler.Login)
|
r.Post("/login", authHandler.Login)
|
||||||
r.Get("/servers", serverHandler.GetServers)
|
r.Get("/servers", serverHandler.GetServers)
|
||||||
|
|
||||||
|
r.Route("/launcher", func(r chi.Router) {
|
||||||
|
r.Get("/modpacks/{name}/manifest", launcherHandler.GetModpackManifest)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
r.Route("/authserver", func(r chi.Router) {
|
r.Route("/authserver", func(r chi.Router) {
|
||||||
r.Post("/authenticate", authHandler.Authenticate)
|
r.Post("/authenticate", authHandler.Authenticate)
|
||||||
@@ -71,23 +84,24 @@ func main() {
|
|||||||
r.Post("/join", authHandler.Join)
|
r.Post("/join", authHandler.Join)
|
||||||
r.Get("/profile/{uuid}", profileHandler.GetProfile)
|
r.Get("/profile/{uuid}", profileHandler.GetProfile)
|
||||||
})
|
})
|
||||||
r.Route("/launcher", func(r chi.Router) {
|
|
||||||
r.Get("/modpacks/{name}/manifest", launcherHandler.GetModpackManifest)
|
|
||||||
})
|
|
||||||
|
|
||||||
// --- Защищенные роуты ---
|
// --- Защищенные роуты ---
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
r.Use(api.AuthMiddleware) // TODO: Заменить на AdminMiddleware
|
r.Use(api.AuthMiddleware)
|
||||||
|
|
||||||
r.Route("/api/user", func(r chi.Router) {
|
r.Route("/api/user", func(r chi.Router) {
|
||||||
r.Post("/skin", profileHandler.UploadSkin)
|
r.Post("/skin", profileHandler.UploadSkin)
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Route("/api/admin", func(r chi.Router) {
|
r.Route("/api/admin", func(r chi.Router) {
|
||||||
r.Use(api.AdminMiddleware)
|
r.Use(api.AdminMiddleware)
|
||||||
|
|
||||||
r.Route("/modpacks", func(r chi.Router) {
|
r.Route("/modpacks", func(r chi.Router) {
|
||||||
r.Post("/import", modpackHandler.ImportModpack)
|
r.Post("/import", modpackHandler.ImportModpack)
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Route("/users", func(r chi.Router) {
|
r.Route("/users", func(r chi.Router) {
|
||||||
|
// ИСПРАВЛЕНО: Используем adminUserHandler
|
||||||
r.Get("/", adminUserHandler.GetAllUsers)
|
r.Get("/", adminUserHandler.GetAllUsers)
|
||||||
r.Patch("/{id}/role", adminUserHandler.UpdateUserRole)
|
r.Patch("/{id}/role", adminUserHandler.UpdateUserRole)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,20 +1,24 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"gitea.mrixs.me/minecraft-platform/backend/internal/core"
|
||||||
"gitea.mrixs.me/minecraft-platform/backend/internal/core/importer"
|
"gitea.mrixs.me/minecraft-platform/backend/internal/core/importer"
|
||||||
"gitea.mrixs.me/minecraft-platform/backend/internal/database"
|
"gitea.mrixs.me/minecraft-platform/backend/internal/database"
|
||||||
"gitea.mrixs.me/minecraft-platform/backend/internal/models"
|
"gitea.mrixs.me/minecraft-platform/backend/internal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ModpackHandler struct {
|
type ModpackHandler struct {
|
||||||
ModpackRepo *database.ModpackRepository
|
ModpackRepo *database.ModpackRepository
|
||||||
|
JanitorService *core.FileJanitorService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ImportModpack обрабатывает загрузку и импорт модпака.
|
||||||
func (h *ModpackHandler) ImportModpack(w http.ResponseWriter, r *http.Request) {
|
func (h *ModpackHandler) ImportModpack(w http.ResponseWriter, r *http.Request) {
|
||||||
if err := r.ParseMultipartForm(512 << 20); err != nil { // 512 MB лимит
|
if err := r.ParseMultipartForm(512 << 20); err != nil { // 512 MB лимит
|
||||||
http.Error(w, "File too large", http.StatusBadRequest)
|
http.Error(w, "File too large", http.StatusBadRequest)
|
||||||
@@ -65,4 +69,7 @@ func (h *ModpackHandler) ImportModpack(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
fmt.Fprintf(w, "Modpack '%s' imported successfully with %d files.", modpack.DisplayName, len(files))
|
fmt.Fprintf(w, "Modpack '%s' imported successfully with %d files.", modpack.DisplayName, len(files))
|
||||||
|
|
||||||
|
// Запускаем очистку в фоне, чтобы не блокировать ответ
|
||||||
|
go h.JanitorService.CleanOrphanedFiles(context.Background())
|
||||||
}
|
}
|
||||||
|
|||||||
61
internal/core/file_janitor.go
Normal file
61
internal/core/file_janitor.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"gitea.mrixs.me/minecraft-platform/backend/internal/database"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileJanitorService отвечает за очистку осиротевших файлов.
|
||||||
|
type FileJanitorService struct {
|
||||||
|
ModpackRepo *database.ModpackRepository
|
||||||
|
StoragePath string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFileJanitorService создает новый экземпляр сервиса-уборщика.
|
||||||
|
func NewFileJanitorService(repo *database.ModpackRepository, storagePath string) *FileJanitorService {
|
||||||
|
return &FileJanitorService{
|
||||||
|
ModpackRepo: repo,
|
||||||
|
StoragePath: storagePath,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanOrphanedFiles сканирует хранилище и удаляет файлы, отсутствующие в БД.
|
||||||
|
func (s *FileJanitorService) CleanOrphanedFiles(ctx context.Context) {
|
||||||
|
log.Println("Janitor: Starting orphaned file cleanup...")
|
||||||
|
|
||||||
|
liveHashes, err := s.ModpackRepo.GetAllFileHashes(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Janitor: Error getting live hashes from DB: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
diskFiles, err := os.ReadDir(s.StoragePath)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Janitor: Error reading storage directory %s: %v", s.StoragePath, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
removedCount := 0
|
||||||
|
for _, file := range diskFiles {
|
||||||
|
if file.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fileName := file.Name()
|
||||||
|
if _, isLive := liveHashes[fileName]; !isLive {
|
||||||
|
filePath := filepath.Join(s.StoragePath, fileName)
|
||||||
|
if err := os.Remove(filePath); err != nil {
|
||||||
|
log.Printf("Janitor: Failed to remove orphaned file %s: %v", filePath, err)
|
||||||
|
} else {
|
||||||
|
log.Printf("Janitor: Removed orphaned file %s", filePath)
|
||||||
|
removedCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Janitor: Cleanup complete. Removed %d orphaned files.", removedCount)
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
// File: backend/internal/database/modpack_repository.go
|
|
||||||
package database
|
package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -81,3 +80,24 @@ func (r *ModpackRepository) GetModpackManifest(ctx context.Context, modpackName
|
|||||||
|
|
||||||
return manifest, nil
|
return manifest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAllFileHashes извлекает все уникальные хеши файлов из базы данных.
|
||||||
|
func (r *ModpackRepository) GetAllFileHashes(ctx context.Context) (map[string]struct{}, error) {
|
||||||
|
query := "SELECT DISTINCT file_hash FROM modpack_files"
|
||||||
|
rows, err := r.DB.Query(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
hashes := make(map[string]struct{})
|
||||||
|
for rows.Next() {
|
||||||
|
var hash string
|
||||||
|
if err := rows.Scan(&hash); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hashes[hash] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashes, rows.Err()
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user