3b26c4f5e1
- Added new AI onboarding and recommendation endpoints in the company handler for starting onboarding and retrieving ranked tender recommendations. - Introduced `StartAIOnboarding` and `GetAIRecommendations` methods in the company service to handle AI interactions. - Updated the company service constructor to include the AI recommendation client. - Enhanced the AI summarizer client with methods for onboarding and fetching recommendations. - Added response structures for onboarding and recommended tenders in the company form. This update enhances the tender management system by integrating AI capabilities for onboarding and tender recommendations, improving user experience and operational efficiency.
368 lines
14 KiB
Go
368 lines
14 KiB
Go
package router
|
|
|
|
import (
|
|
"tm/internal/assets"
|
|
"tm/internal/cms"
|
|
"tm/internal/company"
|
|
"tm/internal/company_category"
|
|
"tm/internal/contact"
|
|
"tm/internal/customer"
|
|
"tm/internal/dashboard"
|
|
"tm/internal/document_scraper"
|
|
"tm/internal/feedback"
|
|
"tm/internal/inquiry"
|
|
"tm/internal/kanban"
|
|
"tm/internal/notification"
|
|
"tm/internal/tender"
|
|
"tm/internal/tender_approval"
|
|
"tm/internal/user"
|
|
"tm/pkg/filestore"
|
|
"tm/pkg/security"
|
|
|
|
"github.com/labstack/echo/v4"
|
|
)
|
|
|
|
// SetupCSPSecurity registers global CSP middleware and the CSP report endpoint once.
|
|
func SetupCSPSecurity(e *echo.Echo) {
|
|
adminCSPConfig := security.DefaultCSPConfig()
|
|
adminCSPConfig.ScriptSrc = []string{"'self'"} // No unsafe-inline for admin
|
|
adminCSPConfig.StyleSrc = []string{"'self'"} // No unsafe-inline for admin
|
|
|
|
publicCSPConfig := security.DefaultCSPConfig()
|
|
|
|
e.Use(security.CSPMiddlewareForPaths("/admin", adminCSPConfig, "/api/v1", publicCSPConfig))
|
|
e.POST("/api/v1/csp-report", security.CSPReportHandler)
|
|
}
|
|
|
|
func RegisterAdminRoutes(e *echo.Echo, userHandler *user.Handler, companyHandler *company.Handler, customerHandler *customer.Handler, tenderHandler *tender.TenderHandler, feedbackHandler *feedback.Handler, tenderApprovalHandler *tender_approval.Handler, inquiryHandler *inquiry.Handler, categoryHandler *company_category.Handler, flagHandler *assets.Handler, notificationHandler *notification.NotificationHandler, contactHandler *contact.Handler, cmsHandler *cms.Handler, kanbanHandler *kanban.Handler, fileStoreHandler *filestore.Handler, dashboardHandler *dashboard.Handler, xssPolicy *security.XSSPolicy) {
|
|
adminV1 := e.Group("/admin/v1")
|
|
|
|
// Admin Users Routes
|
|
adminUsersGP := adminV1.Group("/users")
|
|
{
|
|
adminUsersGP.Use(userHandler.AuthMiddleware())
|
|
adminUsersGP.POST("", userHandler.CreateUser)
|
|
adminUsersGP.GET("", userHandler.ListUsers)
|
|
adminUsersGP.GET("/:id", userHandler.GetUserByID)
|
|
adminUsersGP.PUT("/:id", userHandler.UpdateUser)
|
|
adminUsersGP.DELETE("/:id", userHandler.DeleteUser)
|
|
adminUsersGP.PUT("/:id/status", userHandler.UpdateUserStatus)
|
|
adminUsersGP.POST("/:id/reset-password", userHandler.ResetUserPassword, userHandler.AdminMiddleware())
|
|
}
|
|
|
|
// Admin user profile
|
|
profileGP := adminV1.Group("/profile")
|
|
{
|
|
userAuthorizationGP := profileGP.Group("")
|
|
userAuthorizationGP.POST("/login", userHandler.Login)
|
|
userAuthorizationGP.POST("/refresh-token", userHandler.RefreshToken)
|
|
userAuthorizationGP.POST("/reset-password", userHandler.ResetPassword)
|
|
|
|
userProfileGP := profileGP.Group("")
|
|
userProfileGP.Use(userHandler.AuthMiddleware())
|
|
userProfileGP.GET("", userHandler.GetProfile)
|
|
userProfileGP.PUT("", userHandler.UpdateProfile)
|
|
userProfileGP.PUT("/change-password", userHandler.ChangePassword)
|
|
userProfileGP.DELETE("/logout", userHandler.Logout)
|
|
}
|
|
|
|
// Admin Companies Routes
|
|
companiesGP := adminV1.Group("/companies")
|
|
{
|
|
companiesGP.Use(userHandler.AuthMiddleware())
|
|
companiesGP.POST("", companyHandler.Create)
|
|
companiesGP.GET("", companyHandler.Search)
|
|
companiesGP.GET("/:id", companyHandler.Get)
|
|
companiesGP.PUT("/:id", companyHandler.Update)
|
|
companiesGP.DELETE("/:id", companyHandler.Delete)
|
|
companiesGP.PATCH("/:id/status", companyHandler.UpdateStatus)
|
|
companiesGP.PATCH("/:id/verification", companyHandler.UpdateVerification)
|
|
companiesGP.POST("/:id/documents", companyHandler.UploadDocuments)
|
|
}
|
|
|
|
// Admin Categories Routes
|
|
categoriesGP := adminV1.Group("/company-categories")
|
|
{
|
|
categoriesGP.Use(userHandler.AuthMiddleware())
|
|
categoriesGP.POST("", categoryHandler.Create)
|
|
categoriesGP.GET("", categoryHandler.Search)
|
|
categoriesGP.GET("/:id", categoryHandler.Get)
|
|
categoriesGP.PUT("/:id", categoryHandler.Update)
|
|
categoriesGP.DELETE("/:id", categoryHandler.Delete)
|
|
categoriesGP.PATCH("/:id/publish", categoryHandler.TogglePublish)
|
|
}
|
|
|
|
// Admin Customers Routes
|
|
customersGP := adminV1.Group("/customers")
|
|
{
|
|
customersGP.Use(userHandler.AuthMiddleware())
|
|
customersGP.POST("", customerHandler.CreateCustomer)
|
|
customersGP.GET("", customerHandler.Search)
|
|
customersGP.GET("/:id", customerHandler.GetCustomerByID)
|
|
customersGP.PUT("/:id", customerHandler.UpdateCustomer)
|
|
customersGP.DELETE("/:id", customerHandler.DeleteCustomer)
|
|
customersGP.PUT("/:id/status", customerHandler.UpdateStatus)
|
|
customersGP.POST("/:id/reset-password", customerHandler.ResetCustomerPassword)
|
|
customersGP.PATCH("/:id/role", customerHandler.AssignRole)
|
|
customersGP.PATCH("/:id/companies", customerHandler.AssignCompanies)
|
|
}
|
|
|
|
// Admin Tenders Routes
|
|
tendersGP := adminV1.Group("/tenders")
|
|
{
|
|
tendersGP.Use(userHandler.AuthMiddleware())
|
|
tendersGP.GET("", tenderHandler.Search)
|
|
tendersGP.GET("/ai-summaries", tenderHandler.GetAllAISummaries)
|
|
tendersGP.GET("/scraped-documents", tenderHandler.ListTendersWithScrapedDocuments)
|
|
tendersGP.GET("/:id", tenderHandler.Get)
|
|
tendersGP.PUT("/:id", tenderHandler.Update)
|
|
tendersGP.DELETE("/:id", tenderHandler.Delete)
|
|
tendersGP.GET("/:id/documents", tenderHandler.GetDocuments)
|
|
tendersGP.GET("/:id/documents/download", tenderHandler.DownloadDocuments)
|
|
tendersGP.GET("/:id/ai-summary", tenderHandler.GetAISummary)
|
|
tendersGP.POST("/:id/ai-summarize", tenderHandler.TriggerAISummarize)
|
|
tendersGP.POST("/:id/ai-analyze", tenderHandler.TriggerAIAnalyze)
|
|
tendersGP.POST("/:id/ai-translate", tenderHandler.TriggerAITranslate)
|
|
}
|
|
|
|
// Admin Feedback Routes
|
|
feedbackGP := adminV1.Group("/feedback")
|
|
{
|
|
feedbackGP.Use(userHandler.AuthMiddleware())
|
|
feedbackGP.GET("", feedbackHandler.Search)
|
|
feedbackGP.GET("/:id", feedbackHandler.Get)
|
|
feedbackGP.DELETE("/:id", feedbackHandler.Delete)
|
|
}
|
|
|
|
// Admin Tender-Approvals Routes
|
|
tenderApprovalGP := adminV1.Group("/tender-approvals")
|
|
{
|
|
tenderApprovalGP.Use(userHandler.AuthMiddleware())
|
|
tenderApprovalGP.GET("", tenderApprovalHandler.ListTenderApprovals)
|
|
tenderApprovalGP.GET("/:id", tenderApprovalHandler.GetTenderApproval)
|
|
tenderApprovalGP.GET("/stats", tenderApprovalHandler.GetTenderApprovalStats)
|
|
tenderApprovalGP.GET("/submission-mode/:submission_mode", tenderApprovalHandler.GetTenderApprovalsBySubmissionMode)
|
|
tenderApprovalGP.GET("/status/:status", tenderApprovalHandler.GetTenderApprovalsByStatus)
|
|
}
|
|
|
|
// Admin Inquiry Routes
|
|
inquiryGP := adminV1.Group("/inquiries")
|
|
{
|
|
inquiryGP.Use(userHandler.AuthMiddleware())
|
|
inquiryGP.GET("", inquiryHandler.SearchInquiries)
|
|
inquiryGP.GET("/:id", inquiryHandler.GetInquiryByID)
|
|
inquiryGP.PUT("/:id/status", inquiryHandler.UpdateInquiryStatus)
|
|
inquiryGP.DELETE("/:id", inquiryHandler.DeleteInquiry)
|
|
}
|
|
|
|
// Admin Flags Routes
|
|
flagsGP := adminV1.Group("/flags")
|
|
{
|
|
flagsGP.GET("/:country_code", flagHandler.AdminGetFlagSVG)
|
|
}
|
|
|
|
// Admin Notifications Routes
|
|
notificationsGP := adminV1.Group("/notifications")
|
|
{
|
|
notificationsGP.Use(userHandler.AuthMiddleware())
|
|
notificationsGP.POST("", notificationHandler.Send)
|
|
notificationsGP.GET("", notificationHandler.GetNotifications)
|
|
notificationsGP.GET("/view/:id", notificationHandler.ViewNotification)
|
|
notificationsGP.GET("/mark-seen/:id", notificationHandler.MarkSeen)
|
|
notificationsGP.GET("/my", notificationHandler.AdminNotifications)
|
|
}
|
|
|
|
// Admin Contact Routes
|
|
contactsGP := adminV1.Group("/contacts")
|
|
{
|
|
contactsGP.Use(userHandler.AuthMiddleware())
|
|
contactsGP.GET("", contactHandler.Search)
|
|
contactsGP.GET("/stats", contactHandler.GetStats)
|
|
contactsGP.GET("/:id", contactHandler.GetByID)
|
|
contactsGP.PUT("/:id/status", contactHandler.UpdateStatus)
|
|
contactsGP.DELETE("/:id", contactHandler.Delete)
|
|
}
|
|
|
|
// Admin CMS Routes
|
|
cmsGP := adminV1.Group("/cms")
|
|
{
|
|
cmsGP.Use(userHandler.AuthMiddleware())
|
|
cmsGP.POST("", cmsHandler.Create)
|
|
cmsGP.GET("", cmsHandler.Search)
|
|
cmsGP.GET("/:id", cmsHandler.GetByID)
|
|
cmsGP.PUT("/:id", cmsHandler.Update)
|
|
cmsGP.DELETE("/:id", cmsHandler.Delete)
|
|
}
|
|
|
|
// Kanban Routes — paths are /admin/v1/kanban/* (RegisterRoutes expects this /kanban group)
|
|
kanbanGP := adminV1.Group("/kanban")
|
|
{
|
|
kanbanGP.Use(userHandler.AuthMiddleware())
|
|
kanbanHandler.RegisterRoutes(kanbanGP)
|
|
}
|
|
|
|
// File Store Routes
|
|
filesGP := adminV1.Group("/files")
|
|
{
|
|
filesGP.Use(userHandler.AuthMiddleware())
|
|
filesGP.POST("/upload", fileStoreHandler.Upload)
|
|
filesGP.POST("/upload/batch", fileStoreHandler.UploadBatch)
|
|
filesGP.GET("", fileStoreHandler.ListFiles)
|
|
filesGP.GET("/:file_id/info", fileStoreHandler.GetInfo)
|
|
filesGP.GET("/:file_id/download", fileStoreHandler.Download)
|
|
filesGP.DELETE("/:file_id", fileStoreHandler.Delete)
|
|
}
|
|
|
|
// Dashboard Routes
|
|
dashboardGP := adminV1.Group("/dashboard")
|
|
{
|
|
dashboardGP.Use(userHandler.AuthMiddleware())
|
|
dashboardGP.GET("/summary", dashboardHandler.Summary)
|
|
dashboardGP.GET("/trend", dashboardHandler.Trend)
|
|
dashboardGP.GET("/countries", dashboardHandler.Countries)
|
|
dashboardGP.GET("/notice-types", dashboardHandler.NoticeTypes)
|
|
dashboardGP.GET("/closing-soon", dashboardHandler.ClosingSoon)
|
|
dashboardGP.GET("/recent", dashboardHandler.Recent)
|
|
dashboardGP.GET("/statistics", dashboardHandler.Statistics)
|
|
}
|
|
}
|
|
|
|
func RegisterPublicRoutes(e *echo.Echo, customerHandler *customer.Handler, tenderHandler *tender.TenderHandler, feedbackHandler *feedback.Handler, tenderApprovalHandler *tender_approval.Handler, companyHandler *company.Handler, inquiryHandler *inquiry.Handler, categoryHandler *company_category.Handler, flagHandler *assets.Handler, notificationHandler *notification.NotificationHandler, contactHandler *contact.Handler, cmsHandler *cms.Handler, kanbanHandler *kanban.Handler, fileStoreHandler *filestore.Handler, xssPolicy *security.XSSPolicy) {
|
|
v1 := e.Group("/api/v1")
|
|
|
|
customerGP := v1.Group("/profile")
|
|
{
|
|
customerGP.POST("/login", customerHandler.Login)
|
|
customerGP.POST("/refresh-token", customerHandler.RefreshToken)
|
|
|
|
// Forgot password routes
|
|
customerGP.POST("/forgot-password", customerHandler.RequestResetPassword)
|
|
customerGP.POST("/verify-otp", customerHandler.VerifyOTP)
|
|
customerGP.POST("/reset-password", customerHandler.ResetPassword)
|
|
|
|
profileGP := customerGP.Group("")
|
|
profileGP.Use(customerHandler.AuthMiddleware())
|
|
profileGP.GET("", customerHandler.GetProfile)
|
|
profileGP.PUT("/keywords", customerHandler.UpdateProfileKeywords)
|
|
profileGP.DELETE("/logout", customerHandler.Logout)
|
|
}
|
|
|
|
// Public Tenders Routes
|
|
tendersGP := v1.Group("/tenders")
|
|
tendersGP.Use(customerHandler.AuthMiddleware())
|
|
{
|
|
tendersGP.GET("", tenderHandler.GetPublicTenders)
|
|
tendersGP.GET("/recommend", tenderHandler.RecommendTenders)
|
|
tendersGP.GET("/details/:id", tenderHandler.GetPublicTenderDetails)
|
|
tendersGP.GET("/:id/documents", tenderHandler.GetDocuments)
|
|
tendersGP.GET("/:id/documents/download", tenderHandler.DownloadDocuments)
|
|
tendersGP.GET("/:id/document-summaries", tenderHandler.GetDocumentSummaries)
|
|
tendersGP.GET("/:id/ai-summary", tenderHandler.GetPublicAISummary)
|
|
tendersGP.POST("/:id/ai-translate", tenderHandler.TriggerPublicAITranslate)
|
|
}
|
|
|
|
// Public Feedback Routes
|
|
feedbackGP := v1.Group("/feedback")
|
|
{
|
|
feedbackGP.Use(customerHandler.AuthMiddleware())
|
|
feedbackGP.POST("", feedbackHandler.Toggle)
|
|
feedbackGP.GET("", feedbackHandler.PublicList)
|
|
feedbackGP.GET("/tender/:tender_id", feedbackHandler.PublicGetByTenderID)
|
|
feedbackGP.GET("/:id", feedbackHandler.PublicGet)
|
|
feedbackGP.GET("/stats/customer", feedbackHandler.GetCustomerStats)
|
|
feedbackGP.GET("/stats/company", feedbackHandler.GetCompanyStats)
|
|
}
|
|
|
|
// Public Tender Approvals Routes
|
|
tenderApprovalGP := v1.Group("/tender-approvals")
|
|
{
|
|
tenderApprovalGP.Use(customerHandler.AuthMiddleware())
|
|
tenderApprovalGP.POST("", tenderApprovalHandler.ToggleTenderApproval)
|
|
tenderApprovalGP.GET("", tenderApprovalHandler.PublicGetTenderApprovals)
|
|
tenderApprovalGP.GET("/:id", tenderApprovalHandler.GetPublicTenderApproval)
|
|
tenderApprovalGP.GET("/tender/:tender_id", tenderApprovalHandler.GetTenderApprovalByTenderAndCompany)
|
|
tenderApprovalGP.GET("/stats", tenderApprovalHandler.GetCompanyTenderApprovalStats)
|
|
}
|
|
|
|
// Public Company Routes
|
|
companiesGP := v1.Group("/companies")
|
|
{
|
|
companiesGP.Use(customerHandler.AuthMiddleware())
|
|
companiesGP.GET("", companyHandler.MyCompany)
|
|
}
|
|
|
|
// AI tender recommendation routes
|
|
recommendationGP := v1.Group("")
|
|
recommendationGP.Use(customerHandler.AuthMiddleware())
|
|
{
|
|
recommendationGP.POST("/onboarding", companyHandler.StartOnboarding)
|
|
recommendationGP.POST("/recommend", companyHandler.RecommendTenders)
|
|
}
|
|
|
|
// Public Inquiry Routes
|
|
inquiryGP := v1.Group("/inquiries")
|
|
{
|
|
inquiryGP.POST("", inquiryHandler.CreateInquiry)
|
|
}
|
|
|
|
// Public Flags Routes
|
|
flagsGP := v1.Group("/flags")
|
|
{
|
|
flagsGP.GET("/:country_code", flagHandler.GetFlagSVG)
|
|
}
|
|
|
|
// Public Notifications Routes
|
|
notificationsGP := v1.Group("/notifications")
|
|
{
|
|
notificationsGP.Use(customerHandler.AuthMiddleware())
|
|
notificationsGP.GET("", notificationHandler.CustomerNotifications)
|
|
notificationsGP.GET("/view/:id", notificationHandler.PublicViewNotification)
|
|
notificationsGP.GET("/mark", notificationHandler.PublicAllMarkSeen)
|
|
notificationsGP.GET("/mark/:id", notificationHandler.PublicMarkSeen)
|
|
}
|
|
|
|
// Public Contact Routes
|
|
contactsPublicGP := v1.Group("/contacts")
|
|
{
|
|
contactsPublicGP.POST("", contactHandler.Create)
|
|
}
|
|
|
|
// Public CMS Routes
|
|
cmsGP := v1.Group("/cms")
|
|
{
|
|
cmsGP.GET("/:key", cmsHandler.GetByKey)
|
|
}
|
|
|
|
// Kanban (mobile bid workflow board) — paths are /api/v1/kanban/*
|
|
kanbanGP := v1.Group("/kanban")
|
|
{
|
|
kanbanGP.Use(customerHandler.AuthMiddleware())
|
|
kanbanHandler.RegisterRoutes(kanbanGP)
|
|
}
|
|
|
|
// File Store Routes
|
|
publicFilesGP := v1.Group("/files")
|
|
{
|
|
publicFilesGP.Use(customerHandler.AuthMiddleware())
|
|
publicFilesGP.POST("/upload", fileStoreHandler.Upload)
|
|
publicFilesGP.POST("/upload/batch", fileStoreHandler.UploadBatch)
|
|
publicFilesGP.GET("", fileStoreHandler.ListFiles)
|
|
publicFilesGP.GET("/:file_id/info", fileStoreHandler.GetInfo)
|
|
publicFilesGP.GET("/:file_id/download", fileStoreHandler.Download)
|
|
publicFilesGP.DELETE("/:file_id", fileStoreHandler.Delete)
|
|
}
|
|
}
|
|
|
|
func RegisterDocumentScraperRoutes(e *echo.Echo, docScraperHandler *document_scraper.Handler, apiKey string) {
|
|
v1 := e.Group("/api/v1/scraper")
|
|
|
|
// Apply API key middleware to all document scraper routes
|
|
v1.Use(document_scraper.APIKeyMiddleware(apiKey))
|
|
|
|
// Document Scraper Routes
|
|
{
|
|
v1.GET("/tenders", docScraperHandler.ListPendingTenders)
|
|
v1.GET("/tenders/:notice_id", docScraperHandler.GetTenderByNoticeID)
|
|
}
|
|
}
|