300 lines
15 KiB
Go
300 lines
15 KiB
Go
package main
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
|
|
"code.springupsoftware.com/cmc/cmc-sales/internal/cmc/db"
|
|
"code.springupsoftware.com/cmc/cmc-sales/internal/cmc/email"
|
|
"code.springupsoftware.com/cmc/cmc-sales/internal/cmc/handlers/attachments"
|
|
quotes "code.springupsoftware.com/cmc/cmc-sales/internal/cmc/handlers/quotes"
|
|
"code.springupsoftware.com/cmc/cmc-sales/internal/cmc/templates"
|
|
"github.com/go-co-op/gocron"
|
|
_ "github.com/go-sql-driver/mysql"
|
|
"github.com/gorilla/mux"
|
|
"github.com/joho/godotenv"
|
|
)
|
|
|
|
func main() {
|
|
// Load environment variables
|
|
if err := godotenv.Load(); err != nil {
|
|
log.Println("No .env file found")
|
|
}
|
|
|
|
// Database configuration
|
|
dbHost := getEnv("DB_HOST", "localhost")
|
|
dbPort := getEnv("DB_PORT", "3306")
|
|
dbUser := getEnv("DB_USER", "cmc")
|
|
dbPass := getEnv("DB_PASSWORD", "xVRQI&cA?7AU=hqJ!%au")
|
|
dbName := getEnv("DB_NAME", "cmc")
|
|
|
|
// Connect to database
|
|
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true", dbUser, dbPass, dbHost, dbPort, dbName)
|
|
database, err := sql.Open("mysql", dsn)
|
|
if err != nil {
|
|
log.Fatal("Failed to connect to database:", err)
|
|
}
|
|
defer database.Close()
|
|
|
|
// Test database connection
|
|
if err := database.Ping(); err != nil {
|
|
log.Fatal("Failed to ping database:", err)
|
|
}
|
|
|
|
log.Println("Connected to database successfully")
|
|
|
|
// Create queries instance
|
|
queries := db.New(database)
|
|
|
|
// Initialize template manager
|
|
tmpl, err := templates.NewTemplateManager("templates")
|
|
if err != nil {
|
|
log.Fatal("Failed to initialize templates:", err)
|
|
}
|
|
|
|
// Initialize email service
|
|
emailService := email.GetEmailService()
|
|
|
|
// Load handlers
|
|
quoteHandler := quotes.NewQuotesHandler(queries, tmpl, emailService)
|
|
attachmentHandler := attachments.NewAttachmentHandler(queries)
|
|
|
|
// Setup routes
|
|
r := mux.NewRouter()
|
|
goRouter := r.PathPrefix("/go").Subrouter()
|
|
|
|
// Static files
|
|
goRouter.PathPrefix("/static/").Handler(http.StripPrefix("/go/static/", http.FileServer(http.Dir("static"))))
|
|
|
|
// PDF files
|
|
goRouter.PathPrefix("/pdf/").Handler(http.StripPrefix("/go/pdf/", http.FileServer(http.Dir("webroot/pdf"))))
|
|
|
|
// Quote routes
|
|
goRouter.HandleFunc("/quotes", quoteHandler.QuotesOutstandingView).Methods("GET")
|
|
goRouter.HandleFunc("/quotes/send-reminder", quoteHandler.SendManualReminder).Methods("POST")
|
|
goRouter.HandleFunc("/quotes/disable-reminders", quoteHandler.DisableReminders).Methods("POST")
|
|
goRouter.HandleFunc("/quotes/enable-reminders", quoteHandler.EnableReminders).Methods("POST")
|
|
|
|
// Attachment routes
|
|
goRouter.HandleFunc("/attachments/upload", attachmentHandler.Create).Methods("POST")
|
|
goRouter.HandleFunc("/attachments/{id}", attachmentHandler.Get).Methods("GET")
|
|
goRouter.HandleFunc("/attachments/{id}", attachmentHandler.Delete).Methods("DELETE")
|
|
|
|
// The following routes are currently disabled:
|
|
/*
|
|
// API routes
|
|
api := r.PathPrefix("/api/v1").Subrouter()
|
|
api.HandleFunc("/customers", customerHandler.List).Methods("GET")
|
|
api.HandleFunc("/customers", customerHandler.Create).Methods("POST")
|
|
api.HandleFunc("/customers/{id}", customerHandler.Get).Methods("GET")
|
|
api.HandleFunc("/customers/{id}", customerHandler.Update).Methods("PUT")
|
|
api.HandleFunc("/customers/{id}", customerHandler.Delete).Methods("DELETE")
|
|
api.HandleFunc("/customers/search", customerHandler.Search).Methods("GET")
|
|
// Product routes
|
|
api.HandleFunc("/products", productHandler.List).Methods("GET")
|
|
api.HandleFunc("/products", productHandler.Create).Methods("POST")
|
|
api.HandleFunc("/products/{id}", productHandler.Get).Methods("GET")
|
|
api.HandleFunc("/products/{id}", productHandler.Update).Methods("PUT")
|
|
api.HandleFunc("/products/{id}", productHandler.Delete).Methods("DELETE")
|
|
api.HandleFunc("/products/search", productHandler.Search).Methods("GET")
|
|
// Purchase Order routes
|
|
api.HandleFunc("/purchase-orders", purchaseOrderHandler.List).Methods("GET")
|
|
api.HandleFunc("/purchase-orders", purchaseOrderHandler.Create).Methods("POST")
|
|
api.HandleFunc("/purchase-orders/{id}", purchaseOrderHandler.Get).Methods("GET")
|
|
api.HandleFunc("/purchase-orders/{id}", purchaseOrderHandler.Update).Methods("PUT")
|
|
api.HandleFunc("/purchase-orders/{id}", purchaseOrderHandler.Delete).Methods("DELETE")
|
|
api.HandleFunc("/purchase-orders/search", purchaseOrderHandler.Search).Methods("GET")
|
|
// Enquiry routes
|
|
api.HandleFunc("/enquiries", enquiryHandler.List).Methods("GET")
|
|
api.HandleFunc("/enquiries", enquiryHandler.Create).Methods("POST")
|
|
api.HandleFunc("/enquiries/{id}", enquiryHandler.Get).Methods("GET")
|
|
api.HandleFunc("/enquiries/{id}", enquiryHandler.Update).Methods("PUT")
|
|
api.HandleFunc("/enquiries/{id}", enquiryHandler.Delete).Methods("DELETE")
|
|
api.HandleFunc("/enquiries/{id}/undelete", enquiryHandler.Undelete).Methods("PUT")
|
|
api.HandleFunc("/enquiries/{id}/status", enquiryHandler.UpdateStatus).Methods("PUT")
|
|
api.HandleFunc("/enquiries/{id}/mark-submitted", enquiryHandler.MarkSubmitted).Methods("PUT")
|
|
api.HandleFunc("/enquiries/search", enquiryHandler.Search).Methods("GET")
|
|
// Document routes
|
|
api.HandleFunc("/documents", documentHandler.List).Methods("GET")
|
|
api.HandleFunc("/documents", documentHandler.Create).Methods("POST")
|
|
api.HandleFunc("/documents/{id}", documentHandler.Get).Methods("GET")
|
|
api.HandleFunc("/documents/{id}", documentHandler.Update).Methods("PUT")
|
|
api.HandleFunc("/documents/{id}/archive", documentHandler.Archive).Methods("PUT")
|
|
api.HandleFunc("/documents/{id}/unarchive", documentHandler.Unarchive).Methods("PUT")
|
|
api.HandleFunc("/documents/search", documentHandler.Search).Methods("GET")
|
|
// Address routes
|
|
api.HandleFunc("/addresses", addressHandler.List).Methods("GET")
|
|
api.HandleFunc("/addresses", addressHandler.Create).Methods("POST")
|
|
api.HandleFunc("/addresses/{id}", addressHandler.Get).Methods("GET")
|
|
api.HandleFunc("/addresses/{id}", addressHandler.Update).Methods("PUT")
|
|
api.HandleFunc("/addresses/{id}", addressHandler.Delete).Methods("DELETE")
|
|
api.HandleFunc("/addresses/customer/{customerID}", addressHandler.CustomerAddresses).Methods("GET")
|
|
// Attachment routes
|
|
api.HandleFunc("/attachments", attachmentHandler.List).Methods("GET")
|
|
api.HandleFunc("/attachments/archived", attachmentHandler.Archived).Methods("GET")
|
|
api.HandleFunc("/attachments", attachmentHandler.Create).Methods("POST")
|
|
api.HandleFunc("/attachments/{id}", attachmentHandler.Get).Methods("GET")
|
|
api.HandleFunc("/attachments/{id}", attachmentHandler.Update).Methods("PUT")
|
|
api.HandleFunc("/attachments/{id}", attachmentHandler.Delete).Methods("DELETE")
|
|
// Country routes
|
|
api.HandleFunc("/countries", countryHandler.List).Methods("GET")
|
|
api.HandleFunc("/countries", countryHandler.Create).Methods("POST")
|
|
api.HandleFunc("/countries/{id}", countryHandler.Get).Methods("GET")
|
|
api.HandleFunc("/countries/{id}", countryHandler.Update).Methods("PUT")
|
|
api.HandleFunc("/countries/{id}", countryHandler.Delete).Methods("DELETE")
|
|
api.HandleFunc("/countries/complete", countryHandler.CompleteCountry).Methods("GET")
|
|
// Status routes
|
|
api.HandleFunc("/statuses", statusHandler.List).Methods("GET")
|
|
api.HandleFunc("/statuses", statusHandler.Create).Methods("POST")
|
|
api.HandleFunc("/statuses/{id}", statusHandler.Get).Methods("GET")
|
|
api.HandleFunc("/statuses/{id}", statusHandler.Update).Methods("PUT")
|
|
api.HandleFunc("/statuses/{id}", statusHandler.Delete).Methods("DELETE")
|
|
api.HandleFunc("/statuses/json/{selectedId}", statusHandler.JsonList).Methods("GET")
|
|
// Line Item routes
|
|
api.HandleFunc("/line-items", lineItemHandler.List).Methods("GET")
|
|
api.HandleFunc("/line-items", lineItemHandler.Create).Methods("POST")
|
|
api.HandleFunc("/line-items/{id}", lineItemHandler.Get).Methods("GET")
|
|
api.HandleFunc("/line-items/{id}", lineItemHandler.Update).Methods("PUT")
|
|
api.HandleFunc("/line-items/{id}", lineItemHandler.Delete).Methods("DELETE")
|
|
api.HandleFunc("/line-items/document/{documentID}/table", lineItemHandler.GetTable).Methods("GET")
|
|
// Health check
|
|
api.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte(`{"status":"ok"}`))
|
|
}).Methods("GET")
|
|
// Recent activity endpoint
|
|
r.HandleFunc("/api/recent-activity", documentHandler.GetRecentActivity).Methods("GET")
|
|
// Page routes
|
|
r.HandleFunc("/", pageHandler.Home).Methods("GET")
|
|
// Customer pages
|
|
r.HandleFunc("/customers", pageHandler.CustomersIndex).Methods("GET")
|
|
r.HandleFunc("/customers/new", pageHandler.CustomersNew).Methods("GET")
|
|
r.HandleFunc("/customers/search", pageHandler.CustomersSearch).Methods("GET")
|
|
r.HandleFunc("/customers/{id}", pageHandler.CustomersShow).Methods("GET")
|
|
r.HandleFunc("/customers/{id}/edit", pageHandler.CustomersEdit).Methods("GET")
|
|
// Product pages
|
|
r.HandleFunc("/products", pageHandler.ProductsIndex).Methods("GET")
|
|
r.HandleFunc("/products/new", pageHandler.ProductsNew).Methods("GET")
|
|
r.HandleFunc("/products/search", pageHandler.ProductsSearch).Methods("GET")
|
|
r.HandleFunc("/products/{id}", pageHandler.ProductsShow).Methods("GET")
|
|
r.HandleFunc("/products/{id}/edit", pageHandler.ProductsEdit).Methods("GET")
|
|
// Purchase Order pages
|
|
r.HandleFunc("/purchase-orders", pageHandler.PurchaseOrdersIndex).Methods("GET")
|
|
r.HandleFunc("/purchase-orders/new", pageHandler.PurchaseOrdersNew).Methods("GET")
|
|
r.HandleFunc("/purchase-orders/search", pageHandler.PurchaseOrdersSearch).Methods("GET")
|
|
r.HandleFunc("/purchase-orders/{id}", pageHandler.PurchaseOrdersShow).Methods("GET")
|
|
r.HandleFunc("/purchase-orders/{id}/edit", pageHandler.PurchaseOrdersEdit).Methods("GET")
|
|
// Enquiry pages
|
|
r.HandleFunc("/enquiries", pageHandler.EnquiriesIndex).Methods("GET")
|
|
r.HandleFunc("/enquiries/new", pageHandler.EnquiriesNew).Methods("GET")
|
|
r.HandleFunc("/enquiries/search", pageHandler.EnquiriesSearch).Methods("GET")
|
|
r.HandleFunc("/enquiries/{id}", pageHandler.EnquiriesShow).Methods("GET")
|
|
r.HandleFunc("/enquiries/{id}/edit", pageHandler.EnquiriesEdit).Methods("GET")
|
|
// Document pages
|
|
r.HandleFunc("/documents", pageHandler.DocumentsIndex).Methods("GET")
|
|
r.HandleFunc("/documents/search", pageHandler.DocumentsSearch).Methods("GET")
|
|
r.HandleFunc("/documents/view/{id}", pageHandler.DocumentsView).Methods("GET")
|
|
r.HandleFunc("/documents/{id}", pageHandler.DocumentsShow).Methods("GET")
|
|
r.HandleFunc("/documents/pdf/{id}", documentHandler.GeneratePDF).Methods("GET")
|
|
// Address routes (matching CakePHP)
|
|
r.HandleFunc("/addresses", addressHandler.List).Methods("GET")
|
|
r.HandleFunc("/addresses/view/{id}", addressHandler.Get).Methods("GET")
|
|
r.HandleFunc("/addresses/add/{customerid}", addressHandler.Create).Methods("GET", "POST")
|
|
r.HandleFunc("/addresses/add_another/{increment}", addressHandler.AddAnother).Methods("GET")
|
|
r.HandleFunc("/addresses/remove_another/{increment}", addressHandler.RemoveAnother).Methods("GET")
|
|
r.HandleFunc("/addresses/edit/{id}", addressHandler.Update).Methods("GET", "POST")
|
|
r.HandleFunc("/addresses/customer_addresses/{customerID}", addressHandler.CustomerAddresses).Methods("GET")
|
|
// Attachment routes (matching CakePHP)
|
|
r.HandleFunc("/attachments", attachmentHandler.List).Methods("GET")
|
|
r.HandleFunc("/attachments/view/{id}", attachmentHandler.Get).Methods("GET")
|
|
r.HandleFunc("/attachments/archived", attachmentHandler.Archived).Methods("GET")
|
|
r.HandleFunc("/attachments/add", attachmentHandler.Create).Methods("GET", "POST")
|
|
r.HandleFunc("/attachments/edit/{id}", attachmentHandler.Update).Methods("GET", "POST")
|
|
r.HandleFunc("/attachments/delete/{id}", attachmentHandler.Delete).Methods("POST")
|
|
// Country routes (matching CakePHP)
|
|
r.HandleFunc("/countries", countryHandler.List).Methods("GET")
|
|
r.HandleFunc("/countries/view/{id}", countryHandler.Get).Methods("GET")
|
|
r.HandleFunc("/countries/add", countryHandler.Create).Methods("GET", "POST")
|
|
r.HandleFunc("/countries/edit/{id}", countryHandler.Update).Methods("GET", "POST")
|
|
r.HandleFunc("/countries/delete/{id}", countryHandler.Delete).Methods("POST")
|
|
r.HandleFunc("/countries/complete_country", countryHandler.CompleteCountry).Methods("GET")
|
|
// Status routes (matching CakePHP)
|
|
r.HandleFunc("/statuses", statusHandler.List).Methods("GET")
|
|
r.HandleFunc("/statuses/view/{id}", statusHandler.Get).Methods("GET")
|
|
r.HandleFunc("/statuses/add", statusHandler.Create).Methods("GET", "POST")
|
|
r.HandleFunc("/statuses/edit/{id}", statusHandler.Update).Methods("GET", "POST")
|
|
r.HandleFunc("/statuses/delete/{id}", statusHandler.Delete).Methods("POST")
|
|
r.HandleFunc("/statuses/json_list/{selectedId}", statusHandler.JsonList).Methods("GET")
|
|
// Line Item routes (matching CakePHP)
|
|
r.HandleFunc("/line_items/ajax_add", lineItemHandler.AjaxAdd).Methods("POST")
|
|
r.HandleFunc("/line_items/ajax_edit", lineItemHandler.AjaxEdit).Methods("POST")
|
|
r.HandleFunc("/line_items/ajax_delete/{id}", lineItemHandler.AjaxDelete).Methods("POST")
|
|
r.HandleFunc("/line_items/get_table/{documentID}", lineItemHandler.GetTable).Methods("GET")
|
|
r.HandleFunc("/line_items/edit/{id}", lineItemHandler.Update).Methods("GET", "POST")
|
|
r.HandleFunc("/line_items/add/{documentID}", lineItemHandler.Create).Methods("GET", "POST")
|
|
// HTMX endpoints
|
|
r.HandleFunc("/customers", customerHandler.Create).Methods("POST")
|
|
r.HandleFunc("/customers/{id}", customerHandler.Update).Methods("PUT")
|
|
r.HandleFunc("/customers/{id}", customerHandler.Delete).Methods("DELETE")
|
|
r.HandleFunc("/products", productHandler.Create).Methods("POST")
|
|
r.HandleFunc("/products/{id}", productHandler.Update).Methods("PUT")
|
|
r.HandleFunc("/products/{id}", productHandler.Delete).Methods("DELETE")
|
|
r.HandleFunc("/purchase-orders", purchaseOrderHandler.Create).Methods("POST")
|
|
r.HandleFunc("/purchase-orders/{id}", purchaseOrderHandler.Update).Methods("PUT")
|
|
r.HandleFunc("/purchase-orders/{id}", purchaseOrderHandler.Delete).Methods("DELETE")
|
|
r.HandleFunc("/enquiries", enquiryHandler.Create).Methods("POST")
|
|
r.HandleFunc("/enquiries/{id}", enquiryHandler.Update).Methods("PUT")
|
|
r.HandleFunc("/enquiries/{id}", enquiryHandler.Delete).Methods("DELETE")
|
|
r.HandleFunc("/enquiries/{id}/undelete", enquiryHandler.Undelete).Methods("PUT")
|
|
r.HandleFunc("/enquiries/{id}/status", enquiryHandler.UpdateStatus).Methods("PUT")
|
|
r.HandleFunc("/enquiries/{id}/mark-submitted", enquiryHandler.MarkSubmitted).Methods("PUT")
|
|
r.HandleFunc("/documents", documentHandler.Create).Methods("POST")
|
|
r.HandleFunc("/documents/{id}", documentHandler.Update).Methods("PUT")
|
|
r.HandleFunc("/documents/{id}/archive", documentHandler.Archive).Methods("PUT")
|
|
r.HandleFunc("/documents/{id}/unarchive", documentHandler.Unarchive).Methods("PUT")
|
|
*/
|
|
|
|
// Catch-all for everything else
|
|
r.PathPrefix("/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
w.Write([]byte("404 page not found"))
|
|
})
|
|
|
|
/* Cron Jobs */
|
|
go func() {
|
|
loc, err := time.LoadLocation("Australia/Sydney")
|
|
if err != nil {
|
|
log.Printf("Failed to load Sydney timezone: %v", err)
|
|
loc = time.UTC // fallback to UTC
|
|
}
|
|
s := gocron.NewScheduler(loc)
|
|
s.Every(1).Day().At("08:00").Do(func() {
|
|
// Checks quotes for reminders and expiry notices
|
|
quoteHandler.DailyQuoteExpirationCheck()
|
|
})
|
|
s.Every(1).Minute().Do(func() {
|
|
// Checks quotes for reminders and expiry notices
|
|
quoteHandler.DailyQuoteExpirationCheck()
|
|
})
|
|
s.StartAsync()
|
|
}()
|
|
|
|
// Start server
|
|
port := getEnv("PORT", "8080")
|
|
log.Printf("Starting server on port %s", port)
|
|
if err := http.ListenAndServe(":"+port, r); err != nil {
|
|
log.Fatal("Failed to start server:", err)
|
|
}
|
|
}
|
|
|
|
func getEnv(key, defaultValue string) string {
|
|
if value := os.Getenv(key); value != "" {
|
|
return value
|
|
}
|
|
return defaultValue
|
|
}
|