Files
tm_back/cmd/web/router/routes.go
T
Mazyar e5fa0dfe47
continuous-integration/drone/push Build is passing
Add GetScrapePortals endpoint and related service functionality
- 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.
2026-06-18 19:53:07 +03:30

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