Files
RentalManager/cmd/server/main.go
2026-04-11 12:12:07 +02:00

220 lines
8.9 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
"encoding/json"
"log"
"net/http"
"os"
"strings"
"github.com/gorilla/mux"
"github.com/f4bpo/rental-manager/internal/auth"
"github.com/f4bpo/rental-manager/internal/calendar"
"github.com/f4bpo/rental-manager/internal/category"
"github.com/f4bpo/rental-manager/internal/db"
"github.com/f4bpo/rental-manager/internal/document"
"github.com/f4bpo/rental-manager/internal/fiscal"
"github.com/f4bpo/rental-manager/internal/ical"
"github.com/f4bpo/rental-manager/internal/importer"
"github.com/f4bpo/rental-manager/internal/loan"
"github.com/f4bpo/rental-manager/internal/property"
"github.com/f4bpo/rental-manager/internal/transaction"
"github.com/f4bpo/rental-manager/web"
)
func corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
origin := r.Header.Get("Origin")
if strings.Contains(origin, "localhost") {
w.Header().Set("Access-Control-Allow-Origin", origin)
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
}
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusNoContent)
return
}
next.ServeHTTP(w, r)
})
}
func main() {
database, err := db.Init("./data/rental.db")
if err != nil {
log.Fatalf("failed to init database: %v", err)
}
defer database.Close()
if err := db.Migrate(database); err != nil {
log.Fatalf("failed to run migrations: %v", err)
}
propertyStore := property.NewStore(database)
transactionStore := transaction.NewStore(database)
documentStore := document.NewStore(database)
documentStore.Migrate()
calendarStore := calendar.NewStore(database)
userStore := auth.NewStore(database)
categoryStore := category.NewStore(database)
if userStore.Count() == 0 {
u, err := userStore.Create("admin@rental.local", "Administrateur", "admin1234")
if err != nil {
log.Printf("⚠ impossible de créer l'utilisateur par défaut: %v", err)
} else {
log.Printf("✓ Utilisateur par défaut créé — email: %s / mdp: admin1234", u.Email)
}
}
loanStore := loan.NewStore(database)
loanStore.Migrate()
// Seed tableaux d'amortissement si pas encore importés
loans, _ := loanStore.ListLoans("")
if len(loans) == 0 {
log.Println(" Tableaux d'amortissement non encore configurés.")
log.Println(" Ajoutez vos prêts dans la page Prêts et uploadez les échéances.")
}
icalService := ical.NewService(calendarStore, propertyStore)
icalService.StartSync()
authHandler := auth.NewHandler(userStore)
propertyHandler := property.NewHandler(propertyStore)
transactionHandler := transaction.NewHandler(transactionStore)
documentHandler := document.NewHandler(documentStore, "./data/documents")
calendarHandler := calendar.NewHandler(calendarStore)
fiscalHandler := fiscal.NewHandler(transactionStore, documentStore)
categoryHandler := category.NewHandler(categoryStore)
importHandler := importer.NewHandler(database)
loanHandler := loan.NewHandler(loanStore)
r := mux.NewRouter()
r.Use(corsMiddleware)
api := r.PathPrefix("/api").Subrouter()
api.HandleFunc("/auth/login", authHandler.Login).Methods("POST", "OPTIONS")
api.HandleFunc("/auth/logout", authHandler.Logout).Methods("POST", "OPTIONS")
api.HandleFunc("/auth/register", func(w http.ResponseWriter, r *http.Request) {
if userStore.Count() > 0 {
auth.Middleware(userStore)(http.HandlerFunc(authHandler.Register)).ServeHTTP(w, r)
return
}
authHandler.Register(w, r)
}).Methods("POST", "OPTIONS")
protected := api.NewRoute().Subrouter()
protected.Use(auth.Middleware(userStore))
// Profil & utilisateurs
protected.HandleFunc("/me", authHandler.Me).Methods("GET")
protected.HandleFunc("/me", authHandler.UpdateProfile).Methods("PUT")
protected.HandleFunc("/me/password", authHandler.UpdatePassword).Methods("PUT")
protected.HandleFunc("/users", authHandler.ListUsers).Methods("GET")
protected.HandleFunc("/users/{id}", authHandler.DeleteUser).Methods("DELETE")
// Catégories
protected.HandleFunc("/categories", categoryHandler.List).Methods("GET")
protected.HandleFunc("/categories", categoryHandler.Create).Methods("POST")
protected.HandleFunc("/categories/{id}", categoryHandler.Update).Methods("PUT")
protected.HandleFunc("/categories/{id}", categoryHandler.Delete).Methods("DELETE")
// Biens
protected.HandleFunc("/properties", propertyHandler.List).Methods("GET")
protected.HandleFunc("/properties", propertyHandler.Create).Methods("POST")
protected.HandleFunc("/properties/{id}", propertyHandler.Get).Methods("GET")
protected.HandleFunc("/properties/{id}", propertyHandler.Update).Methods("PUT")
protected.HandleFunc("/properties/{id}", propertyHandler.Delete).Methods("DELETE")
// Transactions
protected.HandleFunc("/transactions", transactionHandler.List).Methods("GET")
protected.HandleFunc("/transactions", transactionHandler.Create).Methods("POST")
protected.HandleFunc("/transactions/summary", transactionHandler.Summary).Methods("GET")
protected.HandleFunc("/transactions/monthly", transactionHandler.Monthly).Methods("GET")
protected.HandleFunc("/transactions/categories", transactionHandler.CategoryBreakdown).Methods("GET")
protected.HandleFunc("/transactions/{id}", transactionHandler.Get).Methods("GET")
protected.HandleFunc("/transactions/{id}", transactionHandler.Update).Methods("PUT")
protected.HandleFunc("/transactions/{id}", transactionHandler.Delete).Methods("DELETE")
protected.HandleFunc("/transactions/{id}/split", transactionHandler.SplitTransaction).Methods("POST")
// Import QIF
protected.HandleFunc("/import/preview", importHandler.Preview).Methods("POST")
protected.HandleFunc("/import/check", importHandler.Check).Methods("POST")
protected.HandleFunc("/import/qif", importHandler.Import).Methods("POST")
// Documents
protected.HandleFunc("/documents", documentHandler.List).Methods("GET")
protected.HandleFunc("/documents", documentHandler.Upload).Methods("POST")
protected.HandleFunc("/documents/export", documentHandler.Export).Methods("GET")
protected.HandleFunc("/documents/{id}", documentHandler.Get).Methods("GET")
protected.HandleFunc("/documents/{id}", documentHandler.Delete).Methods("DELETE")
protected.HandleFunc("/documents/{id}/download", documentHandler.Download).Methods("GET")
// Calendrier
protected.HandleFunc("/calendar", calendarHandler.List).Methods("GET")
protected.HandleFunc("/calendar", calendarHandler.CreateEvent).Methods("POST")
protected.HandleFunc("/calendar/stats", calendarHandler.Stats).Methods("GET")
protected.HandleFunc("/calendar/sync", func(w http.ResponseWriter, r *http.Request) {
results := icalService.SyncAll()
if results == nil {
results = []ical.SyncResult{}
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(results)
}).Methods("POST")
protected.HandleFunc("/calendar/{id}", calendarHandler.UpdateEvent).Methods("PUT")
protected.HandleFunc("/calendar/{id}", calendarHandler.DeleteEvent).Methods("DELETE")
// Prêts immobiliers
protected.HandleFunc("/loans", loanHandler.ListLoans).Methods("GET")
protected.HandleFunc("/loans", loanHandler.CreateLoan).Methods("POST")
// Routes statiques AVANT les routes avec {id}
protected.HandleFunc("/loans/split", loanHandler.GetSplitForAmount).Methods("GET")
protected.HandleFunc("/loans/upload-pdf", loanHandler.UploadPDF).Methods("POST")
protected.HandleFunc("/loans/create", loanHandler.CreateLoanManual).Methods("POST")
// Routes avec paramètre {id}
protected.HandleFunc("/loans/{id}", loanHandler.DeleteLoan).Methods("DELETE")
protected.HandleFunc("/loans/{id}/lines", loanHandler.GetLines).Methods("GET")
protected.HandleFunc("/loans/{id}/lines", loanHandler.UploadLines).Methods("POST")
protected.HandleFunc("/loans/{id}/split", loanHandler.SplitByDate).Methods("GET")
protected.HandleFunc("/loans/{id}/summary", loanHandler.AnnualSummary).Methods("GET")
protected.HandleFunc("/loans/{id}/reload", loanHandler.ReloadLines).Methods("POST")
// Export fiscal
protected.HandleFunc("/fiscal/export", fiscalHandler.Export).Methods("GET")
protected.HandleFunc("/fiscal/summary", fiscalHandler.Summary).Methods("GET")
// Arrêt du serveur
protected.HandleFunc("/shutdown", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
go func() { os.Exit(0) }()
}).Methods("POST")
// Frontend embarqué
assets, err := web.Assets()
if err != nil {
log.Fatalf("embed frontend: %v", err)
}
fileServer := http.FileServer(http.FS(assets))
r.PathPrefix("/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
path := strings.TrimPrefix(r.URL.Path, "/")
if path == "" {
path = "index.html"
}
if _, err := assets.Open(path); err != nil {
r.URL.Path = "/"
}
fileServer.ServeHTTP(w, r)
})
port := os.Getenv("PORT")
if port == "" {
port = "9000"
}
log.Printf("🏠 Rental Manager démarré sur http://localhost:%s", port)
log.Fatal(http.ListenAndServe(":"+port, r))
}