Files
tm_back/cmd/web/router/routes.go
T
Mazyar 3b26c4f5e1 Implement AI onboarding and recommendation features in company service
- 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.
2026-06-11 01:08:59 +03:30

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)
}
}