e5fa0dfe47
continuous-integration/drone/push Build is passing
- Introduced the GetScrapePortals method in the AI pipeline handler to list document scraping portals supported by the Opplens AI service. - Updated the service layer to include GetScrapePortals, which retrieves the portals from the client and handles errors appropriately. - Enhanced the routes to register the new endpoint for retrieving scrape portals. - Added a new error type for invalid date ranges in the document scraper, improving validation and error handling. This update expands the AI pipeline capabilities, allowing for better management of document scraping portals within the tender management system.
391 lines
15 KiB
Go
391 lines
15 KiB
Go
package router
|
|
|
|
import (
|
|
"tm/internal/assets"
|
|
"tm/internal/ai_pipeline"
|
|
"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, aiPipelineHandler *ai_pipeline.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)
|
|
}
|
|
|
|
// AI Pipeline Routes (proxy to Opplens AI service)
|
|
aiPipelineGP := adminV1.Group("/ai-pipeline")
|
|
{
|
|
aiPipelineGP.Use(userHandler.AuthMiddleware())
|
|
aiPipelineGP.POST("/scrape/documents", aiPipelineHandler.ScrapeDocuments)
|
|
aiPipelineGP.POST("/scrape/documents/batch", aiPipelineHandler.ScrapeDocumentsBatch)
|
|
aiPipelineGP.GET("/scrape/portals", aiPipelineHandler.GetScrapePortals)
|
|
aiPipelineGP.POST("/summarize/batch", aiPipelineHandler.SummarizeBatch)
|
|
aiPipelineGP.POST("/translate/batch", aiPipelineHandler.TranslateBatch)
|
|
aiPipelineGP.POST("/sync", aiPipelineHandler.Sync)
|
|
aiPipelineGP.POST("/run", aiPipelineHandler.Run)
|
|
aiPipelineGP.POST("/run/batch", aiPipelineHandler.RunBatch)
|
|
aiPipelineGP.POST("/analyze", aiPipelineHandler.Analyze)
|
|
aiPipelineGP.POST("/daily-run", aiPipelineHandler.DailyRun)
|
|
aiPipelineGP.POST("/auto", aiPipelineHandler.Auto)
|
|
aiPipelineGP.GET("/last-run", aiPipelineHandler.GetLastRun)
|
|
aiPipelineGP.GET("/last-auto-run", aiPipelineHandler.GetLastAutoRun)
|
|
aiPipelineGP.GET("/report", aiPipelineHandler.GetReport)
|
|
aiPipelineGP.GET("/stats", aiPipelineHandler.GetStats)
|
|
aiPipelineGP.GET("/minio-stats", aiPipelineHandler.GetMinioStats)
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|