134 lines
3.9 KiB
Go
134 lines
3.9 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
chimiddleware "github.com/go-chi/chi/v5/middleware"
|
|
"maintainarr/internal/config"
|
|
"maintainarr/internal/db"
|
|
"maintainarr/internal/handlers"
|
|
localmiddleware "maintainarr/internal/middleware"
|
|
"maintainarr/internal/models"
|
|
"maintainarr/internal/services"
|
|
"maintainarr/internal/views"
|
|
)
|
|
|
|
type App struct {
|
|
config config.Config
|
|
db *sql.DB
|
|
server *http.Server
|
|
scheduler *services.SchedulerService
|
|
}
|
|
|
|
func New() (*App, error) {
|
|
cfg := config.Load()
|
|
|
|
database, err := db.Open(cfg.DatabasePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
repo := services.NewRepository(database)
|
|
auth := services.NewAuthService(cfg.OrgName)
|
|
crypto, err := services.NewCryptoService(cfg.EncryptionKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sessions := services.NewSessionService(cfg.SessionKey)
|
|
nodes := services.NewNodeService(database, crypto)
|
|
renderer, err := views.NewRenderer()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
org, err := repo.EnsureOrganization(ctx, cfg.OrgName, cfg.DefaultTheme)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if org.ThemeMode == "" {
|
|
if err := repo.UpdateOrganizationTheme(ctx, org.Theme, cfg.DefaultMode); err != nil {
|
|
return nil, err
|
|
}
|
|
org.ThemeMode = cfg.DefaultMode
|
|
}
|
|
if err := repo.SeedDefaults(ctx, org.ID); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := repo.EnsurePrimaryAdmin(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
handler := handlers.New(repo, auth, sessions, nodes, renderer, org, cfg.BaseURL)
|
|
router := chi.NewRouter()
|
|
router.Use(chimiddleware.RequestID)
|
|
router.Use(chimiddleware.RealIP)
|
|
router.Use(chimiddleware.Recoverer)
|
|
router.Use(chimiddleware.Logger)
|
|
|
|
router.Handle("/static/*", http.StripPrefix("/static/", http.FileServer(http.Dir("web/static"))))
|
|
router.Get("/", handler.Home)
|
|
router.Get("/login", handler.LoginPage)
|
|
router.Post("/login", handler.Login)
|
|
router.Get("/login/otp", handler.LoginOTPPage)
|
|
router.Post("/login/otp", handler.LoginOTP)
|
|
router.Get("/register", handler.RegisterPage)
|
|
router.Post("/register", handler.Register)
|
|
router.Get("/logout", handler.Logout)
|
|
|
|
router.Group(func(protected chi.Router) {
|
|
protected.Use(localmiddleware.RequireAuth(sessions, repo))
|
|
protected.Get("/setup-2fa", handler.SetupOTPPage)
|
|
protected.Post("/setup-2fa", handler.SetupOTP)
|
|
protected.Get("/dashboard", handler.Dashboard)
|
|
protected.Get("/dashboard/nodes", handler.DashboardNodes)
|
|
protected.Get("/nodes/{nodeID}", handler.NodeOverview)
|
|
protected.Get("/nodes/{nodeID}/stats", handler.NodeStatsAPI)
|
|
protected.Get("/nodes/{nodeID}/console", handler.NodeConsole)
|
|
protected.Get("/nodes/{nodeID}/console/ws", handler.NodeConsoleWebSocket)
|
|
protected.Get("/groups", handler.GroupsPage)
|
|
protected.Get("/automations", handler.AutomationsPage)
|
|
protected.Get("/settings", handler.SettingsPage)
|
|
protected.Post("/settings/theme", handler.UpdateTheme)
|
|
|
|
protected.Group(func(editor chi.Router) {
|
|
editor.Use(localmiddleware.RequireRole(models.RoleEditor))
|
|
editor.Post("/groups", handler.CreateGroup)
|
|
editor.Post("/nodes", handler.CreateNode)
|
|
editor.Post("/nodes/{nodeID}/actions/{action}", handler.NodeAction)
|
|
editor.Post("/nodes/{nodeID}/commands", handler.NodeQuickCommand)
|
|
editor.Post("/nodes/{nodeID}/delete", handler.DeleteNode)
|
|
editor.Post("/automations", handler.CreateAutomation)
|
|
})
|
|
})
|
|
|
|
scheduler := services.NewSchedulerService(database, nodes)
|
|
if err := scheduler.Start(context.Background(), org.ID, cfg.RefreshCron); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &App{
|
|
config: cfg,
|
|
db: database,
|
|
server: &http.Server{
|
|
Addr: cfg.Address,
|
|
Handler: router,
|
|
ReadHeaderTimeout: 10 * time.Second,
|
|
},
|
|
scheduler: scheduler,
|
|
}, nil
|
|
}
|
|
|
|
func (a *App) Run() error {
|
|
fmt.Printf("Maintainarr listening on %s\n", a.config.Address)
|
|
return a.server.ListenAndServe()
|
|
}
|