Services
fxconsole (web)(web)
Web console Laravel + React giám sát tài khoản MT5, quản lý VPS và deploy, cấu hình hot-reload cho EA, phân tích thị trường và báo cáo. Trang này giúp bạn biết console làm gì, chạy trên container nào, route nào gọi được, dữ liệu nằm ở đâu và broker đọc setting nào. The Laravel + React web console that monitors MT5 accounts, manages VPSs and deployments, pushes hot-reload config to EAs, and runs market analysis and reports. This page tells you what the console does, which containers it runs in, which routes exist, where data lives and which settings the brokers read.
Console làm gìWhat the console does
Mọi trang đều cần đăng nhập. Người dùng thường thấy tài khoản của chính mình (khớp theo users.mt5_secret_key); admin thấy toàn bộ và có thêm nhóm /admin/*.
Every page requires a login. Regular users see their own accounts (matched by users.mt5_secret_key); admins see everything and get the extra /admin/* group.
| Khu vựcArea | Route | Ai dùngWho | Chức năngWhat it does |
|---|---|---|---|
| Dashboard trạng thái tài khoảnAccount status dashboard | /dashboard, /dashboard/accounts/{account_id} |
Mọi userAll users | Balance, equity, lệnh mở, lịch sử hôm nay, ghi chú, watchlist, cảnh báo sụt equity. Dữ liệu đến từ EA AccountStatusUpdater (HTTP hoặc qua AccountStatusBroker). Có chế độ polling và chế độ "Stream (socket)" qua /status-ws.Balance, equity, open positions, today's history, notes, watchlist, equity-drop alerts. Data comes from the AccountStatusUpdater EA (HTTP or via AccountStatusBroker). Polling mode and "Stream (socket)" mode over /status-ws. |
| Báo cáo & hiệu suấtReports & performance | /dashboard/accounts/{account_id}/report, /performance |
Mọi userAll users | "Generate report" yêu cầu EA stream lịch sử deal theo từng tuần; /performance vẽ chuỗi hằng ngày từ báo cáo định kỳ (balance/profit cộng dồn các tài khoản của user, đến phiên đóng cửa gần nhất), kèm hai ô tổng cho khoảng đang chọn: Lợi nhuận và Đã rút (tổng các balance op âm, tức lệnh rút tiền; rê chuột để xem số lần rút). Admin xem user khác qua /admin/users/{id}/performance."Generate report" asks the EA to stream deal history week by week; /performance charts a daily series from the scheduled reports (balance/profit summed over the user's accounts, up to the last market close), with two totals for the selected range: Profit and Withdrawn (sum of negative balance operations, i.e. withdrawals; hover for the count). Admins view another user via /admin/users/{id}/performance. |
| Charts | /charts, /charts/{slug}/{symbol} |
Mọi userAll users | Giá realtime từ TickBroker (/ws) + nến OHLC trong TimescaleDB, có thể hiện lệnh đang mở của tài khoản mình trên chart.Realtime prices from TickBroker (/ws) + OHLC candles in TimescaleDB, optionally overlaying your own open positions. |
| Phân tích, lịch kinh tế, tinAnalysis, calendar, news | /analysis, /analysis/{slug}/{symbol}, /calendar, /news |
Mọi user (chỉ đọc); admin cấu hình ở /admin/analysisAll users (read-only); admins configure at /admin/analysis |
Thẻ phân tích do lệnh lịch analysis:run tạo (mức kỹ thuật + đoạn nhận định do Claude viết qua sidecar claude-cli). Lịch kinh tế đồng bộ từ lịch MT5 (calendar:sync), tin nóng từ RSS công khai (news:sync). Trang chỉ đọc bảng DB, không gọi nguồn ngoài.Analysis cards are produced by the scheduled analysis:run command (technical levels + a narrative written by Claude through the claude-cli sidecar). The economic calendar is mirrored from the MT5 calendar (calendar:sync), headlines from public RSS (news:sync). Pages only read DB tables, never the upstream. |
| VPS & deployVPS & deployments | /vps, /vps/{id}, /deployments, /deployments/new |
Mọi user xem fleet và tự deploy trong hạn mức max_deployed_accounts; thao tác sửa VPS chỉ adminAll users see the fleet and self-deploy within their max_deployed_accounts quota; VPS mutations are admin-only |
Deploy / gỡ / đổi package / migrate tài khoản MT5 giữa VPS, trạng thái live qua VpsStreamBroker (/vps-ws). Admin: thêm VPS, Proxmox clone template, nâng cấp agent, logs, rollback, gán tài khoản cho user khác.Deploy / remove / change package / migrate MT5 accounts between VPSs, live status via VpsStreamBroker (/vps-ws). Admin: add VPS, Proxmox template clone, agent upgrades, logs, rollback, reassigning accounts to another user. |
| Package & bản agentPackages & agent releases | /admin/packages, /admin/agent-releases |
Admin | Thư viện package deploy (zip EA + .set) cho user chọn khi deploy; bulk upgrade mọi tài khoản đang dùng 1 package; upload zip phiên bản vps-agent; dọn file zip mồ côi.Deployment package library (EA zip + .set) users pick from; bulk-upgrade every account on a package; upload vps-agent release zips; sweep orphaned zips. |
| EA products (easupported)EA products (easupported) | /admin/easupported, /admin/easupported/{slug} |
Admin | Đăng ký EA product (slug + secret token), xem tài khoản đang heartbeat, Apply / Clear config hot-reload. Xem bên dưới.Register EA products (slug + secret token), see accounts that heartbeat, Apply / Clear hot-reload config. See below. |
| EA của tôiMy EAs | /easupported, /easupported/{slug}, /easupported/{slug}/account/{id} |
Mọi user (chỉ account được gán cho mình)All users (only accounts assigned to them) | Cùng bảng trạng thái và Apply / Clear hot config như trang admin, giới hạn ở account MT5 được gán cho user. Xem bên dưới.The same status table and Apply / Clear hot config as the admin page, limited to the MT5 accounts assigned to the user. See below. |
| Hiển thị tiền: USD/VND, hệ số chiaMoney display: USD/VND, money divisor | Nút $ / ₫ trên header (POST /currency), menu Hệ số chia (POST /deployments/bulk-divisor), hệ số theo server trên /admin/brokersHeader $ / ₫ button (POST /currency), Money divisor menu (POST /deployments/bulk-divisor), per-server divisor on /admin/brokers |
Mọi user; đặt hệ số theo server chỉ adminAll users; per-server divisors are admin-only | Hiện mọi số tiền bằng VND theo tỷ giá Binance P2P; chia số tiền của account báo ×10 / ×100 (vd. account cent) theo hệ số /10 hoặc /100. Xem Hiển thị tiền.Shows every money figure in VND at the Binance P2P rate; divides the money of accounts that report ×10 / ×100 (e.g. cent accounts) by /10 or /100. See Money display. |
| Remote desktopRemote desktop | POST /admin/vps/{id}/remote-session |
Admin | Mở phiên VNC trong trình duyệt qua Guacamole bằng token ký 60 giây; có trang audit /admin/vps/{id}/remote-sessions.Opens a browser VNC session through Guacamole with a 60-second signed token; audit page at /admin/vps/{id}/remote-sessions. |
| Quản trị khácOther admin | /admin/users, /admin/settings, /admin/brokers, /admin/theme, /admin/ip-report, /admin/proxmox/nodes |
Admin | User (role, hạn mức, khoá, chuyển toàn bộ tài khoản), setting runtime, hồ sơ broker + logo, màu giao diện + ảnh nền login, báo cáo IP public theo tài khoản, node Proxmox.Users (role, quota, block, reassign all accounts), runtime settings, broker profiles + logos, theme accents + login background, public-IP report per account, Proxmox nodes. |
StackStack
Backend
- Laravel 12 (PHP ^8.2) + Inertia
- MySQL (kết nối mặc định, database
trade_copier)(default connection, databasetrade_copier) - Redis 7 (không persist)(no persistence)
- TimescaleDB (PostgreSQL 16) chỉ cho
ohlc_dataforohlc_dataonly - Scheduler
php artisan schedule:work(không cron hệ thống)Schedulerphp artisan schedule:work(no system cron)
Frontend
- React 19 + TypeScript +
@inertiajs/react, Vite 7 - Tailwind CSS v4 (không có
tailwind.config.js, theme nằm trongresources/css/app.css)Tailwind CSS v4 (notailwind.config.js; theme lives inresources/css/app.css) - shadcn/ui (new-york, OKLCH) —
resources/js/components/ui/ - recharts cho biểu đồ tài khoản, lightweight-charts cho chart giá/tickrecharts for account charts, lightweight-charts for price/tick charts
- Font IBM Plex Sans, số dùng
tabular-numsIBM Plex Sans, numbers usetabular-nums
Page nằm ở resources/js/Pages/**, layout chung resources/js/layouts/AppLayout.tsx, sidebar resources/js/components/AppSidebar.tsx, alias @/ → resources/js/. Dev: npm run dev; build: npm run build; typecheck: npx tsc --noEmit (không có script lint).
Pages live in resources/js/Pages/**, shared layout resources/js/layouts/AppLayout.tsx, sidebar resources/js/components/AppSidebar.tsx, alias @/ → resources/js/. Dev: npm run dev; build: npm run build; typecheck: npx tsc --noEmit (no lint script).
Chỉ dùng token màu ngữ nghĩa (text-up, text-down, text-success, bg-primary…), không dùng màu palette thô; không tự dựng component mà shadcn đã có; màu xanh brand dùng hạn chế; phải chạy được cả sáng lẫn tối. Mọi bảng liệt kê dòng phải dùng toolbar chung (refresh, cột, tìm kiếm) và mọi cột dữ liệu sort được. Chi tiết: monitor/fxconsole/CLAUDE.md.
Use semantic colour tokens only (text-up, text-down, text-success, bg-primary…), never raw palette colours; never hand-roll a component shadcn already provides; keep brand blue scarce; light and dark must both work. Every row-listing table uses the shared toolbar (refresh, columns, search) and every data column is sortable. Details: monitor/fxconsole/CLAUDE.md.
ContainerContainers
Stack monitor/fxconsole/docker-compose.yml (project fxconsole, network fxconsole_default). Bốn broker là stack riêng, join vào network này — xem Các broker realtime.
Stack monitor/fxconsole/docker-compose.yml (project fxconsole, network fxconsole_default). The four brokers are separate stacks that join this network — see Realtime brokers.
| Service | Container | CổngPorts | Vai tròRole |
|---|---|---|---|
app | fxconsole-app | 9000 (nội bộ)9000 (internal) | PHP-FPM. Mỗi lần boot: cài composer/npm nếu manifest đổi, npm run build, tạo APP_KEY chỉ khi chưa có, php artisan migrate --force, view:clear. Mount read-only ../VpsStreamBroker/certs vào /broker-certs để phục vụ GET /api/agent/broker-config.PHP-FPM. On every boot: installs composer/npm deps if manifests changed, npm run build, generates APP_KEY only when missing, php artisan migrate --force, view:clear. Mounts ../VpsStreamBroker/certs read-only at /broker-certs to serve GET /api/agent/broker-config. |
web | fxconsole-web | 8080:80 |
nginx. Proxy WebSocket /ws → TickBroker 9601, /status-ws → AccountStatusBroker 9603, /vps-ws → VpsStreamBroker 9606 (proxy_ssl_verify off, timeout 3600s). Broker gọi Laravel qua http://fxconsole-web (cổng 80 nội bộ).nginx. WebSocket proxies /ws → TickBroker 9601, /status-ws → AccountStatusBroker 9603, /vps-ws → VpsStreamBroker 9606 (proxy_ssl_verify off, 3600s timeouts). Brokers call Laravel at http://fxconsole-web (internal port 80). |
redis | fxconsole-redis | 6379 (nội bộ)6379 (internal) | Redis 7, --save "", --appendonly no, allkeys-lru: dữ liệu trạng thái được EA tạo lại liên tục nên ưu tiên tốc độ ghi.Redis 7 with --save "", --appendonly no, allkeys-lru: status data is regenerated by EAs constantly, so write speed wins over durability. |
timescale | fxconsole-timescale | 5432:5432 |
TimescaleDB pg16 cho ohlc_data. Cổng publish mọi interface — nếu host có IP public phải chặn 5432 ở firewall.TimescaleDB pg16 for ohlc_data. Published on all interfaces — block 5432 at the firewall if the host has a public IP. |
scheduler | fxconsole-scheduler | — | Chờ vendor/autoload.php, chạy schedule:clear-cache (xoá lock kẹt) rồi schedule:work.Waits for vendor/autoload.php, runs schedule:clear-cache (clears stuck locks), then schedule:work. |
claude-cli | fxconsole-claude-cli | 8788 (nội bộ)8788 (internal) | Sidecar Claude Code CLI dùng gói subscription (OAuth). Credential ở volume claude_creds / claude_config. Xem claude-cli.Claude Code CLI sidecar on the team subscription (OAuth). Credentials on the claude_creds / claude_config volumes. See claude-cli. |
guacd, guacamole | fxconsole-guacd, fxconsole-guacamole | 8282:8080 |
Remote desktop trong trình duyệt, network riêng guac_internal, auth JSON (không DB). Xem Guacamole.Browser remote desktop on its own guac_internal network, JSON auth (no DB). See Guacamole. |
down -vNever down -v
docker compose down -v xoá volume có tên: timescale_data (mất toàn bộ lịch sử OHLC) và claude_creds / claude_config (mất đăng nhập subscription). Chỉ dùng down thường.
docker compose down -v destroys the named volumes: timescale_data (all OHLC history) and claude_creds / claude_config (subscription login). Use plain down only.
Route chínhKey routes
API cho EA và agent (routes/api.php)EA and agent API (routes/api.php)
Stateless, không session. Tất cả có tiền tố /api.
Stateless, no session. All prefixed with /api.
| Method · Path | Ai gọiCaller | Auth | Mục đíchPurpose |
|---|---|---|---|
POST /api/account-status | AccountStatusUpdater | Authorization: Bearer = setting ea_api_bearer |
Upsert trạng thái tài khoản, nhận chunk báo cáo, trả pending_report khi có yêu cầu báo cáo.Upserts account status, ingests report chunks, returns pending_report when a report is requested. |
POST /api/vps/heartbeat | vps-agent | X-Vps-Secret |
Heartbeat HTTPS (đường dự phòng khi broker stream chết).HTTPS heartbeat (fallback when the broker stream is down). |
GET /api/vps/cmd/{id}/payload | CmdAgent | X-Vps-Secret |
Kéo payload lệnh đầy đủ (mật khẩu MT5 mã hoá…) sau khi broker đẩy cmd_id.Pulls the full command payload (encrypted MT5 password…) after the broker pushes cmd_id. |
GET /api/agent/broker-config | prepare-vm.ps1 | X-Vps-Secret |
Trả broker_host, broker_port, broker_ca để cấu hình stunnel.Returns broker_host, broker_port, broker_ca for stunnel. |
POST /api/agent/init-config | VM clone từ templateTemplate-cloned VM | Authorization: Bearer = vps_init_bootstrap_token + MAC đã đăng ký+ pre-registered MAC |
Zero-touch provisioning; token trống → 503.Zero-touch provisioning; empty token → 503. |
GET /api/agent/release/{id}/download | upgrade.ps1 | X-Vps-Secret |
Tải zip agent mới; header X-Release-Hash trả SHA256.Downloads the new agent zip; X-Release-Hash header echoes the SHA256. |
POST /api/tmap/save · GET /api/tmap/load · DELETE /api/tmap/clear · POST /api/tmap/heartbeat | EA cũLegacy EAs | Authorization: Bearer = tmap_secret_key (fallback TMAP_SECRET_KEY)(fallback TMAP_SECRET_KEY) |
Ticket map + leader election dùng token chung (legacy). EA mới dùng đường per-product bên dưới.Ticket map + leader election with the shared token (legacy). New EAs use the per-product route below. |
GET /api/sync-state · POST /api/sync-bars | TickStreamerPub | Bearer | Bù nến OHLC bị hụt khi EA offline.OHLC backfill for gaps built up while the EA was offline. |
Endpoint EA theo productPer-product EA endpoints
Khai báo trong routes/web.php nhưng bỏ toàn bộ middleware session/cookie/CSRF/Inertia; auth bằng Bearer = secret_token của product (EA đặt vào InpCloudBearerToken). URL gốc là InpCloudMyApiDomain = https://<host>/easupported/{slug}.
Declared in routes/web.php but stripped of every session/cookie/CSRF/Inertia middleware; auth is Bearer = the product's secret_token (set on the EA as InpCloudBearerToken). The base URL is InpCloudMyApiDomain = https://<host>/easupported/{slug}.
| Method · Path | Mục đíchPurpose |
|---|---|
POST /easupported/{slug}/api/tmap/heartbeat | Gửi config_schema + payload + node; nhận leader, hot_keys và giá trị override.Sends config_schema + payload + node; receives leader state, hot_keys and override values. |
POST /easupported/{slug}/api/tmap/save | Lưu ticket map (bỏ qua ghi khi map không đổi, trả "skipped":true).Saves the ticket map (skips the write when unchanged, returns "skipped":true). |
GET /easupported/{slug}/api/tmap/load?magic=&account= | Nạp ticket map khi khởi động.Loads the ticket map at startup. |
DELETE /easupported/{slug}/api/tmap/clear?magic=&account= | Xoá ticket map.Clears the ticket map. |
Endpoint nội bộ cho brokerInternal broker endpoints
| Method · Path | Ai gọiCaller | Mục đíchPurpose |
|---|---|---|
POST /api/internal/ea-link/key | EaLinkBroker | Body {"slug"} → 200 {"ok":true,"ea_id","key"} với key = sha256(secret_token) dạng hex (token thô không rời Laravel); 404 {"ok":false,"reason":"unknown_ea"}.Body {"slug"} → 200 {"ok":true,"ea_id","key"} where key = hex sha256(secret_token) (the raw token never leaves Laravel); 404 {"ok":false,"reason":"unknown_ea"}. |
POST /api/internal/broker/command-ack | VpsStreamBroker | Mỗi event kết thúc lệnh của agent → Laravel chạy cascade (mt5_accounts, observer, email deploy, migration bước 2).Each terminal agent event → Laravel runs the cascade (mt5_accounts, observers, deploy emails, migration stage 2). |
Cả hai xác thực bằng header X-Broker-Secret = setting broker_internal_secret và fail-closed khi server chưa cấu hình secret.
Both authenticate with the X-Broker-Secret header = the broker_internal_secret setting and fail closed when the server has no secret configured.
Route web (session)Web routes (session)
- Khách:
/login,/register,/forgot-password,/reset-password(OTP qua email).POST /localeđổi ngôn ngữ cho cả khách lẫn user. - User: các trang ở bảng phía trên, cộng
/settings(hồ sơ, thông báo, secret key MT5, đổi mật khẩu bằng OTP),/notifications. Bấm thông báo "deploy thành công" mở/dashboard/accounts/{login}(cả thông báo cũ đã lưu link/vps); thông báo deploy lỗi hoặc gỡ vẫn mở/vps(admin) hoặc/deployments. - Chỉ user đã đăng nhập:
POST /currency(USD|VND),POST /deployments/bulk-divisor(account_ids[],mode=1|10|100|auto; user thường chỉ đổi account của mình, admin đổi mọi account), nhóm/easupported/*(EA của tôi). - JSON cho SPA Charts nằm trong
web.phpdưới tiền tố/apiđể dùng cookie session:/api/brokers,/api/brokers/{slug}/symbols,/api/brokers/{slug}/quotes,/api/charts/{slug}/{symbol},/api/charts/{slug}/{symbol}/latest,/api/broker-profiles,/api/mt5-accounts,/api/mt5-accounts/{accountId}/positions. - Admin: toàn bộ nằm trong group
/adminvới middlewareadmin(403 cho user thường).
- Guests:
/login,/register,/forgot-password,/reset-password(email OTP).POST /localeswitches language for guests and users. - Users: the pages in the table above, plus
/settings(profile, notifications, MT5 secret key, OTP password change) and/notifications. Clicking a "deploy succeeded" notification opens/dashboard/accounts/{login}(stored notifications that still carry/vpstoo); failed or removed deploy notifications still open/vps(admins) or/deployments. - Logged-in users only:
POST /currency(USD|VND),POST /deployments/bulk-divisor(account_ids[],mode=1|10|100|auto; regular users change only their own accounts, admins any account), the/easupported/*group (My EAs). - JSON for the Charts SPA lives in
web.phpunder the/apiprefix so it uses the session cookie:/api/brokers,/api/brokers/{slug}/symbols,/api/brokers/{slug}/quotes,/api/charts/{slug}/{symbol},/api/charts/{slug}/{symbol}/latest,/api/broker-profiles,/api/mt5-accounts,/api/mt5-accounts/{accountId}/positions. - Admin: everything sits in the
/admingroup behind theadminmiddleware (403 for regular users).
Mục "Route web" trong monitor/README.html chỉ còn liệt kê /login, /register, /dashboard và "làm mới theo interval (AJAX)" — đó là giao diện Blade đời đầu. Bảng trên đọc từ routes/web.php hiện tại.
The "Route web" section in monitor/README.html still lists only /login, /register, /dashboard and "interval AJAX refresh" — that was the original Blade UI. The tables above are read from the current routes/web.php.
EA products (easupported) và hot-reloadEA products (easupported) and hot-reload
Nguyên tắc: 1 EA product = 1 slug + 1 secret token. Server là nguồn chân lý của config override, input của EA là giá trị fallback. Giá trị config chỉ đi qua HTTPS; socket EA Link chỉ mang tín hiệu. Chuẩn đầy đủ cho EA: Chuẩn tích hợp EA.
Principle: 1 EA product = 1 slug + 1 secret token. The server is the source of truth for config overrides; the EA's input is the fallback. Config values travel over HTTPS only; the EA Link socket carries only a signal. Full EA-side standard: EA integration standard.
Đăng ký productRegistering a product
- Mở
/admin/easupported→ thêm EA: nhập tên, slug được gợi ý từ tên (server slugify lại và thêm hậu tố-2,-3… cho unique), secret token 8–128 ký tự.Open/admin/easupported→ add EA: enter a name; the slug is suggested from it (the server slugifies again and appends-2,-3… to keep it unique); secret token 8–128 characters. - Copy URL
https://<host>/easupported/{slug}vàoInpCloudMyApiDomainvà token vàoInpCloudBearerTokentrên terminal (không commit.setchứa token).Copy the URLhttps://<host>/easupported/{slug}intoInpCloudMyApiDomainand the token intoInpCloudBearerTokenon the terminal (never commit a.setwith the token). - Chạy EA: heartbeat đầu tiên tạo dòng trong
ea_accountvà lưuconfig_schemavào product.Run the EA: the first heartbeat creates anea_accountrow and stores theconfig_schemaon the product.
Xoá product sẽ cascade xoá ea_account → ea_config. Nghi lộ token: đổi token trên trang rồi cập nhật .set của mọi terminal; EaLinkBroker nhận key mới cho kết nối mới trong vòng KEY_CACHE_SEC (60s).
Deleting a product cascades to ea_account → ea_config. If a token may have leaked: change it on the page and update every terminal's .set; EaLinkBroker picks up the new key for new connections within KEY_CACHE_SEC (60s).
config_schema và trang accountconfig_schema and the account pages
- EA tự khai báo danh sách config chỉnh được trong mỗi heartbeat: mỗi phần tử có
key(snake_case),label,type(bool·int·float·enum·string),default,options(bắt buộc với enum). Web render form từ schema, không hardcode. - Schema lưu theo product; nhiều build khác schema cùng slug thì web hiển thị schema của account heartbeat gần nhất.
/admin/easupported/{slug}: danh sách account (Online khi heartbeat < 120s; account có heartbeat cuối quá 5 ngày được ẩn khỏi bảng và khỏi số đếm, không bị xoá, hiện lại ở heartbeat kế tiếp), broker + server, heartbeat cuối, config đang override, badge Link (có presence EA Link) hoặc HTTP. Bảng dùng toolbar chung (tự làm mới, cột, sort mọi cột, tìm kiếm); trang payload cũng vậy./admin/easupported/{slug}/account/{id}: bảng payload 2 cộtname/datado EA đẩy (thường là nguyên nội dung info panel).
- The EA declares its editable configs in every heartbeat: each item has
key(snake_case),label,type(bool·int·float·enum·string),default,options(required for enum). The web renders the form from the schema; nothing is hardcoded. - The schema is stored per product; when several builds with different schemas share a slug, the web shows the schema of the most recently heartbeating account.
/admin/easupported/{slug}: account list (Online when heartbeat < 120s; accounts whose last heartbeat is more than 5 days old are hidden from the table and the counts, not deleted, and come back on their next heartbeat), broker + server, last heartbeat, current overrides, a Link badge (EA Link presence) or HTTP. The table uses the shared toolbar (auto refresh, columns, every column sortable, search); so does the payload page./admin/easupported/{slug}/account/{id}: two-columnname/datapayload table pushed by the EA (usually the full info panel).
Apply / Clear configApply / Clear config
- Apply (
POST /admin/easupported/{slug}/config): chọn 1 hoặc nhiều account, nhập giá trị. Chỉ nhận key có trong schema (product chưa báo schema → lỗi "This EA has not reported its config schema yet."), chỉ account thuộc product đó. Ghiea_config(param,value,updated_by). - Clear (
DELETE /admin/easupported/{slug}/config): xoá override của các account, toàn bộ hoặc theo danh sáchparams. Apply và Clear đều ghi log (easupported.config.apply/easupported.config.clear, kèmscopeadmin hoặc user). Mỗi giá trị Apply phải là một giá trị đơn (không phải mảng), tối đa 128 ký tự. - Có thay đổi thì
EaLinkNotifierpublish Redisea:link:cfgvới{"slug","targets":[{"composite_id","magic"}],"rev":<ms>}. EA đang có Link kéo config ngay; EA khác nhận ở heartbeat kế tiếp. - Heartbeat response chỉ chứa key đang override, kèm
hot_keys(CSV). Key vắng tronghot_keys→ EA quay về giá trị input;"hot_keys":""→ revert toàn bộ.
- Apply (
POST /admin/easupported/{slug}/config): pick one or many accounts and enter values. Only schema keys are accepted (no schema yet → "This EA has not reported its config schema yet."), and only accounts of that product. Writesea_config(param,value,updated_by). - Clear (
DELETE /admin/easupported/{slug}/config): deletes the overrides of the selected accounts, all of them or only the listedparams. Apply and Clear are both logged (easupported.config.apply/easupported.config.clear, withscopeadmin or user). Each Apply value must be a single value (not an array), at most 128 characters. - When something changed,
EaLinkNotifierpublishes Redisea:link:cfgwith{"slug","targets":[{"composite_id","magic"}],"rev":<ms>}. Linked EAs pull config immediately; others get it on their next heartbeat. - The heartbeat response carries only keys that are overridden, plus
hot_keys(CSV). A key missing fromhot_keys→ the EA reverts to its input;"hot_keys":""→ revert everything.
sequenceDiagram
participant A as Admin
participant L as Laravel
participant R as Redis
participant B as EaLinkBroker 9607
participant E as EA (MT5)
A->>L: Apply / Clear trên /admin/easupported/{slug}
L->>L: Ghi / xoá ea_config
L->>R: PUBLISH ea:link:cfg {slug, targets, rev}
R-->>B: PSUBSCRIBE *ea:link:cfg
B->>E: t=cfg, rev (chỉ tín hiệu)
E->>L: POST /easupported/{slug}/api/tmap/heartbeat (HTTPS)
L-->>E: hot_keys + giá trị override
E->>B: t=cfg_ack, rev, oksequenceDiagram
participant A as Admin
participant L as Laravel
participant R as Redis
participant B as EaLinkBroker 9607
participant E as EA (MT5)
A->>L: Apply / Clear on /admin/easupported/{slug}
L->>L: Write / delete ea_config
L->>R: PUBLISH ea:link:cfg {slug, targets, rev}
R-->>B: PSUBSCRIBE *ea:link:cfg
B->>E: t=cfg, rev (signal only)
E->>L: POST /easupported/{slug}/api/tmap/heartbeat (HTTPS)
L-->>E: hot_keys + override values
E->>B: t=cfg_ack, rev, okChi tiết protocol EA Link: EaLinkBroker.EA Link protocol details: EaLinkBroker.
Trang EA của tôi (user)My EAs page (users)
/easupported(mục "EA của tôi" trên sidebar): một thẻ cho mỗi EA product đang chạy trên ít nhất một account được gán cho user, kèm số account và số account online. Trang không gửi secret token hay URL của EA./easupported/{slug}và/easupported/{slug}/account/{id}: cùng bảng và trang payload như admin. Apply / Clear gọiPOST/DELETE /easupported/{slug}/config; user phải xác nhận trước khi Apply. EA không có account nào của user → 404, nên user không thấy các EA product khác.- Mọi lần đọc và ghi đều lọc ở server (
EaAccount::ownedBy). Chỉ chọn account không phải của mình → lỗiaccount_ids. Logic dùng chung với admin nằm ởApp\Services\EaSupportedAccounts.
Account được gán = dòng mt5_accounts chưa gỡ có user_id của user (user tự deploy, admin deploy hộ, hoặc admin gán lại). Một dòng ea_account thuộc user khi khớp dòng đó theo một trong ba điều kiện:
/easupported("My EAs" in the sidebar): one card per EA product running on at least one account assigned to the user, with the account and online counts. The page never sends secret tokens or EA URLs./easupported/{slug}and/easupported/{slug}/account/{id}: the same table and payload page as the admin view. Apply / Clear callPOST/DELETE /easupported/{slug}/config; users confirm before Apply. An EA with none of the user's accounts → 404, so other EA products stay hidden.- Every read and write is filtered on the server (
EaAccount::ownedBy). Selecting only accounts that aren't yours → anaccount_idserror. The logic shared with the admin pages lives inApp\Services\EaSupportedAccounts.
Assigned account = an active mt5_accounts row with the user's user_id (deployed by the user, deployed by an admin on their behalf, or reassigned to them). An ea_account row belongs to the user when it matches that row by any of three conditions:
| Điều kiệnCondition | Khi nào có tác dụngWhen it applies |
|---|---|
composite_id trùng nhauSame composite_id |
slug(company)::slug(server)::login. Giá trị thật được ghi vào mt5_accounts bởi heartbeat HTTP của AccountStatusUpdater và (từ 2026.09.14.1) heartbeat /easupported/{slug}/api/tmap/heartbeat (Mt5Account::syncCompositeId). Trước đó cột là placeholder broker::unknown-server::login.slug(company)::slug(server)::login. The real value is written to mt5_accounts by AccountStatusUpdater's HTTP heartbeat and (since 2026.09.14.1) by the /easupported/{slug}/api/tmap/heartbeat heartbeat (Mt5Account::syncCompositeId). Until then the column holds the broker::unknown-server::login placeholder. |
login + serverlogin + server |
EA gửi tên server MT5 trong trường server.The EA sends the MT5 server name in server. |
login + tên brokerlogin + broker name |
GoldenRobber gửi server = ACCOUNT_COMPANY (vd "Vantage Markets (Pty) Ltd"); so với broker_profiles.name của account được gán. Từ 2026.09.14.1.GoldenRobber sends server = ACCOUNT_COMPANY (e.g. "Vantage Markets (Pty) Ltd"); compared with the assigned account's broker_profiles.name. Since 2026.09.14.1. |
Mỗi login chỉ có một dòng active trong mt5_accounts (UNIQUE(login, removed_at)), nên khớp theo login không thể trỏ sang user khác. Cùng số login ở broker hoặc server khác thì không khớp. Account không deploy qua fxconsole (không có dòng mt5_accounts) không hiện ở trang này.
Each login has a single active row in mt5_accounts (UNIQUE(login, removed_at)), so a login match cannot point at another user. The same login number at another broker or server does not match. Accounts not deployed through fxconsole (no mt5_accounts row) do not appear on this page.
Bảng dữ liệuTables
| BảngTable | Vai tròRole |
|---|---|
easupported | Product: slug (unique), name, secret_token, description, document, config_schemaProduct: slug (unique), name, secret_token, description, document, config_schema |
ea_account | Account phát hiện từ heartbeat: ea_id, composite_id, login, name, magic, server, payload, last_heartbeat; unique (ea_id, composite_id, magic)Accounts discovered from heartbeats: ea_id, composite_id, login, name, magic, server, payload, last_heartbeat; unique (ea_id, composite_id, magic) |
ea_config | Override theo account: ea_account_id, param, value, updated_byPer-account overrides: ea_account_id, param, value, updated_by |
tmap_entries, tmap_nodes | Ticket map và node leader election (theo magic + account)Ticket map and leader-election nodes (by magic + account) |
Hiển thị tiền: USD/VND và hệ số chiaMoney display: USD/VND and money divisor
Số tiền được lưu và stream đúng như EA gửi. Việc đổi đơn vị chỉ xảy ra khi hiển thị (resources/js/lib/currency.ts: useMoney(), toUsd()) và khi server cộng tổng. Lot, giá, phần trăm và id không bao giờ bị đổi.
Money figures are stored and streamed exactly as the EA reports them. Units change only on display (resources/js/lib/currency.ts: useMoney(), toUsd()) and when the server sums totals. Lots, prices, percentages and ids are never converted.
Đổi USD ↔ VNDUSD ↔ VND switch
- Nút
$/₫cạnh nút ngôn ngữ, chỉ hiện khi đã đăng nhập. Lựa chọn lưu ởusers.currencyquaPOST /currency. Menu hiện "1 USDT = … ₫ · Binance P2P · x phút trước" và cảnh báo khi giá cũ hơn 1 giờ. - VND = giá trị USD × tỷ giá, làm tròn tới đồng, hậu tố
₫; trục biểu đồ dùng dạng rút gọn. USD giữ dạng 2 chữ số thập phân như trước. Chưa có tỷ giá → web hiện USD và khoá mục VND. - Tỷ giá lấy từ endpoint công khai mà trang Binance P2P dùng (
p2p.binance.com/bapi/c2c/v2/friendly/c2c/adv/search, không cần key) vớifiat=VND,asset=USDT,tradeType=SELL: median giá của 10 quảng cáo đầu, tức giá bán USDT (số đồng thực nhận). - Scheduler gọi
fx:sync-usdt-vndmỗi phút; lần gọi Binance thật chỉ chạy khi đã quausdt_vnd_interval_minutes(mặc định 5, khoảng 1–60) kể từ lần thử trước. Cần ít nhất 3 quảng cáo hợp lệ và median trong khoảng 10.000–100.000, không thì giữ giá cũ. HTTP 403 / 418 / 429 → tạm dừng 15 phút. - Giá lưu ở bảng
fx_rates(một dòng cho mỗi cặp; không để Redis vì Redis không persist). Shared propfx.usdt_vndđọc qua cache 60 giây. - Endpoint không chính thức: Binance có thể đổi hoặc chặn IP server. Khi đó web vẫn chạy với giá cũ và hiện thời điểm cập nhật cuối.
- A
$/₫button next to the language button, shown only when logged in. The choice is stored inusers.currencyviaPOST /currency. The menu shows "1 USDT = … ₫ · Binance P2P · x min ago" and warns when the rate is older than an hour. - VND = USD value × rate, rounded to whole đồng, with a
₫suffix; chart axes use a compact form. USD keeps its usual 2-decimal format. No rate yet → the web shows USD and disables the VND option. - The rate comes from the public endpoint the Binance P2P page itself uses (
p2p.binance.com/bapi/c2c/v2/friendly/c2c/adv/search, no key) withfiat=VND,asset=USDT,tradeType=SELL: the median price of the first 10 ads, i.e. the USDT sell price (the đồng actually received). - The scheduler runs
fx:sync-usdt-vndevery minute; the real Binance call only happens onceusdt_vnd_interval_minutes(default 5, range 1–60) has passed since the last attempt. It needs at least 3 usable ads and a median between 10,000 and 100,000, otherwise the previous rate is kept. HTTP 403 / 418 / 429 → back off for 15 minutes. - The rate lives in the
fx_ratestable (one row per pair; not Redis, which has no persistence). Thefx.usdt_vndshared prop reads it through a 60-second cache. - The endpoint is unofficial: Binance may change it or block the server IP. The web then keeps working with the last rate and shows when it was fetched.
cd ~/goldenrobber/GoldenRobber/monitor/fxconsole
docker compose exec app php artisan fx:sync-usdt-vnd # respects the interval / back-off
docker compose exec app php artisan fx:sync-usdt-vnd --force # fetch now
Hệ số chia /1 · /10 · /100Money divisor /1 · /10 · /100
Một số account báo số tiền lớn hơn đơn vị thật: account cent (USC) báo ×100 (250.000 USC = 2.500 USD), vài broker báo ×10. EA không cho biết điều này, nên mỗi account có một hệ số chia và fxconsole chia số tiền cho hệ số đó ở mọi nơi hiển thị hoặc cộng tổng. /1 = giữ nguyên (standard), /100 = cent.
Some accounts report money larger than the real unit: cent (USC) accounts report ×100 (250,000 USC = 2,500 USD), a few brokers report ×10. The EA doesn't say so, so every account has a money divisor and fxconsole divides its money by it wherever money is shown or summed. /1 = as reported (standard), /100 = cent.
mt5_accounts.money_divisor |
NghĩaMeaning | Đặt ở đâuSet where |
|---|---|---|
null (mặc định)(default) |
Tự động: hệ số của server account trong broker_profiles.server_divisors (so khớp không phân biệt hoa thường và khoảng trắng thừa); server không có hệ số → /1Auto: the divisor of the account's server in broker_profiles.server_divisors (case- and extra-space-insensitive); a server without one → /1 |
Chọn /1 · /10 · /100 cạnh từng server khi sửa broker ở /admin/brokers (admin)Pick /1 · /10 · /100 next to each server when editing a broker on /admin/brokers (admin) |
1 · 10 · 100 |
Đặt tay cho account, thắng hệ số của serverSet by hand for the account, overrides the server's divisor | Menu Hệ số chia → /1, /10 hoặc /100Money divisor menu → /1, /10 or /100 |
- Menu Hệ số chia áp cho các dòng đang chọn trên
/deployments(chỉ chọn được dòng running hoặc failed),/admin/users/{id}/accountsvà/admin/packages/{id}/accounts(chỉ dòng running). Mục "Tự động theo server của broker" đưa cột vềnull. EndpointPOST /deployments/bulk-divisor; user thường chỉ đổi account của mình. - Ba bảng trên có cột Hệ số:
/1,/10hoặc/100, thêm chữ "tự động" khi đang theo server. Account đang deploy chưa chọn được: dùng hệ số của server. - Account mới luôn ở chế độ tự động. Form deploy báo trước khi server đang chọn có hệ số
/10hoặc/100. - Hệ số thật sự tính ở một chỗ:
Mt5Account::divisorByLogin()/effectiveDivisor(). Account chỉ chạy EA, không có dòngmt5_accounts, theo tên server EA báo lên (Dashboard). - Chia ở: Dashboard, trang account (thống kê, lệnh mở, lịch sử, thắng/thua, biểu đồ balance, báo cáo), trang VPS, admin Users và UserAccounts, ô tổng navbar/sidebar,
/performance(chia phía server trước khi cộng). Cột tiền sort theo giá trị đã chia; quy đổi VND tính trên số đã chia. Nhãn/10hoặc/100hiện cạnh login ở Dashboard, trang account và trang VPS (không hiện cho/1). - Mỗi user tự chọn xem số đã chia hay số gốc: công tắc Chia theo hệ số account trong menu
$/₫(lưu ởusers.money_divide, mặc định bật, quaPOST /currencyvớimoney_divide). Chỉ đổi cách user đó nhìn, không đổi hệ số của account. Tắt thì mọi nơi ở trên hiện số gốc EA báo về (kể cả tổng và/performance) và nhãn/100chuyển màu xám. - Từ
2026.09.14.2thay cho cờ Cent / Standard cũ: migration2026_09_14_000001đổiis_centtrue →100, false →1, rỗng →null, và mỗi server trongcent_servers→/100, rồi xoá hai cột cũ.
- The Money divisor menu applies to the selected rows on
/deployments(only running or failed rows are selectable),/admin/users/{id}/accountsand/admin/packages/{id}/accounts(running rows only). "Auto — follow the broker's servers" resets the column tonull. EndpointPOST /deployments/bulk-divisor; regular users change only their own accounts. - Those three tables have a Divisor column:
/1,/10or/100, with "auto" when it follows the server. Accounts still deploying can't be selected: they use the server's divisor. - New accounts always start on auto. The deploy form warns when the picked server has a
/10or/100divisor. - The effective divisor is computed in one place:
Mt5Account::divisorByLogin()/effectiveDivisor(). EA-only accounts with nomt5_accountsrow follow the server name the EA reports (Dashboard). - Divided on: the Dashboard, the account page (stats, open positions, history, win/loss, balance chart, reports), the VPS page, admin Users and UserAccounts, the navbar/sidebar totals,
/performance(divided server-side before summing). Money columns sort by the divided value; VND conversion uses the divided value. A/10or/100badge shows next to the login on the Dashboard, the account page and the VPS page (none for/1). - Each user picks divided or raw figures: the Apply account divisor switch in the
$/₫menu (stored inusers.money_divide, on by default, viaPOST /currencywithmoney_divide). It changes only that user's view, never the account's divisor. When off, every place above shows the raw figures the EA reports (totals and/performanceincluded) and the/100badge turns grey. - Replaces the old Cent / Standard flag since
2026.09.14.2: migration2026_09_14_000001mapsis_centtrue →100, false →1, empty →null, and every server incent_servers→/100, then drops both old columns.
Bearer token, secret key và token TMapBearer token, secret key and TMap token
Có nhiều loại key khác nhau, đừng nhầm. Giá trị thật chỉ nằm trong .env, bảng settings hoặc trang admin — không bao giờ trong tài liệu.
There are several distinct keys; don't mix them up. Real values live only in .env, the settings table or admin pages — never in docs.
| LoạiKind | Phía EA / clientEA / client side | Phía serverServer side | Bảo vệProtects |
|---|---|---|---|
| Account Status Bearer | InpApiBearer → Authorization: Bearer <secret> |
Setting ea_api_bearer ("EA API Bearer Token"), fallback EA_API_BEARER; trống = endpoint tắtSetting ea_api_bearer ("EA API Bearer Token"), fallback EA_API_BEARER; empty = endpoint disabled |
POST /api/account-status |
| Account Secret Key | InpAccountSecretKey → field JSONJSON field secret_key |
Index Redis account_status_keys:{secret_key}; khớp với users.mt5_secret_key để biết tài khoản của aiRedis index account_status_keys:{secret_key}; matched with users.mt5_secret_key to decide ownership |
Phân quyền xem theo user; đồng thời là khoá subscribe WS per-userPer-user visibility; also the per-user WS subscribe key |
| Token EA productEA product token | InpCloudBearerToken |
easupported.secret_token (mỗi product một token)(one per product) |
/easupported/{slug}/api/tmap/*; sha256 của nó là HMAC key EA Link; its sha256 is the EA Link HMAC key |
| TMap Bearer (legacy)TMap Bearer (legacy) | InpCloudBearerToken (EA cũ)(older EAs) |
tmap_secret_key → TMAP_SECRET_KEY trong .env; cũng là Cloudflare secret TMAP_SECRET_KEYin .env; also the Cloudflare secret TMAP_SECRET_KEY |
/api/tmap/*, Cloudflare /tmap/save|load|clear |
| Secret TLS AccountStatusBrokerAccountStatusBroker TLS secret | InpBrokerSecret |
BROKER_SECRET trong .env broker (nguồn chân lý); fxconsole hiển thị read-only qua BROKER_STREAM_SECRETin the broker .env (source of truth); fxconsole shows it read-only via BROKER_STREAM_SECRET |
HELLO|statuspub|v2|… |
| Token firehose adminAdmin firehose token | Trình duyệt admin (Inertia prop)Admin browser (Inertia prop) | broker_admin_secret |
subscribe_all trênon /status-ws, /vps-ws |
| Secret nội bộ brokerBroker internal secret | EaLinkBroker, VpsStreamBroker | broker_internal_secret |
/api/internal/* (X-Broker-Secret) |
monitor/README.html ghi EA_API_BEARER và TMAP_SECRET_KEY chỉ nằm trong .env. Hiện tại ea_api_bearer sửa được trên /admin/settings (DB thắng .env). Ô "TradingMap Secret" đã bị gỡ khỏi Settings: mỗi EA product có token riêng ở /admin/easupported; /api/tmap/* cũ vẫn chấp nhận TMAP_SECRET_KEY cho client cũ.
monitor/README.html says EA_API_BEARER and TMAP_SECRET_KEY live only in .env. Today ea_api_bearer is editable on /admin/settings (DB wins over .env). The "TradingMap Secret" field was removed from Settings: each EA product has its own token on /admin/easupported; the legacy /api/tmap/* still accepts TMAP_SECRET_KEY for old clients.
fxconsole TMap API
Backup thứ hai của Ticket Mapping (sau Cloudflare D1) cho EA GoldenRobber. Quy trình khôi phục: Khôi phục TMap; worker Cloudflare: Cloudflare TMap worker.
The second Ticket Mapping backup (after Cloudflare D1) for the GoldenRobber EA. Recovery procedure: TMap disaster recovery; Cloudflare worker: Cloudflare TMap worker.
File liên quanFiles
database/migrations/2026_04_25_000001_create_tmap_entries_table.php— schemaapp/Models/TmapEntry.php— model + fingerprintmodel + fingerprintapp/Http/Controllers/Api/TmapApiController.php— save/load/clear/heartbeat token chungsave/load/clear/heartbeat with the shared tokenapp/Http/Controllers/Api/EaSupportedApiController.php— cùng chức năng theo productsame features, per productconfig/app.php(tmap_secret_key),.env.example(TMAP_SECRET_KEY)
Bảng tmap_entriesTable tmap_entries
| CộtColumn | KiểuType | Mô tảDescription |
|---|---|---|
id | bigint PK | Auto-increment |
magic | varchar(32) | Magic number của EAEA magic number |
account | varchar(64) | Số tài khoản MT5MT5 account number |
ticket | unsigned bigint | Position ticket |
round_id | int | Round ID từ commentRound ID from the comment |
add_num | int | B#N hoặc S#N (0 = Init)B#N or S#N (0 = Init) |
is_dca_pos | boolean | true = DCAPos, false = DCANeg |
pos_type | int | 0 = Buy, 1 = Sell |
comment | varchar(255) | Comment gốcOriginal comment |
created_at / updated_at | timestamp | — |
Index: UNIQUE(magic, account, ticket) + INDEX(magic, account).
Indexes: UNIQUE(magic, account, ticket) + INDEX(magic, account).
Hành vi savesave behaviour
- REPLACE ALL: trong 1 transaction,
DELETE WHERE magic+accountrồiINSERTtoàn bộ entries — không append, không merge. - Từ 2026.09.04.1: trước khi ghi, server tính fingerprint
sha1của payload (không phụ thuộc thứ tự entry) và so với Redis keytmap_fp:{magic}:{account}(TTL 3600s). Trùng → trả{"ok":true,"count":N,"skipped":true}, không đụng DB. Lý do: EA đẩy lại toàn bộ map mỗi 5–10s kể cả khi không đổi, gây ~873k lần ghi/ngày và ~3 GB binlog/ngày trên database 179 MB, làm đầy đĩa. - Redis lỗi → fail open (vẫn ghi). TTL 1 giờ bảo đảm map được ghi lại ít nhất mỗi giờ. Mọi chỗ xoá dòng ngoài
save()phải gọiforgetFingerprint.
- REPLACE ALL: in one transaction,
DELETE WHERE magic+accountthenINSERTevery entry — no append, no merge. - Since 2026.09.04.1: before writing, the server computes an order-independent
sha1fingerprint of the payload and compares it with Redis keytmap_fp:{magic}:{account}(TTL 3600s). Match → returns{"ok":true,"count":N,"skipped":true}without touching the DB. Why: the EA re-pushes the full map every 5–10s even when unchanged, which caused ~873k writes/day and ~3 GB of binlog per day on a 179 MB database and filled the disk. - Redis error → fail open (write anyway). The 1-hour TTL guarantees a rewrite at least hourly. Any code that deletes rows outside
save()must callforgetFingerprint.
Setup và kiểm traSetup and checks
# Legacy: đặt TMAP_SECRET_KEY=<secret> trong .env rồi
cd monitor/fxconsole
php artisan config:clear
php artisan migrate
# Đếm entry đã sync
php artisan tinker
>>> App\Models\TmapEntry::where('magic', '21478308')->count();
# Dọn entry không cập nhật > 30 ngày
>>> App\Models\TmapEntry::where('updated_at', '<', now()->subDays(30))->delete();
# Legacy: set TMAP_SECRET_KEY=<secret> in .env, then
cd monitor/fxconsole
php artisan config:clear
php artisan migrate
# Count synced entries
php artisan tinker
>>> App\Models\TmapEntry::where('magic', '21478308')->count();
# Delete entries not updated for > 30 days
>>> App\Models\TmapEntry::where('updated_at', '<', now()->subDays(30))->delete();
Lệnh xoá trên đi thẳng qua model, không qua save(), nên fingerprint Redis vẫn còn tối đa 1 giờ: nếu EA gửi lại đúng map cũ, lần ghi có thể bị bỏ qua đến khi TTL hết. Xoá thêm key tmap_fp:* tương ứng nếu cần ghi lại ngay.
The delete above goes straight through the model, not save(), so the Redis fingerprint survives for up to an hour: if the EA re-sends the same map, the write may be skipped until the TTL expires. Also delete the matching tmap_fp:* key if you need an immediate rewrite.
MySQL, Redis, TimescaleDBMySQL, Redis, TimescaleDB
MySQL
Nguồn chân lý: users, settings, vpses, vps_commands, mt5_accounts, broker_profiles, lot_history, report_deals, easupported / ea_account / ea_config, tmap_entries / tmap_nodes, fx_rates (tỷ giá USDT/VND), bảng phân tích/lịch/tin. Cột mới 2026.09.13–14: users.currency, mt5_accounts.money_divisor, broker_profiles.server_divisors. VpsStreamBroker đọc vpses.secret_key và ghi vpses.account_logins; ba broker đọc settings.
Source of truth: users, settings, vpses, vps_commands, mt5_accounts, broker_profiles, lot_history, report_deals, easupported / ea_account / ea_config, tmap_entries / tmap_nodes, fx_rates (USDT/VND rate), analysis/calendar/news tables. New columns 2026.09.13–14: users.currency, mt5_accounts.money_divisor, broker_profiles.server_divisors. VpsStreamBroker reads vpses.secret_key and writes vpses.account_logins; three brokers read settings.
Redis
Trạng thái nóng và pub/sub. Prefix Laravel laravel-database- (broker phải đặt REDIS_PREFIX trùng). Không persist: sau khi Redis restart, dữ liệu được EA/agent/broker ghi lại.
Hot state and pub/sub. Laravel prefix laravel-database- (brokers must set the same REDIS_PREFIX). No persistence: after a Redis restart EAs, agents and brokers repopulate it.
TimescaleDB
Chỉ ohlc_data (qua DB::connection('timescale')). ohlc:aggregate chạy mỗi phút. Biến TIMESCALE_HOST, TIMESCALE_PORT, TIMESCALE_DATABASE, TIMESCALE_USERNAME, TIMESCALE_PASSWORD.
Only ohlc_data (via DB::connection('timescale')). ohlc:aggregate runs every minute. Variables TIMESCALE_HOST, TIMESCALE_PORT, TIMESCALE_DATABASE, TIMESCALE_USERNAME, TIMESCALE_PASSWORD.
| Key / channel Redis (không prefix)Redis key / channel (unprefixed) | Ai ghiWriter | Nội dungContents |
|---|---|---|
account_status:{id}, account_positions:{id}, account_experts:{id}, account_today_history:{id} | Laravel, AccountStatusBroker | Snapshot tài khoản, TTL 48hAccount snapshot, TTL 48h |
account_status_keys:{secret_key}, account_status_all_secret_keys | Laravel, AccountStatusBroker | Index tài khoản theo secret keyAccount index by secret key |
vps:agent:{id}:heartbeat, vps:agent:{id}:interval | VpsStreamBroker, Laravel | Heartbeat agent (TTL 15s), interval hot-reloadAgent heartbeat (TTL 15s), hot-reload interval |
ea_link:presence:{slug}:{composite_id}:{magic} | EaLinkBroker | Hash presence EA Link (TTL 90s), web coi là live nếu < 60sEA Link presence hash (TTL 90s); the web treats < 60s as live |
app_config:broker_admin_secret, app_config:broker_internal_secret, app_config:hb_interval_secs, app_config:stream_cadence_ms | Laravel | Mirror setting cho broker (tầng 2 sau DB)Setting mirrors for brokers (tier 2 after DB) |
tmap_fp:{magic}:{account} | Laravel | Fingerprint TMap, TTL 3600sTMap fingerprint, TTL 3600s |
ticks:{broker}:{symbol}, broker_latest:{broker}, brokers, broker:{broker}:symbols, calendar:{broker} | TickBroker | Tick stream, giá mới nhất, lịch kinh tế (key không prefix)Tick stream, latest quotes, economic calendar (no prefix) |
ChannelChannels vps:cmd, vps:state, ea:link:cfg, report:request | Laravel | Đẩy lệnh VPS, trạng thái, tín hiệu config, yêu cầu báo cáo (report:request không prefix)VPS command push, state changes, config signal, report request (report:request is unprefixed) |
CPU/RAM/disk/net của VPS chỉ sống trong Redis/stream, không lưu DB: hiển thị giá trị "lần cuối" cũ sẽ đánh lừa người xem. Riêng số tài khoản trên VPS: màn hình hiển thị dùng Redis heartbeat rồi fallback cột DB; màn hình quyết định deploy đếm mt5_accounts.
VPS CPU/RAM/disk/net live only in Redis/the stream, never the DB: a stale "last known" value would mislead viewers. VPS account count: display surfaces use the Redis heartbeat with a DB-column fallback; deploy-decision surfaces count mt5_accounts.
Trang /admin/settingsThe /admin/settings page
Setting lưu ở bảng MySQL settings; app_setting(key) đọc DB trước, trống thì dùng giá trị .env. Ô trống khi lưu = xoá override, quay về .env. Ô readonly chỉ hiển thị vì nguồn chân lý nằm ngoài app. Trang cũng hiện app_version để xác nhận build đang chạy. Hạ tầng Laravel thuần (DB_*, REDIS_*, APP_KEY/ENV/DEBUG/URL) không sửa ở đây. Setting phân tích thị trường đã chuyển sang /admin/analysis; thông tin Proxmox chuyển sang từng node ở /admin/proxmox/nodes.
Settings are stored in the MySQL settings table; app_setting(key) reads the DB first and falls back to .env. Saving a blank field deletes the override and falls back to .env. Readonly fields are display-only because their source of truth lives outside the app. The page also shows app_version to confirm the live build. Pure Laravel infra (DB_*, REDIS_*, APP_KEY/ENV/DEBUG/URL) is not editable here. Market-analysis settings moved to /admin/analysis; Proxmox credentials moved to per-node rows on /admin/proxmox/nodes.
| Key | Label | Env | Ý nghĩaMeaning |
|---|---|---|---|
ea_api_bearer | EA API Bearer Token | EA_API_BEARER | Bearer của AccountStatusUpdater. Trống = endpoint tắt.AccountStatusUpdater bearer. Empty = endpoint disabled. |
favicon_url, html_title, page_heading, app_slogan | Favicon URL, Browser tab title, Dashboard heading, Header slogan | FAVICON_URL, APP_HTML_TITLE, APP_PAGE_HEADING, APP_SLOGAN | Nhận diện giao diện.Branding. |
app_public_url | Public app URL | APP_PUBLIC_URL | URL công khai để sinh link tuyệt đối trong email và url()/route(), thay cho sửa APP_URL + restart. Không có / cuối.Public URL for absolute links in emails and url()/route(), instead of editing APP_URL + restarting. No trailing /. |
datetime_format, timezone | Datetime format, App timezone | APP_DATETIME_FORMAT, APP_TIMEZONE | Pattern date() của PHP; timezone của app và Carbon.PHP date() pattern; app and Carbon timezone. |
staleness_multiplier | Staleness multiplier | STALENESS_MULTIPLIER | Ngưỡng "lost" = interval auto refresh × hệ số (1–20)."Lost" threshold = auto-refresh interval × this (1–20). |
default_heartbeat_interval | Default heartbeat interval (s) | DEFAULT_HEARTBEAT_INTERVAL | Giây giữa 2 heartbeat agent, dùng cho VPS mới tạo (1–3600).Seconds between agent heartbeats, seeded into newly created VPS rows (1–3600). |
mail_enabled, mail_mailer, mail_host, mail_port, mail_scheme, mail_username, mail_password, mail_from_address, mail_from_name | MAIL_* | SMTP (Mailjet), áp lại mỗi request nên đổi credential không cần restart. mail_enabled tắt = mọi mail đi vào transport array (bỏ im lặng) — dùng khi bảo trì.SMTP (Mailjet), re-applied per request so credentials rotate without a restart. mail_enabled off = all mail goes to the array transport (silently discarded) — use during maintenance. | |
mail_type_password_otp, mail_type_equity_drop, mail_type_deploy_status, mail_type_report_ready, mail_type_analysis_digest, mail_type_account_blocked | Mail — per-type | MAIL_TYPE_* | Mỗi loại mail một checkbox, kiểm tra bởi App\Support\MailType trước khi dựng mail nên loại bị tắt không vào queue. Trống = bật. Tắt một loại không ảnh hưởng chuông trong app; riêng mail_type_password_otp tắt là không ai đổi/quên mật khẩu được. mail_enabled tắt vẫn đè lên tất cả.One checkbox per mail kind, checked by App\Support\MailType before the Mailable is built, so a disabled kind never reaches the queue. Blank = on. Turning one off leaves the in-app bell untouched; mail_type_password_otp off means nobody can reset or change a password. mail_enabled off still overrides them all. |
hb_interval_secs | Stream: HB cadence (s) | HB_INTERVAL_SECS | Nhịp frame keep-alive tối giản của EA AccountStatusUpdater khi không ai xem (5–120s).Cadence of the AccountStatusUpdater EA's minimal keep-alive frame while nobody watches (5–120s). |
stream_cadence_ms | Stream: full-push cadence (ms) | STREAM_CADENCE_MS | Nhịp push đầy đủ khi có trình duyệt subscribe (0–30000; 0 = mỗi tick, giới hạn bởi InpMinPushIntervalMs; mặc định 900).Full-push cadence while a browser is subscribed (0–30000; 0 = per tick, capped by InpMinPushIntervalMs; default 900). |
broker_admin_secret | Broker admin firehose token | BROKER_ADMIN_SECRET | Cho admin mở stream "mọi tài khoản" trên AccountStatusBroker và VpsStreamBroker. Trống ở mọi nơi = tắt Stream, quay về polling.Lets admins open the "all accounts" stream on AccountStatusBroker and VpsStreamBroker. Empty everywhere = Stream mode off, polling only. |
broker_internal_secret | EALinkBroker Secret Key | BROKER_INTERNAL_SECRET | X-Broker-Secret cho call broker → Laravel. Dùng chung cho EaLinkBroker (/api/internal/ea-link/key) và VpsStreamBroker (/api/internal/broker/command-ack): đổi là ảnh hưởng cả hai. Trống ở mọi nơi = call nội bộ fail closed.X-Broker-Secret for broker → Laravel calls. Shared by EaLinkBroker (/api/internal/ea-link/key) and VpsStreamBroker (/api/internal/broker/command-ack): rotating it affects both. Empty everywhere = internal calls fail closed. |
broker_stream_secret | Broker stream secret readonly | BROKER_STREAM_SECRET | Hiển thị để copy vào InpBrokerSecret; phải trùng BROKER_SECRET trong .env của AccountStatusBroker.Shown for copying into InpBrokerSecret; must equal BROKER_SECRET in the AccountStatusBroker .env. |
guac_json_secret_key | Guacamole JSON Auth secret readonly | GUAC_JSON_SECRET_KEY | Khoá ký token remote desktop; phải trùng JSON_SECRET_KEY của container guacamole.Signs remote-desktop tokens; must equal the guacamole container's JSON_SECRET_KEY. |
guac_public_url | Guacamole public URL | GUAC_PUBLIC_URL | URL HTTPS của webapp Guacamole, không / cuối. Trống = tắt nút Connect.HTTPS URL of the Guacamole webapp, no trailing /. Empty = Connect button disabled. |
vps_init_bootstrap_token | VPS init bootstrap token | VPS_INIT_BOOTSTRAP_TOKEN | Bearer cho /api/agent/init-config, nướng sẵn vào vps-agent.ini.init-template. Trống = 503.Bearer for /api/agent/init-config, baked into vps-agent.ini.init-template. Empty = 503. |
vps_broker_host, vps_broker_port, vps_broker_url | VpsStreamBroker hostname / port / connection string | VPS_BROKER_HOST, VPS_BROKER_PORT, VPS_BROKER_URL | Dùng khi cài agent để render stunnel.conf. Host phải trùng CN cert (CERT_HOST); port mặc định 9605; connection string trống = tự ghép tls://host:port.Used at agent install to render stunnel.conf. Host must match the cert CN (CERT_HOST); port defaults to 9605; blank connection string = derived tls://host:port. |
report_schedule_frequency, report_close_time_dst, report_close_time_std, report_market_tz, report_lookback_months | Reports: … | REPORT_* | Lịch report:generate-all: everyday / weekend, giờ đóng cửa UTC theo mùa DST / chuẩn, timezone để xét DST (mặc định EET), số tháng lịch sử (mặc định 3).report:generate-all schedule: everyday / weekend, UTC close time for DST / standard season, timezone for the DST check (default EET), months of history (default 3). |
news_retention_days | News: keep headlines for (days) | NEWS_RETENTION_DAYS | Số ngày giữ tin (mặc định 2).Days to keep headlines (default 2). |
usdt_vnd_interval_minutes | USDT/VND rate: update every (minutes) | USDT_VND_INTERVAL_MINUTES | Nhóm "Tỷ giá". Số phút giữa hai lần fx:sync-usdt-vnd gọi Binance P2P (mặc định 5, 1–60). Ngắn quá dễ bị chặn IP. Xem Đổi USD ↔ VND."Exchange rate" group. Minutes between two Binance P2P calls by fx:sync-usdt-vnd (default 5, 1–60). Too short risks an IP block. See USD ↔ VND switch. |
Setting mà broker đọcSettings the brokers read
Từ commit cb272d5 (fxconsole 2026.09.12.3), broker đọc theo thứ tự bảng MySQL settings → Redis app_config:* → .env của broker, cache 30s mỗi key (lỗi DB thử lại sau 5s). Nhờ đọc DB trước, deploy hoặc Redis restart không làm mất giá trị trước khi admin mở trang. Broker cần DB_* trong .env của nó (deploy.sh tự điền từ monitor/fxconsole/.env).
Since commit cb272d5 (fxconsole 2026.09.12.3) brokers read in the order MySQL settings table → Redis app_config:* → the broker's own .env, with a 30s per-key cache (DB failures retry after 5s). Because the DB is read first, a deploy or Redis restart no longer blanks values until an admin opens a page. Brokers need DB_* in their .env (deploy.sh fills them from monitor/fxconsole/.env).
| Setting | AccountStatusBroker | VpsStreamBroker | EaLinkBroker |
|---|---|---|---|
broker_admin_secret | ✓ | ✓ | — |
broker_internal_secret | — | ✓ | ✓ |
hb_interval_secs | ✓ | — | — |
stream_cadence_ms | ✓ | — | — |
Laravel vẫn ghi mirror Redis mỗi khi lưu Settings (BrokerAdminSecret::syncToRedis(), BrokerInternalSecret::syncToRedis(), StreamConfig::syncToRedis()) và mỗi lần admin tải trang (HandleInertiaRequests). TickBroker không đọc setting nào: secret của nó chỉ là TICK_BROKER_SECRET trong .env.
Laravel still writes the Redis mirrors on every Settings save (BrokerAdminSecret::syncToRedis(), BrokerInternalSecret::syncToRedis(), StreamConfig::syncToRedis()) and on every admin page load (HandleInertiaRequests). TickBroker reads no settings: its only secret is TICK_BROKER_SECRET in .env.
.env.example đã cũStale .env.example comments
Comment trong monitor/fxconsole/.env.example ghi BROKER_INTERNAL_SECRET "phải khớp chính xác" với .env của VpsStreamBroker và "rotate bằng cách restart cả hai container". Đó là hành vi trước cb272d5. Hiện tại chỉ cần đổi trên /admin/settings; broker nhận trong ~30s. Giá trị .env chỉ là tầng cuối cùng.
The comment in monitor/fxconsole/.env.example says BROKER_INTERNAL_SECRET "MUST exactly match" the VpsStreamBroker .env and to "rotate by bouncing both containers". That was the behaviour before cb272d5. Today you change it on /admin/settings and brokers pick it up within ~30s; the .env value is only the last tier.
Phân tích, báo cáo và schedulerAnalysis, reports and the scheduler
- Phân tích thị trường:
/admin/analysisquản lý engine, model, interval, key Finnhub, URL sidecar, danh sách cặp (broker, symbol) và prompt tuỳ chỉnh, nút chạy từng cặp / chạy tất cả, trạng thái sidecar và đăng nhập lại OAuth.analysis:rungọihttp://claude-cli:8788(POST /analyze). Nội dung nhận định sinh bằng tiếng Việt, không dịch. Gói subscription chạy nền 24/7 là vùng xám điều khoản: giữ số cặp ít và interval thưa. - Báo cáo: user bấm Generate → Laravel lưu yêu cầu và publish
report:request→ AccountStatusBroker đẩyreport_requesttới EA → EA stream deal theo từng tuần quaPOST /api/account-status. Chunk đầu xoá deal cũ, chunk cuối tính tổng kết và xoá yêu cầu. Admin có nút "Run reports now" (POST /admin/reports/run-now).
- Market analysis:
/admin/analysismanages the engine, model, interval, Finnhub key, sidecar URL, the (broker, symbol) watchlist and custom prompts, per-pair / run-all buttons, sidecar health and OAuth re-login.analysis:runcallshttp://claude-cli:8788(POST /analyze). Narratives are generated in Vietnamese and not translated. Running a subscription 24/7 from a backend is a terms-of-service grey area: keep pairs few and intervals relaxed. - Reports: a user clicks Generate → Laravel stores the request and publishes
report:request→ AccountStatusBroker pushesreport_requestto the EA → the EA streams deals week by week throughPOST /api/account-status. The first chunk truncates old deals, the last computes the summary and clears the request. Admins have "Run reports now" (POST /admin/reports/run-now).
| Lệnh lịchScheduled command | NhịpCadence | ViệcJob |
|---|---|---|
ohlc:aggregate | mỗi phútevery minute | Gộp tick thành nến OHLCAggregates ticks into OHLC bars |
brokers:sync | 5 phút5 min | Đồng bộ danh sách brokerSyncs the broker list |
calendar:sync | 5 phút5 min | Mirror lịch kinh tế từ Redis calendar:{broker}Mirrors the economic calendar from Redis calendar:{broker} |
news:sync | 2 phút2 min | Tin RSS + dọn theo news_retention_daysRSS headlines + prune by news_retention_days |
fx:sync-usdt-vnd | mỗi phút, gọi Binance khi đủ usdt_vnd_interval_minutesevery minute, calls Binance once usdt_vnd_interval_minutes has passed | Tỷ giá USDT/VND (median 10 quảng cáo SELL Binance P2P) vào fx_ratesUSDT/VND rate (median of 10 Binance P2P SELL ads) into fx_rates |
report:generate-all | mỗi phút, chỉ chạy khi tới giờ theo setting Reportsevery minute, runs only when due per the Reports settings | Yêu cầu báo cáo cho mọi tài khoảnRequests reports for every account |
analysis:run --scope=global / --scope=weekly | mỗi phút, khi tới hạnevery minute, when due | Sinh thẻ phân tíchBuilds analysis cards |
fxconsole:scan-lost-vpses, fxconsole:purge-removed-accounts | 5 phút5 min | Đánh dấu VPS mất kết nối, dọn tài khoản đã gỡFlags lost VPSs, purges removed accounts |
fxconsole:detect-mt5-drift | 10 phút10 min | Phát hiện MT5 lệch trạng tháiDetects MT5 state drift |
fxconsole:purge-completed-commands, fxconsole:close-stale-remote-sessions | mỗi giờhourly | Dọn lệnh VPS xong, đóng phiên remote treoPurges finished VPS commands, closes stale remote sessions |
proxmox:finish-provisions | mỗi phútevery minute | Hoàn tất VM clone từ ProxmoxFinishes Proxmox-cloned VMs |
Phiên remote GuacamoleGuacamole remote sessions
- Admin bấm Connect trên
/vps/{id}→POST /admin/vps/{id}/remote-session.Admin clicks Connect on/vps/{id}→POST /admin/vps/{id}/remote-session. - Server giải mã mật khẩu VNC của VPS, dựng mô tả kết nối JSON (
protocol=vnc), ký HMAC-SHA256 rồi mã hoá AES-128-CBC bằngGUAC_JSON_SECRET_KEY(32 hex), ghi dòng audit.The server decrypts the VPS's VNC password, builds a JSON connection descriptor (protocol=vnc), signs it with HMAC-SHA256 and encrypts with AES-128-CBC usingGUAC_JSON_SECRET_KEY(32 hex), and writes an audit row. - Trả URL
<GUAC_PUBLIC_URL>/?data=<base64>hết hạn sau 60s; trình duyệt mở tab mới, Guacamole giải mã và gọi guacd.Returns<GUAC_PUBLIC_URL>/?data=<base64>valid for 60s; the browser opens a new tab, Guacamole decodes it and dials guacd.
Dùng VNC thay RDP vì RDP khoá desktop console khi ngắt, làm hỏng thao tác GUI tự động của CmdAgent (inject URL WebRequest vào MT5 bằng SendInput). Thiếu guac_json_secret_key hoặc guac_public_url → 503. Chi tiết: Guacamole.
VNC is used instead of RDP because RDP locks the console desktop on disconnect, breaking CmdAgent's GUI automation (injecting the MT5 WebRequest URL via SendInput). Missing guac_json_secret_key or guac_public_url → 503. Details: Guacamole.
User và quyềnUsers and roles
- Hai role:
adminvàuser(User::isAdmin()). Middlewareadmintrả 403 cho mọi route/admin/*với user thường. max_deployed_accounts: hạn mức tự deploy.blocked_at: user bị khoá không đăng nhập được và bị đá khỏi session.mt5_accounts.user_id= account được gán cho user: quyết định/deployments,/easupported(EA của tôi), ô tổng trên navbar và/performance. Dashboard vẫn khớp theomt5_secret_key.mt5_secret_key: khoá gắn tài khoản MT5 với user (trùngInpAccountSecretKeytrên EA). Đổi / sinh lại ở/settings.- Admin: đổi role, hạn mức, khoá, chuyển toàn bộ tài khoản sang user khác (
/admin/users/{id}/reassign-all) hoặc từng tài khoản (/deployments/{id}/reassign). Lưu ý: chuyển chủ không đổi secret key đang cài trên EA.
- Two roles:
adminanduser(User::isAdmin()). Theadminmiddleware returns 403 on every/admin/*route for regular users. max_deployed_accounts: self-deploy quota.blocked_at: a blocked user cannot log in and is kicked out of their session.mt5_accounts.user_id= the accounts assigned to the user: drives/deployments,/easupported(My EAs), the navbar totals and/performance. The Dashboard still matches bymt5_secret_key.mt5_secret_key: ties MT5 accounts to the user (equalsInpAccountSecretKeyon the EA). Change / regenerate at/settings.- Admins: change role, quota, block, move all accounts to another user (
/admin/users/{id}/reassign-all) or a single account (/deployments/{id}/reassign). Note: reassigning ownership does not change the secret key already set on the EA.
Song ngữ (i18n)Bilingual UI (i18n)
- Từ điển
resources/js/lib/i18n/vi.tsvàen.ts, kiểu ởtypes.ts. Tiếng Việt là nguồn/mặc định:t(key)fallback về VI rồi về chính key. - Trong page:
const t = useT();rồit('ns.key'). Mảng label dựng bằng hàmbuild(t). - Locale là shared Inertia prop (
user.locale, hoặc session, mặc địnhvi); đổi quaPOST /locale. - Thêm ngôn ngữ: tạo
<lang>.ts(copy key củavi.ts), thêm vàoMESSAGES, thêm lựa chọn trongLanguageSwitcher.tsxvà validation phía server (LocaleController+HandleInertiaRequests). Không cần sửa page. - Nội dung phân tích giữ tiếng Việt.
- Dictionaries
resources/js/lib/i18n/vi.tsanden.ts, types intypes.ts. Vietnamese is the source/default:t(key)falls back to VI, then to the raw key. - In a page:
const t = useT();thent('ns.key'). Label arrays are built with abuild(t)function. - The locale is a shared Inertia prop (
user.locale, else session, defaultvi); switched viaPOST /locale. - Adding a language: create
<lang>.ts(copy the keys ofvi.ts), add it toMESSAGES, add an option inLanguageSwitcher.tsxand the server-side validation (LocaleController+HandleInertiaRequests). No page files change. - Analysis content stays in Vietnamese.
Quy tắc versionVersioning rule
app_version mỗi lần sửa codeBump app_version on every code change
Mọi thay đổi fxconsole (backend hay frontend) phải bump app_version trong config/app.php. Định dạng CalVer YYYY.MM.DD.N: ngày hôm nay + số thứ tự trong ngày, sang ngày mới thì N về 1. Giá trị hiện trên /admin/settings để xác nhận build đã lên sau git pull + restart. Hiện tại: 2026.09.14.1 (có thể override bằng APP_VERSION). EA có quy tắc riêng: AccountStatusUpdater.mq5 dùng #property version, GoldenRobber.mq5 dùng GR_BUILD.
Every fxconsole change (backend or frontend) bumps app_version in config/app.php. Format is CalVer YYYY.MM.DD.N: today's date + same-day counter, reset N to 1 on a new day. The value is shown on /admin/settings so you can confirm the build is live after git pull + restart. Current: 2026.09.14.1 (overridable with APP_VERSION). EAs follow their own rule: AccountStatusUpdater.mq5 uses #property version, GoldenRobber.mq5 uses GR_BUILD.
| Version | Thay đổiChange |
|---|---|
2026.09.04.1 | Bỏ ghi lại TMap khi map không đổi (fingerprint Redis)Skip TMap rewrite when the map is unchanged (Redis fingerprint) |
2026.09.12.1 | EA Link hot-reload realtime (GoldenRobber 1.42 b1, EaLinkBroker)EA Link realtime hot-reload (GoldenRobber 1.42 b1, EaLinkBroker) |
2026.09.12.2 | Label broker_internal_secret đổi thành "EALinkBroker Secret Key"broker_internal_secret label renamed to "EALinkBroker Secret Key" |
2026.09.12.3 | Broker đọc secret/setting thẳng từ bảng settings (MySQL)Brokers read secrets/settings straight from the settings table (MySQL) |
2026.09.13.1 | Thông báo "deploy thành công" mở /dashboard/accounts/{login}"Deploy succeeded" notifications open /dashboard/accounts/{login} |
2026.09.13.2 | Trang EA của tôi (/easupported) cho user; bảng account/payload dùng toolbar chungMy EAs page (/easupported) for users; account/payload tables use the shared toolbar |
2026.09.13.3 | Nút đổi USD ↔ VND theo tỷ giá Binance P2P (fx:sync-usdt-vnd, bảng fx_rates)USD ↔ VND switch at the Binance P2P rate (fx:sync-usdt-vnd, fx_rates table) |
2026.09.13.4 | /easupported khớp account được gán (thêm login + server)/easupported matches assigned accounts (adds login + server) |
2026.09.13.5 | Account cent (USC) hiển thị ÷100 = USD (cờ theo từng account)Cent (USC) accounts shown ÷100 = USD (per-account flag) |
2026.09.13.6 | Cent / Standard hàng loạt + tự động theo server Cent của brokerBulk Cent / Standard + automatic by broker Cent servers |
2026.09.14.1 | /easupported khớp login + tên broker (account GoldenRobber); heartbeat EA đồng bộ composite_id/easupported matches login + broker name (GoldenRobber accounts); the EA heartbeat syncs composite_id |
2026.09.14.2 | Hệ số chia /1 · /10 · /100 thay cho Cent / Standard (theo account và theo server)Money divisor /1 · /10 · /100 replaces Cent / Standard (per account and per server) |
2026.09.14.3 | Modal Nhà môi giới cuộn được, nút Lưu / Hủy luôn hiệnBroker modal scrolls, Save / Cancel always visible |
2026.09.16.1 | Công tắc chia theo hệ số / xem số gốc cho từng user trong menu tiền tệPer-user divided / raw figures switch in the currency menu |
2026.09.17.1 | Mô tả gói triển khai là ô nhiều dòng (release note), giữ xuống dòng khi hiển thịDeployment package description is a multi-line box (release note), line breaks kept when shown |
2026.09.22.1 | Chart "Biến động số dư theo ngày" chọn được kiểu đường / cộtThe daily balance chart can switch between line and bars |
2026.09.28.2 | /easupported và /admin/easupported: ẩn account có heartbeat cuối quá 5 ngày/easupported and /admin/easupported: hide accounts whose last heartbeat is more than 5 days old |
2026.09.28.3 | /vps: header nhóm Proxmox node hiện thêm tổng tài khoản MT5 (vd. (5 VPS · 26 / 38 tài khoản))/vps: Proxmox node group headers also show the MT5 account total (e.g. (5 VPS · 26 / 38 accounts)) |
2026.09.28.4 | Cột Node (VPS) ở Dashboard, trang account và báo cáo IP tìm VPS theo login, hiện tên VPS hiện tại (đổi tên VPS không còn bị giữ tên cũ)The Node (VPS) cell on the Dashboard, account page and IP report finds the VPS by login and shows its current name (a renamed VPS no longer shows its old name) |
2026.09.29.1 | Lưu VPS đã liên kết Proxmox trên /vps cũng đổi tên VM trên PVE cho khớp (PVE lỗi thì vẫn lưu, banner báo lỗi); tên VPS Proxmox chỉ gồm chữ, số, -, .. Không đổi tên máy WindowsSaving a Proxmox-linked VPS on /vps also renames its PVE VM to match (on a PVE error the save stands and the banner reports it); Proxmox VPS names allow letters, digits, - and . only. The Windows machine name is not changed |
2026.09.29.2 | Dialog Edit VPS có ô "Đổi tên máy Windows theo tên VPS và khởi động lại VM ngay" (mặc định tắt): xếp lệnh rename_host cho agent 1.3.4-phase11+, mọi MT5 trên VPS ngừng ~1–2 phút rồi tự mở lạiVPS Edit dialog gets "Rename the Windows machine to the VPS name and restart the VM now" (off by default): queues rename_host for agent 1.3.4-phase11+; every MT5 on the VPS stops ~1–2 minutes, then relaunches |
Reverse proxy, migration và DockerReverse proxy, migrations and Docker
Reverse proxy & nhiều domainReverse proxy & multiple domains
- Proxy phía trước phải chuyển tiếp
X-Forwarded-ProtovàX-Forwarded-Host; đặtTRUSTED_PROXIES=*để Laravel tin header này (vàRequest::ip()là IP VPS thật cho báo cáo IP). - Redirect hoặc form sai scheme: kiểm tra
APP_URL(hoặc settingapp_public_url), header forwarded, rồi chạyphp artisan config:clearsau khi đổi.env. - EA
WebRequestcần HTTPS với cert hợp lệ. Cert self-signed lỗi vớiGetLastError = 4014(CERT_VERIFICATION_FAILED). Host phải có trong Tools → Options → Expert Advisors → Allow WebRequest for listed URL.
- The front proxy must forward
X-Forwarded-ProtoandX-Forwarded-Host; setTRUSTED_PROXIES=*so Laravel trusts them (andRequest::ip()is the real VPS IP for the IP report). - Wrong-scheme redirects or forms: check
APP_URL(or theapp_public_urlsetting), the forwarded headers, then runphp artisan config:clearafter editing.env. - EA
WebRequestrequires HTTPS with a valid certificate. Self-signed certs fail withGetLastError = 4014(CERT_VERIFICATION_FAILED). The host must be in Tools → Options → Expert Advisors → Allow WebRequest for listed URL.
Chạy và cập nhậtRun and update
# Dev bằng Docker, mở http://localhost:8080
cd monitor/fxconsole
docker compose up --build
# Sau khi pull code mới (container app tự migrate khi boot; chạy tay nếu cần)
php artisan migrate
php artisan config:clear # khi thêm config mới
# Production: toàn bộ fxconsole + 4 broker
bash deploy.sh
# Dev with Docker, open http://localhost:8080
cd monitor/fxconsole
docker compose up --build
# After pulling new code (the app container migrates on boot; run by hand if needed)
php artisan migrate
php artisan config:clear # when new config was added
# Production: fxconsole + the 4 brokers
bash deploy.sh
deploy.sh (gốc repo) chạy git restore ., checkout + pull main, down / up -d --build fxconsole, rồi từng broker (AccountStatusBroker, TickBroker, VpsStreamBroker, EaLinkBroker) sau khi tự tạo .env từ .env.example và điền DB_*. Lần đầu (hoặc khi refresh token hết hạn) phải đăng nhập claude-cli bằng tay: docker compose exec -it claude-cli claude login. Hướng dẫn đầy đủ: Hướng dẫn deploy.
deploy.sh (repo root) runs git restore ., checks out + pulls main, down / up -d --build for fxconsole, then each broker (AccountStatusBroker, TickBroker, VpsStreamBroker, EaLinkBroker) after creating its .env from .env.example and filling DB_*. On first run (or when the refresh token expires) log in to claude-cli by hand: docker compose exec -it claude-cli claude login. Full guide: Deployment guide.
APP_KEYAPP_KEY
Không bao giờ chạy php artisan key:generate --force trên instance đang có dữ liệu: đổi key làm mọi cột mã hoá (vpses.vnc_password, mt5_accounts.password_encrypted, cookie session…) không giải mã được nữa. Container chỉ tạo key khi .env chưa có.
Never run php artisan key:generate --force on an instance with data: a new key makes every encrypted column (vpses.vnc_password, mt5_accounts.password_encrypted, session cookies…) undecryptable. The container only generates a key when .env has none.