report-dashboardadmin/src/views/report-dashboard/api/internal/report/report_sites, report_domains, report_tasks collections)/api/reports/| Tab | Tên | Component |
|---|---|---|
statistics | Thống kê | ReportStatisticsTab.vue |
sites | Trang Báo Cáo | SiteReportsTab.vue |
domains | Quản lý miền | ReportDomainPool.vue |
pc-report | Báo Cáo PC | PCReportDashboard.vue |
IndexView.vue thiết lập setInterval mỗi 120 giây để tự động cập nhật tab Thống kê khi đang active.IndexView.vue
├── ReportDashboardHeader.vue ← DateRange picker + title
├── NTabs (4 tab)
│ ├── ReportStatisticsTab.vue ← Tab Thống kê
│ ├── SiteReportsTab.vue ← Tab Trang Báo Cáo
│ ├── ReportDomainPool.vue ← Tab Quản lý miền
│ └── PCReportDashboard.vue ← Tab Báo Cáo PC
├── TaskDetailModal.vue ← Modal chi tiết task (global)
└── DomainDetailModal.vue ← Modal chi tiết domain (global)useReportDashboardData (composables/useReportDashboardData.ts):reportTasks, chartData, detailedStats, keywordStatistics.fetchAllData() — Gọi song song Promise.all([reportStore.refreshAll, fetchReportTasks, fetchChartData, fetchDetailedStats]).isFetchingReportTasks, v.v.) để ngăn concurrent duplicate fetch.useReportDashboardOperations (composables/useReportDashboardOperations.ts):onChangePage, onChangePageSize, onExport.disableDate — Hàm kiểm tra ngày không hợp lệ cho DatePicker.IndexView.vue khởi tạo hai ref riêng biệt:dateRange qua header → chartDateRange được đồng bộ → fetchAllData() được gọi lại.| Endpoint | Mục đích |
|---|---|
GET /api/reports/stats?start_date=&end_date= | 4 số liệu thẻ thống kê đầu trang |
GET /api/reports/statistics/daily?start_date=&end_date= | Dữ liệu biểu đồ theo tuần |
GET /api/reports/tasks?page=1&page_size=10000&... | Chi tiết tasks để tính detailedStats |
GET /api/reports/tasks?page=N&page_size=M&... | Danh sách tasks phân trang |
GET /api/reports/stats:| Thẻ | Field | Mô tả |
|---|---|---|
| TỔNG BÁO CÁO | total_reports | Tổng số report task trong khoảng ngày |
| TỶ LỆ THÀNH CÔNG | success_rate | success_count / total * 100 |
| TỶ LỆ THẤT BẠI | failure_rate | failure_count / total * 100 |
| XỬ LÝ HÔM NAY | processed_today | Số task được xử lý ngày hôm nay |
GetReportStats:start_date/end_date → mặc định lấy hôm nay (00:00:00 đến 23:59:59 UTC).23:59:59.999999999 UTC để bao gồm toàn bộ cuối ngày.GET /api/reports/statistics/daily?start_date=&end_date=data.keywords — object có key là tên từ khóa, mỗi keyword có:{
"total_processed": 20,
"success_count": 10,
"failure_count": 5,
"dates": {
"2026-03-17": { "success_count": 2, "failure_count": 1, "total_processed": 3 },
...
},
"hours": { "0": {...}, "1": {...}, ... }
}fetchChartData):dates từ startDate đến endDate (mỗi ngày một điểm).success, failed, pending từ tất cả keyword.pending = total_processed - success_count - failure_count.Thành công, Thất bại, Đang chờ.fetchDetailedStats gọi GET /api/reports/tasks?page=1&page_size=10000 để lấy toàn bộ tasks rồi group phía client theo key domain|platform|keyword:COMPLETED → success++FAILED → failed++PENDING / IN_PROGRESS / PROCESSING → pending++SiteReportsTab.vue
├── SiteReportsHeader.vue ← Toolbar (refresh, create, bulk actions)
├── SiteReportsStats.vue ← 4 số liệu tổng quan
├── SiteReportsFilters.vue ← Tìm kiếm + filter category/status
├── NTabs (browser / email) ← Lọc theo loại site
├── SiteReportCard × N ← Card cho từng site
│
├── SitePromptEditor.vue ← Modal sửa AI Prompt
├── BulkPromptUpdateModal.vue ← Modal cập nhật prompt hàng loạt
├── CreateSiteModal.vue ← Modal tạo site mới
└── EditSiteModal.vue ← Modal sửa siteReportSite (MongoDB collection: report_sites)| Field | Kiểu | Mô tả |
|---|---|---|
_id | ObjectID | - |
name | string | Key định danh, normalized (lowercase, underscores), VD: google_safe_browsing |
display_name | string | Tên hiển thị, VD: Google Safe Browsing |
description | string | Mô tả ngắn |
icon | string | MDI icon name, VD: mdi:google |
url | string | URL form báo cáo (BROWSER_BASED) hoặc địa chỉ email (EMAIL_BASED) |
category | string | Nhóm: Security, Browser, Social Media, Email, Advertising... |
type | string | BROWSER_BASED hoặc EMAIL_BASED |
enabled | bool | Bật/tắt site này |
status | string | active hoặc inactive |
success_count | int | Tổng số lần báo cáo thành công |
failure_count | int | Tổng số lần thất bại |
success_rate | float64 | Tỷ lệ thành công (%) |
prompt | string | AI prompt cho GPT xử lý form |
gpt_config | GPTConfig | Cấu hình OpenAI model |
response_config | ResponseConfig | Định nghĩa các field cần điền vào form |
GPTConfig + prompt + ResponseConfig để tự động điền form báo cáo trên trình duyệt. GPT trả về JSON theo schema định nghĩa, worker map vào các field trên form.report_sites_defaults.go)| Name | DisplayName | URL | Category |
|---|---|---|---|
SPAMHAUS | Spamhaus | submit.spamhaus.org/submit/ | Security |
GOOGLE_SAFE_BROWSING | Google Safe Browsing | safebrowsing.google.com/... | Security |
ADGUARD | AdGuard | reports.adguard.com/... | Ad Blocker |
GOOGLE_ADS | Google Ads | support.google.com/... | Advertising |
TIKTOK | TikTok | tiktok.com/legal/report | Social Media |
GOOGLE_PLAY | Google Play | support.google.com/... | App Store |
US_CERT | US-CERT | us-cert.gov/report | Security |
ESET | ESET | eset.com/us/support/... | Security |
MICROSOFT_SMARTSCREEN | Microsoft SmartScreen | microsoft.com/... | Security |
TREND_MICRO | Trend Micro | trendmicro.com/... | Security |
X_TWITTER | X (Twitter) | help.twitter.com/forms/... | Social Media |
MICROSOFT | Microsoft | microsoft.com/... | Technology |
NETCRAFT | Netcraft | report.netcraft.com/ | Security |
MOZILLA_FIREFOX | Mozilla Firefox | mozilla.org/... | Browser |
| Name | DisplayName | Email Address |
|---|---|---|
KASPERSKY_EMAIL | Kaspersky | newvirus@kaspersky.com |
ESET_EMAIL | ESET | samples@eset.com |
AVAST_EMAIL | Avast | virus@avast.com |
BITDEFENDER_EMAIL | Bitdefender | virus-submission@bitdefender.com |
MCAFEE_EMAIL | McAfee | virus_research@mcafee.com |
SOPHOS_EMAIL | Sophos | samples@sophos.com |
OPENPHISH_EMAIL | OpenPhish | submit@openphish.com |
MICROSOFT_EMAIL | Microsoft | abuse@microsoft.com |
GOOGLE_EMAIL | spam@google.com | |
PAYPAL_EMAIL | PayPal | spoof@paypal.com |
GetReportSites load từ MongoDB, nó merge với getDefaultReportSites().display_name / description / icon → lấy giá trị từ default.name được normalize bằng normalizeSiteName():"Google Safe Browsing!" → google_safe_browsing.| Button | Handler | API |
|---|---|---|
| Bật tất cả | enableAllSites | POST /reports/sites/toggle-all {enabled: true} |
| Tắt tất cả | disableAllSites | POST /reports/sites/toggle-all {enabled: false} |
| Bật Browser | handleEnableBrowser | POST /reports/sites/toggle-by-type {type:"BROWSER_BASED", enabled:true} |
| Tắt Browser | handleDisableBrowser | POST /reports/sites/toggle-by-type {type:"BROWSER_BASED", enabled:false} |
| Bật Email | handleEnableEmail | POST /reports/sites/toggle-by-type {type:"EMAIL_BASED", enabled:true} |
| Tắt Email | handleDisableEmail | POST /reports/sites/toggle-by-type {type:"EMAIL_BASED", enabled:false} |
| Cập nhật prompt hàng loạt | showBulkPromptModal | POST /reports/sites/bulk-update-prompts |
report_domains).ReportDomain (MongoDB: report_domains)| Field | Kiểu | Mô tả |
|---|---|---|
_id | ObjectID | - |
domain | string | URL đầy đủ, bắt đầu bằng http:// hoặc https:// |
keyword | string | Từ khóa liên quan (bắt buộc nếu type = google_ads) |
type | string | google_ads hoặc normal |
status | string | pending / processing / ready / completed |
image_urls | []string | URLs screenshot ảnh (do worker upload) |
video_urls | []string | URLs screenshot video |
third_service_task_id | *string | Task ID từ service bên thứ 3 |
history_domain_id | *int64 | ID từ bảng lịch sử (MySQL) |
task_group_id | *string | ID nhóm task |
profile_id | *string | Profile được gán |
proxy_id | *string | Proxy được gán |
flow | *string | Loại flow xử lý |
engine | *string | Engine sử dụng |
report_pages | []string | Danh sách site được chọn để báo cáo |
created_at | time | - |
updated_at | time | - |
statusstatus của domain dựa trên trạng thái tất cả report_tasks liên kết:Nếu có task IN_PROGRESS/PROCESSING → domain.status = "processing"
Nếu có task PENDING và domain đang "ready" → domain.status = "ready"
Nếu có task PENDING → domain.status = "pending"
Nếu tất cả task đã xong (COMPLETED/FAILED) → domain.status = "completed"
Không có task nào → giữ nguyên status từ DBreport_tasks.report_domain_id == domain._idreport_tasks.domain == domain.domain AND report_tasks.keyword == domain.keywordGetReportDomainscreated_at DESC. Timeout: 30 seconds.status = "deleted":report_tasks liên quan qua CancelTasksByDomainID.domain, keyword, type.POST /reports/bulk-update với ids và fields.GET /reports/domains/:id. Hiển thị:report_tasks liên kết (với platform_display_name được lookup từ report_sites).profiles theo assigned_profile_id.servers table theo server_id.PCReportDashboard.vue
├── PCReportHeader.vue ← Title + nút Export CSV
├── SummaryStatisticsCard.vue ← Bảng tổng hợp Overall + Today + Monthly
├── DailyStatsCard.vue ← Thống kê hôm nay
├── BrandStatisticsTable.vue ← Bảng theo từng Brand
└── DailyStatisticsTable.vue ← Bảng 10 ngày gần nhấtGET /api/reports/pc-report?start_date=&end_date=PCReportData (Go struct)total_ads_found — Tổng quảng cáo đã phát hiệntotal_ad_reports — Tổng báo cáo đã gửitotal_ads_rip — Tổng quảng cáo đã bị chếtad_accounts_count — Tổng số ad accountremaining_accounts — Số account còn sốngdead_accounts — Số account đã chếtkeywords_count — Tổng keywordsactive_keywords — Keywords đang activereport_success_rate — Tỷ lệ report thành công (%)completed_reports — Số report đã hoàn thànhfailed_reports — Số report thất bạiads_detected, reports_count, new_dead_ads, remaining_ads, dead_accounts_percent.PCReportHeader có nút export → gọi usePCReportExport.exportPCReport(start, end):GET /api/reports/pc-report/csv?start_date=&end_date=&brand=&format=csvContent-Disposition: attachment; filename=pc_report_{date}.csv.format=csv → GeneratePCReportCSV() — Full report với tất cả sections.format=simple → GenerateSimpleCSV() — Chỉ số liệu cốt lõi.[Admin] Tạo domain tại tab Quản lý miền
↓
[Cron Job / Worker] Lấy task từ MongoDB
GET /webhook/report/tasks?server_ip=...&limit=5
↓
[API] RequestReportTask():
- GetAndUpdatePendingTasks (MongoDB: report_tasks, status=PENDING)
- Set task status = IN_PROGRESS
- Trả về task với webhookURL, proxyURL, profileURL
↓
[Worker/tools_v2] Nhận task
- Gọi proxyURL để lấy proxy sạch
- Gọi profileURL để lấy browser profile
- Nếu BROWSER_BASED:
→ Mở trình duyệt với proxy + profile
→ Dùng GPT (prompt + response_config) để phân tích form báo cáo
→ Điền form, submit
→ Screenshot kết quả
- Nếu EMAIL_BASED:
→ Gửi email đến địa chỉ trong site.url
→ Đính kèm thông tin domain vi phạm
↓
[Worker] Gửi callback kết quả về API
POST /webhook/report/callback/report
Body: { task_id, result: { success, message, screenshot_url, ... } }
↓
[API] Cập nhật report_task:
- status = COMPLETED hoặc FAILED
- completed_at, execution_time, result
- Nếu thành công: cập nhật report_site.success_count++
- Nếu thất bại: report_site.failure_count++
↓
[Frontend] Hiển thị kết quả trong tab Thống kê và Quản lý miền| Method | Endpoint | Mô tả |
|---|---|---|
GET | /reports/tasks | Danh sách tasks phân trang. Params: page, page_size, domain, keyword, platform, status, start_date, end_date |
GET | /reports/tasks/monitoring | Monitoring tasks đang active |
GET | /reports/tasks/active | Danh sách tasks đang xử lý |
GET | /reports/tasks/:id | Chi tiết một task |
GET | /reports/stats | 4 số liệu thống kê tổng quan |
GET | /reports/chart-data | Dữ liệu biểu đồ, param type=tasks |
| Method | Endpoint | Mô tả |
|---|---|---|
GET | /reports/sites | Danh sách tất cả sites |
POST | /reports/sites | Tạo site mới. Body: {name, display_name, type, url, enabled, prompt, gpt_config, response_config} |
PUT | /reports/sites/:site_name | Cập nhật site |
DELETE | /reports/sites/:site_name | Xóa site |
POST | /reports/sites/toggle | Bật/tắt một site. Body: {site_name, enabled} |
POST | /reports/sites/toggle-all | Bật/tắt tất cả. Body: {enabled} |
POST | /reports/sites/toggle-by-type | Bật/tắt theo type. Body: {type, enabled} |
POST | /reports/sites/bulk-update-prompts | Cập nhật prompt hàng loạt. Body: {site_names, prompt} |
GET | /reports/sites/stats | Thống kê tổng hợp sites |
GET | /reports/sites/:site_name/config | Full config (prompt + gpt_config + response_config) |
GET | /reports/sites/:site_name/prompt | Lấy prompt |
PUT | /reports/sites/:site_name/prompt | Cập nhật prompt. Body: {prompt} |
GET | /reports/sites/:site_name/gpt-config | Lấy GPT config |
PUT | /reports/sites/:site_name/gpt-config | Cập nhật GPT config. Body: {model, temperature, max_tokens, response_format} |
GET | /reports/sites/:site_name/response-config | Lấy response config |
PUT | /reports/sites/:site_name/response-config | Cập nhật response config |
GET | /reports/vendors/enabled | Lấy email vendors đang enabled |
| Method | Endpoint | Mô tả |
|---|---|---|
GET | /reports/domains | Danh sách domains. Params: page, page_size, domain, keyword, type, status, exclude_deleted |
GET | /reports/domains/:id | Chi tiết domain + tất cả tasks liên kết |
POST | /reports/domains | Thêm domain mới |
GET | /reports/statistics/daily | Thống kê theo ngày/keyword. Default: 7 ngày gần nhất |
| Method | Endpoint | Mô tả |
|---|---|---|
GET | /reports/analytics/keywords | Phân tích theo keyword. Params: keyword, page, page_size, start_date, end_date |
GET | /reports/analytics/top-keywords | Top keywords. Param: limit (max 20, default 5) |
GET | /reports/analytics/stats | Thống kê analytics tổng hợp |
| Method | Endpoint | Mô tả |
|---|---|---|
GET | /reports/pc-report | JSON data cho tab Báo cáo PC |
GET | /reports/pc-report/csv | Export CSV. Params: start_date, end_date, brand, format |
GET | /reports/pc-report/json | JSON format cho PC report |
| Method | Endpoint | Mô tả |
|---|---|---|
POST | /reports/bulk-update | Cập nhật hàng loạt domains. Body: {ids: [], fields: {}} |
admin/src/stores/reportStore.tsIndexView.vue thông qua storeToRefs:reportStore.refreshAll(startTs, endTs, silent):GET /reports/stats để cập nhật stats.silent = true → không hiển thị loading spinner.report_sitesname (indexed).report_domainsdomain + keyword. Sort mặc định: created_at DESC.status = "deleted".report_tasksreport_domain_id — Liên kết với report_domains._id (string, không phải ObjectID reference).platform — Tên site (khớp với report_sites.name).report_type — BROWSER_BASED hoặc EMAIL_BASED.status — PENDING → IN_PROGRESS → COMPLETED / FAILED.assigned_profile_id — Profile email được dùng để submit form.server_id — Server thực thi task (liên kết MySQL servers.id).result — Kết quả dạng JSON: { success, message, screenshot_url, report_id, ... }.profilesGetReportDomainByID, backend batch-fetch profiles để map assigned_profile_id → email.ReportDashboardService — Service chính, xử lý:reportSiteRepo → MongoDB report_sites)report_domains)reportTaskRepo → MongoDB report_tasks)servers table qua mysqlClientPCReportService — Service riêng cho tab Báo Cáo PC:Service (legacy) — Service cũ cho GeneratePCReport, vẫn dùng cho endpoint /pc-report/csv.SiteReportsTabuseSiteReportsFiltering là computed property, filter phía client:currentTab = 'browser' → chỉ hiển thị type = BROWSER_BASED.currentTab = 'email' → chỉ hiển thị type = EMAIL_BASED.ReportDomainPoolfilters.type hoặc filters.status thay đổi (không cần click nút Filter).fetchDetailedStats dùng page_size=10000 để load toàn bộ rồi group phía client — đây là điểm cần lưu ý về hiệu năng khi data lớn.api/internal/report/handler/email_report_handler.gonet/http standard library (KHÔNG dùng Fiber như các handler khác):GET /api/reports/email/statsGET /api/reports/email/stats/platformGET /api/reports/email/stats/vendorPOST /api/reports/email/callbackGET /api/reports/email/tasksGET /api/reports/email/tasks/{id}GET /api/reports/email/analyticsGetEmailReportTasks trả về mock task với platform KASPERSKY_EMAIL.ProcessEmailReportCallback xử lý callback từ worker email:task_id + result từ body.ValidateEmailReportResult(result) — kiểm tra dữ liệu hợp lệ.ProcessEmailReportTask(taskID, result) — update MongoDB.LogEmailReportResult(taskID, result) — ghi log debug.Worker gọi: GET /reports/sites/{site_name}/config
→ Trả về SiteFullConfigDTO:
{
site_name: "google_safe_browsing",
prompt: "You are filling a phishing report form...",
gpt_config: { model: "gpt-4o-mini", temperature: 0.1, max_tokens: 500, response_format: "json" },
response_config: {
required_fields: ["url", "description"],
form_fields: {
"url": { type: "text", required: true, max_length: 500 },
"description": { type: "textarea", required: true, max_length: 2000 }
}
}
}prompt + domain URL vào GPT API.{ "url": "...", "description": "..." }.response_config.form_fields.handler.go set Cache-Control: no-cache cho endpoint này để đảm bảo worker luôn lấy config mới nhất.admin/src/views/report-dashboard/
├── IndexView.vue
├── composables/
│ ├── useReportDashboardData.ts
│ └── useReportDashboardOperations.ts
└── components/
├── report-dashboard/
│ ├── ReportDashboardHeader.vue
│ ├── ReportStatisticsTab.vue
│ └── (sub-components chart, stats card...)
├── site-reports/
│ ├── SiteReportsHeader.vue
│ ├── SiteReportsStats.vue
│ ├── SiteReportsFilters.vue
│ ├── SiteReportCard.vue
│ ├── SitePromptEditor.vue
│ ├── BulkPromptUpdateModal.vue
│ ├── CreateSiteModal.vue
│ ├── EditSiteModal.vue
│ └── composables/
│ ├── useSiteReportsData.ts
│ ├── useSiteReportsOperations.ts
│ └── useSiteReportsFiltering.ts
├── report-domain-pool/
│ ├── ReportDomainPoolHeader.vue
│ ├── ReportDomainPoolStats.vue
│ ├── ReportDomainPoolFilters.vue
│ └── composables/
│ ├── useReportDomainPoolColumns.ts
│ ├── useReportDomainPoolData.ts
│ └── useReportDomainPoolStats.ts
├── pc-report/
│ ├── PCReportHeader.vue
│ ├── SummaryStatisticsCard.vue
│ ├── DailyStatsCard.vue
│ ├── BrandStatisticsTable.vue
│ ├── DailyStatisticsTable.vue
│ └── composables/
│ ├── usePCReportData.ts
│ ├── usePCReportExport.ts
│ └── usePCReportColumns.ts
├── SiteReportsTab.vue
├── ReportDomainPool.vue
├── PCReportDashboard.vue
├── TaskDetailModal.vue
├── DomainDetailModal.vue
├── AddDomainModal.vue
├── EditDomainModal.vue
└── ViewDomainModal.vueapi/internal/report/
├── report.go ← Structs: PCReportData, AdData, AccountData, ReportRequest
├── csv_generator.go ← GeneratePCReportCSV, GenerateSimpleCSV
├── data_analysis.go ← Utility functions phân tích dữ liệu
├── dto/ ← Data Transfer Objects
├── handler/
│ ├── handler.go ← Route registration + Handler struct
│ ├── report_tasks_handlers.go ← GetReportTasks, GetReportStats, GetChartData
│ ├── report_site_handlers.go ← CRUD sites + toggle + bulk update prompt
│ ├── report_domain_handlers.go ← CRUD domains + daily statistics
│ ├── report_analytics_handlers.go ← Keyword analytics
│ ├── report_pc_handlers.go ← PC Report JSON endpoint
│ ├── report_dashboard_handler.go ← Handler struct definition
│ ├── report_task_monitoring_handlers.go
│ ├── report_utility_handlers.go
│ ├── request_report_task_handler.go ← Worker task request endpoint
│ └── email_report_handler.go ← Email callback (net/http)
├── model/
│ ├── report_site.go ← ReportSite, GPTConfig, ResponseConfig, FieldConfig
│ └── report_domain.go ← ReportDomain
├── service/
│ ├── service.go ← ReportDashboardService struct + DI
│ ├── report_sites.go ← Site CRUD + toggle operations
│ ├── report_sites_defaults.go ← 24 default sites (hardcoded)
│ ├── report_domains.go ← Domain CRUD + status computation
│ ├── report_tasks.go ← Task queries
│ ├── request_report_task.go ← Worker task dispatch logic
│ ├── task_detail.go ← Task detail with profile/server lookup
│ ├── task_monitoring.go ← Active task monitoring
│ ├── email_report_service.go ← Email-based report service
│ ├── pc_report.go ← PC Report aggregation (legacy)
│ ├── pc_report_service.go ← PCReportService
│ ├── pc_report_calculator.go ← Brand/daily stats calculation
│ ├── keyword_analytics.go ← Keyword analytics
│ ├── report_stats.go ← GetReportStats
│ └── ...các file helper khác
├── repository/ ← MongoDB repository layer
├── webhook/ ← Webhook callback handlers
├── cron/ ← Cron jobs (task scheduling)
└── utils/ ← Utility functionsreport_site_handlers.go, tất cả site_name param từ URL đều được decode qua url.QueryUnescape() trước khi xử lý. Điều này xử lý trường hợp tên site chứa ký tự đặc biệt khi truyền qua URL.RequestReportTask(serverIP, limit) tự động nhân limit lên 3 trước khi query:GetReportDomainByID), service thực hiện:profiles (MongoDB) → lấy email từ assigned_profile_id.servers (MySQL servers table) → lấy name và ip từ server_id.mongoDb và mysqlClient.GetReportDomainByID, platformDisplayNameCache là map in-memory trong scope của một request để tránh query MongoDB nhiều lần cho cùng một platform name.status được tính toán on-the-fly mỗi lần request từ trạng thái các tasks liên quan. Không có mechanism locking — nếu nhiều request đồng thời, có thể có race condition ngắn.GET /reports/statistics/dailyGetDailyStatisticsGetWeeklyReportMetricsByKeywordAndDate(ctx, startDate, endDate){
"success": true,
"data": {
"keywords": {
"từ khóa A": {
"total_processed": 20,
"success_count": 10,
"failure_count": 5,
"dates": {
"2026-03-17": {
"total_processed": 5,
"success_count": 3,
"failure_count": 1
}
},
"hours": {
"0": { "success_count": 1, "failure_count": 0 },
"14": { "success_count": 2, "failure_count": 1 }
}
},
"từ khóa B": { ... }
}
}
}dd/MM format)SUM(success_count) qua tất cả keyword trong ngày đóSUM(failure_count)SUM(total_processed - success - failure)api/internal/report/cron/report_domains với status = "ready".report_sites với enabled = true.report_task mới trong MongoDB với:status = "PENDING"platform = site.namereport_type = site.type (BROWSER_BASED hoặc EMAIL_BASED)domain = domain.domainkeyword = domain.keywordscheduled_for = time.Now()unique_key = domain + "|" + site.name + "|" + datepriority Ưu tiên xử lý (int, cao hơn = ưu tiên hơn)report_domains.status = "processing".unique_key ngăn tạo task trùng lặp cho cùng một domain+site trong cùng một ngày.tools_v2 Worker node (server 192.168.1.x)
POST /webhook/report/tasks?server_ip=192.168.1.x&limit=5
RequestReportTask(serverIP="192.168.1.x", limit=5)
Request 15 tasks (53) từ MongoDB
Filter: status=PENDING, sorted by priority DESC, created_at ASC
Update: status=IN_PROGRESS, server_id=serverID
Return 15 task objects (sau khi validate b ỏ garbage)webhook_url URL để callback kết quả về: {WEBHOOK_HOST}/webhook/report/callback/reportproxy_url URL lấy proxy: {WEBHOOK_HOST}/webhook/check-ipprofile_url URL lấy profile: {WEBHOOK_HOST}/webhook/profile/get-random-profile?server_id=1&used_for=reportPOST /webhook/report/callback/reportapi/internal/report/webhook/{
"task_id": "680a1b2c3d4e5f6a7b8c9d0e",
"status": "COMPLETED",
"result": {
"success": true,
"message": "Report submitted successfully",
"platform": "GOOGLE_SAFE_BROWSING",
"report_type": "BROWSER_BASED",
"submitted_at": "2026-03-23T07:00:00Z",
"url": "https://malware-site.com",
"report_id": "GS-12345",
"response_time": 3200,
"screenshot_url": "https://storage.example.com/screens/abc123.png",
"screenshot_key": "screens/abc123.png"
},
"execution_time": 15000,
"proxy_id": "proxy_abc123",
"profile_id": "profile_xyz789"
}{
"task_id": "680a1b2c3d4e5f6a7b8c9d0f",
"status": "COMPLETED",
"result": {
"success": true,
"message": "Email reports sent to 2/3 recipients",
"platform": "KASPERSKY_EMAIL",
"report_type": "EMAIL_BASED",
"submitted_at": "2026-03-23T07:00:00Z",
"url": "https://malware-site.com",
"email_recipients": ["newvirus@kaspersky.com", "virus@kaspersky.com"],
"email_success_count": 2,
"email_failure_count": 1,
"email_results": [
{ "recipient": "newvirus@kaspersky.com", "success": true, "message_id": "msg-123" },
{ "recipient": "virus@kaspersky.com", "success": false, "error": "SMTP timeout" }
]
}
}task_id Tìm task trong MongoDB.report_tasks:status = COMPLETED / FAILEDcompleted_at = nowexecution_time = result.executionTimeresult = {...}callback_sent = truecallback_sent_at = nowreport_sites.success_count++ hoặc failure_count++.report_sites.success_rate.GET /reports/domains/:id là endpoint phức tạp nhất vì nó join data từ nhiều nguồn:report_domains (MongoDB)
có tasks liên kết
report_tasks (MongoDB) JOIN qua report_domain_id HOẶC (domain + keyword)
mỗi task có assigned_profile_id
profiles (MongoDB) Batch lookup để lấy email
mỗi task có server_id
servers (MySQL) Lookup để lấy name + ip
mỗi task có platform
report_sites (MongoDB) Lookup display_name cho từng platformprofileIDMap[task.AssignedProfileID] Email từ profiles collection (source of truth).task.Result["profile_email"] Email được lưu trong result (fallback).POST /reports/bulk-update{ "ids": ["id1", "id2"], "fields": { "status": "deleted", "keyword": "..." } }domain field: nếu có trong fields, phải bắt đầu bằng http:// hoặc https://.updated_at được tự động thêm vào updateFields.fields.status = "deleted" tự động gọi CancelTasksByDomainID cho từng domain.{
"success": true,
"data": {
"success_count": 2,
"failed_count": 0,
"failed_ids": [],
"message": "Updated 2 records successfully"
}
}type của report_domain ảnh hưởng đến cách Worker xử lý:type | Mô tả | Yêu cầu |
|---|---|---|
google_ads | Domain đang chạy Google Ads | keyword bắt buộc |
normal | Domain thông thường (không qua tìm kiếm) | keyword tùy chọn |
google_ads:normal:GetSiteStats)GET /reports/sites/stats{
"total_sites": 24,
"active_sites": 18,
"inactive_sites": 6,
"success_rate": 87.5,
"total_reports": 945,
"success_reports": 827,
"failure_reports": 118
}report_tasks:SiteReportCard.vue hiển thị:operations.toggleSite(site)SitePromptEditorEditSiteModalDELETE /reports/sites/:site_nametoggleSite gọi POST /reports/sites/toggle:components/site-reports/SitePromptEditor.vueSiteReportCard.handleEditPrompt(site) set selectedSite, mở showPromptEditor.SitePromptEditor load prompt hiện tại: GET /reports/sites/:site_name/prompt.PUT /reports/sites/:site_name/prompt { prompt: "..." }.saved parent gọi data.fetchSites() để refresh.You are a security analyst filling out a phishing report form.
The domain {domain} has been identified as a phishing site targeting {keyword}.
Fill in the following fields:
- url: The full URL of the phishing site
- description: Brief description of why this site is phishing
- category: "PHISHING" or "MALWARE"components/site-reports/BulkPromptUpdateModal.vuePOST /reports/sites/bulk-update-prompts{ "site_names": ["google_safe_browsing", "spamhaus"], "prompt": "..." }updated_count.saved(updatedCount) nếu updatedCount > 0 refetch sites.GET /reports/vendors/enabledGetEnabledVendorsEMAIL_BASED sites đang enabled = true và status = "active".GET /reports/tasks/monitoringGetTaskMonitoringapi/internal/report/service/task_monitoring.goGET /reports/tasks/activestatus = IN_PROGRESS, kèm thông tin:domain, platform, server_id, started_atexecution_time (giây kể từ started_at)GET /reports/tasks/:idgpt_raw_prompt Lưu lại raw prompt đã thực sự gửi cho model GPT (sau khi đã inject domain, keyword vào template). Dùng để debug khi GPT trả về output sai.mongoDb MongoDB connectionmysqlClient GORM MySQL client (dùng cho server lookup)reportSiteRepo MongoDB repo cho report_sitesreportTaskRepo MongoDB repo cho report_tasksads_metrics_service.go Aggregate metrics từ ads collectionaccount_metrics_service.go Tính dead/alive accountsbrand_extractor.go Phân loại brand từ domain/keyword/pc-report/csv. Sử dụng csv_generator.go để tạo file CSV.csv_generator.go tạo CSV với các cột:Report Date | Brand | Ads Detected | Reports Count | New Dead Ads |
Remaining Ads | Ad Accounts | Remaining Accounts | Dead Accounts | Dead % |
Success Rate | Failure Rate | Keywords Count | Active KeywordsGetSiteFullConfig:RequestReportTask bỏ qua task có domain rỗng thay vì trả lỗi:keyword = "unknown" thay vì bỏ qua.useReportDashboardData dùng module-level booleans không phải ref:unique_key = domain + "|" + site.name + "|" + date để tránh submit trùng lặp trong cùng ngày. Cron job check key này trước khi tạo task mới.