Commit Graph
100 Commits
Author SHA1 Message Date
junvandGitHub 3a1d80bb86 Merge pull request #98 from wahyd4/dependabot/npm_and_yarn/multi-c4a89f1b01
build(deps-dev): bump vite and @vitejs/plugin-react
2026-07-16 10:14:05 +10:00
junvandGitHub 2980138ae0 Merge pull request #99 from wahyd4/dependabot/npm_and_yarn/echarts-6.1.0
build(deps): bump echarts from 5.6.0 to 6.1.0
2026-07-16 10:13:22 +10:00
junv bf3e2e80cf feat(links): auto-create non-existent tags on link create, lowercase all
Select2 submits raw text as the value for new tags typed into the tag
box (tags: true + createTag in link_form.html). The previous
ModelMultipleChoiceField silently dropped those values because they
were not PKs of existing tags, so new tags were never persisted.

Add TagInputField, a tolerant ModelMultipleChoiceField subclass:
  - existing tag PKs resolve as before
  - raw text is treated as a new (or case-insensitive-existing) tag
  - names are stripped and lowercased before get_or_create so the tag
    namespace stays consistent and case-insensitive duplicates are
    avoided. The Tag.save() hook still derives the slug.

LinkForm now uses TagInputField for its tags field, so both
LinkCreateView and LinkUpdateView pick up the new behaviour. The
Select2 createTag callback in link_form.html now lowercases the term
so users see immediately what will be stored.

Add 7 regression tests in TestLinkTagAutoCreate covering: new tag
created + lowercased; multiple new tags; existing tag reused (no
duplicate); mixed PK + new-text submission; no tags submitted; empty
or whitespace-only submission ignored; whitespace stripped.
2026-07-16 10:10:07 +10:00
junv 2625ef10b4 test(pricemon): pin telegram notification contract
Add integration tests covering the price-drop → Telegram alert flow:

- Sends Telegram MarkdownV2 message to global SiteSettings chat_id when
  detected price <= watcher.alert_price_threshold.
- No alert when global creds missing.
- No alert when price above threshold.
- No alert when threshold unset on watcher.
- Non-recurring watcher with an undismissed alert is not re-notified.
- Telegram API failure does not break the check (snapshot + alert row
  still persist, last_price updates).
2026-07-16 09:50:37 +10:00
junv 4a5f0f63ee fix(pricemon): load i18n in _alerts partial and pass alerts to include
AlertsPartialView (htmx-polled every 30s from the dashboard) returned
500 because _alerts.html used {% trans %} without {% load i18n %}.
Added the load tag so the partial renders correctly.

The dashboard's inline {% include 'pricemon/_alerts.html' %} also
relied on an undefined 'alerts' context var (DashboardView sets
'active_alerts'), so the alerts panel rendered empty even when alerts
existed. Pass alerts=active_alerts explicitly via the include.

Add regression tests covering both the dashboard render with an alert
and the alerts partial endpoint.
2026-07-16 09:34:15 +10:00
junv 3966740a4c Update 2026-07-15 15:51:37 +10:00
junv d26f332ef9 update video player! 2026-07-15 15:41:04 +10:00
junv 93f1a1ccae ui update 2026-07-15 15:37:19 +10:00
junv 647ce87359 Add price monitor toggle 2026-06-23 16:25:59 +10:00
junvandGitHub cdae61ae4c Merge pull request #94 from wahyd4/fix/invest-report-tag-alias
fix: show legacy investment reports on invest dashboard
2026-06-14 09:05:48 +10:00
junvandGitHub 1fa81a1cdc Merge pull request #93 from wahyd4/feat/invest-chart-value-mode
feat: add invest chart value mode
2026-06-14 08:50:26 +10:00
junvandGitHub 2dadf8718f Merge pull request #92 from wahyd4/feat/invest-agent-performance-main
feat: add cashflow-adjusted invest dashboard to main
2026-06-14 08:22:48 +10:00
junv 466b9c1fcb add testing feature 2026-05-23 10:15:09 +10:00
junv 227bc9b1de Update price monitor settings 2026-05-23 10:15:09 +10:00
junvandCopilot d99a362a37 jbot: keep avatar thinking during TTS and protect history storage
- Use a thinking/running face while speech audio is being generated
- Switch to speaking only when playback actually begins
- Add quota-safe history persistence that strips audio from older entries
  instead of crashing localStorage writes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 20:34:57 +10:00
junvandCopilot 4b2a4621fb jbot: delay TTS status and strip markdown from speech
- Show a dedicated '生成语音中…' state while TTS is being generated
- Only switch to '播放中…' once audio is actually ready/playing
- Strip markdown formatting from the spoken text while preserving the raw
  assistant message in the chat UI
- Render assistant messages as lightweight markdown in the bubble view

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 20:34:57 +10:00
junv dcb31838d9 Update UI 2026-05-17 14:13:38 +10:00
junvandCopilot 21fd058d46 jbot: fix voice button stuck state + add global fallback handlers
Fixes:
1. Race condition: after await startRec(), check isSpaceDownRef again —
   fast-tap released before mic ready was leaving phase stuck at 'listening'
2. Global window mouseup/touchend/touchcancel listeners as fallback so
   releasing outside the button boundary always triggers handleSpaceUp
3. Show ✕ cancel button during ALL non-idle phases (was only thinking/speaking)
4. handleCancel now stops the recorder if called during listening/acquiring
5. Safety auto-cancel timeout: if stuck in 'listening' > 60s, auto-stop
6. Add stopRec to handleSpaceDown dep array

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:38 +10:00
junvandCopilot 2c41b46da0 jbot: cancel button + abort + better Hermes error message
- VoiceButton gets a red ✕ cancel button that appears during thinking/speaking
- AbortController per pipeline run — ✕ aborts the in-flight apiChat fetch,
  stops TTS playback, and returns to idle cleanly
- Hermes connection-refused error shows readable message instead of raw stack
- apiChat() accepts optional AbortSignal (4th arg), passed from App.jsx
- handleSpaceUp dep array updated

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot bb3fe1b554 jbot: disable local memory in Hermes Agent mode
In Hermes mode JBOT is a pure voice UI — memory is owned by the Agent:
- Backend: sys prompt excludes local memories when backend_mode=hermes
- Backend: config_view now returns backendMode so the frontend can adapt
- Frontend: App.jsx loads backendMode on mount via apiConfig()
- Frontend: memories not passed to apiChat() when isHermesMode
- Frontend: memory extraction (apiExtractMemory) skipped when isHermesMode
- Frontend: MemoryPanel hidden in Hermes mode; replaced by subtle
  '🧠 Hermes Agent 模式 · 记忆由 Agent 管理' badge

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot 76603425a5 jbot: add Hermes Agent backend mode
- New DB fields: hermes_url, hermes_api_key, backend_mode (openrouter/hermes)
- Migration 0002 adds the three fields to JbotApiConfig
- _do_hermes_chat(): streams /v1/chat/completions from Hermes Agent,
  collects hermes.tool.progress SSE events, returns (final_text, tool_calls)
- chat_view branches on backend_mode; Hermes response includes toolCalls list
- Settings page: new '🧠 AI 后端模式' section — radio to toggle mode,
  conditional Hermes URL + API key fields, LLM model field moved under
  OpenRouter mode
- ChatPanel: ToolCallsList component renders collapsible '🔧 Hermes 使用了
  N 个工具' widget before the bot reply — tool calls are NOT spoken via TTS
- CSS: .tool-calls, .tool-calls-* styles for the tool progress widget

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot d944c3ecf9 jbot: remove right-footer wrapper — vbtn-wrap is direct flex child
right-footer served no purpose after hint text was removed. vbtn-wrap
is now a direct flex child of right-col: white bar at the bottom on
desktop, position:fixed on mobile — no stray white boxes anywhere.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot c60b1c3bd9 jbot: remove white background from chat panel — blends with right column
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot 7cd36ad81b jbot: remove hint text from bottom footer — keep only the voice button
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot 85da425c38 jbot: anchor voice button to bottom via unified right-footer
- Merged hint text + VoiceButton into a single .right-footer div
  (flex column, white bg, border-top) so they form one solid footer bar
  visually anchored to the bottom of the right column — no more floating
- .right-col gets min-height:0 so flex children can shrink properly
- .vbtn-wrap is now a transparent width:100% wrapper inside right-footer
  (desktop); mobile keeps its own fixed-position overlay
- .right-footer { display: none } on mobile (fixed vbtn-wrap takes over)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot 90ebd68511 jbot: fix Settings scrollbar + remove redundant history link
- Remove 'overflow:hidden' from html/body in index.css — it was blocking
  scroll on all pages (Settings, History). The chat page is self-contained
  via .app {height:100dvh; overflow:hidden} so body lock is unnecessary.
  .app reverted back to height:100dvh (self-sufficient, not body-relative)
- Remove duplicate 历史记录 link from the right-column hint bar; it
  already appears in the ChatPanel header, so three links was redundant

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot 3cbbaac083 jbot: reorganize layout — memory+hint to right, interval to settings
- Move MemoryPanel from bottom of left column to top of right column
  (above chat messages, with a clean border-bottom separator)
- Move hint text ('按住 Space 或按钮说话 · 音色') from left column
  to a compact bar on the right column between chat and voice button;
  hidden on mobile to save space
- Remove 切换间隔 slider from left column; add it to Settings page
  under 🤖 机器人身份 section with proper slider+value layout
- Add switchIntervalMs:10000 to DEFAULT_SETTINGS in settingsStore so
  the value persists across sessions; App.jsx reads it on mount
- Left column is now much leaner: face + interaction buttons + micro
  expression grid + auto-rotate toggle only

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot 4870e1a6f4 jbot: hide left-col scrollbar + add home button
- Left column: scrollbar-width:none + ::-webkit-scrollbar:none to
  hide the scrollbar chrome while keeping overflow-y:auto for tall
  viewports; tightened padding (14→10px top, 20→14px bottom) and
  panel/section gaps (14→10px, 12→8px, h3 margin 7→5px) to help
  content fit without scrolling on typical laptop heights
- Header: added 🏠 home anchor linking to '/' (Links homepage) next
  to the JBOT logo; styled as a small icon button

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot 7f55aec007 jbot: fix no-scroll viewport layout
- index.css targeted #root but app mounts to #jbot-root — viewport
  height was never constrained, causing full page scroll
- Fix: rename #root → #jbot-root, add html/body {height:100%;overflow:hidden}
- .app now uses height:100% relative to #jbot-root instead of
  duplicate 100dvh, so the layout is a true single-screen SPA

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot 1efcebfd9b jbot: fix voice button visibility - chat-panel height overflow
.chat-panel was using height:100% which consumed all right-col space,
pushing VoiceButton out of view (clipped by overflow:hidden).

Fix: switch .chat-panel to flex:1 + min-height:0 so it participates
in flex layout and leaves room for the VoiceButton below it.

Also moved VoiceButton component outside App() to avoid React
component-identity churn on every render.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot cdb7efd61d jbot: add WeChat-style hold-to-talk voice button
- Added VoiceButton component (inline in App.jsx) with full
  mousedown/mouseup/mouseleave + touchstart/touchend/touchcancel support
- Shows context-aware label: 🎙 按住说话 /  准备中 / 🔴 松开发送 / ⚙ 思考中 / 🔊 播放中
- Pulsing red animation while recording, amber pulse while acquiring mic
- Disabled (gray) while AI is thinking or speaking
- Desktop: pill button docked at bottom of chat panel (max-width 380px)
- Mobile: fixed to bottom of viewport with safe-area-inset support,
  height 60px for easy thumb tap; right-col padded so messages aren't hidden
- Updated hint text and listening status to be input-agnostic

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot fd1d9db753 fix(jbot): ignore normal WebSocket close frame in on_error
Volcengine ASR server sends a WS close frame (opcode=8, code 1000,
'finish last sequence') after receiving the final audio chunk.
websocket-client fires on_error with this frame data instead of
(or in addition to) on_close — treat opcode=8 and 'already closed'
as normal termination, not an error.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot 2d6d4b09fc fix(jbot): replace WebSocketApp.send_binary with ws.send(data, OPCODE_BINARY)
WebSocketApp callbacks receive the WebSocketApp object, not WebSocket.
WebSocketApp has no send_binary() method — must use:
  ws.send(data, ws_module.ABNF.OPCODE_BINARY)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot e9dea75423 fix(jbot): add type=module to script tag + add JBOT to nav menu
- jbot/index.html: <script type="module"> required for Vite ES module output
  (jbot.js uses import statements for the shared client chunk)
- base.html: add JBOT menu item with robot icon after Invest in dropdown

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot a47ea316c7 feat(jbot): UI-configurable API keys + fix Dockerfile
- Add JbotApiConfig Django model (DB singleton) for storing
  Volcengine/OpenRouter credentials set via Settings UI
- New endpoint GET/POST /api/jbot/api-config/ — tokens masked as ***
  on read; only non-placeholder values are updated on write
- views.py: _get_cfg() helper reads DB first, env vars as fallback
- Settings.jsx: new 🔑 API 配置 section with show/hide token fields
  for VOLC App ID, Access Token, OpenRouter API Key, LLM Model, ASR Resource
- api.js: apiGetApiConfig() / apiSaveApiConfig() client helpers
- Dockerfile: COPY jbot/ in both builder + production stages (was missing)
- entrypoint.sh: runs migrate --noinput before gunicorn (auto-creates table)
- k8s/manifest.yaml: remove jbot-credentials secretKeyRef (no Secret needed);
  keep LLM_MODEL/VOLC_TTS_VOICE/VOLC_ASR_RESOURCE as optional env defaults

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot a87b4643fd feat: port JBOT AI robot face into links as Django app
- Add jbot Django app (jbot/__init__.py, apps.py, urls.py, api_urls.py)
- Python backend (jbot/views.py): TTS 2.0, ASR 1.0 BigASR, LLM/memory via OpenRouter
- React SPA frontend at /jbot/ (static_src/jbot/): RobotFace, ChatPanel, expressions,
  voices, push-to-talk, history, memory, settings pages
- Vite entry jbot: static_src/jbot/main.jsx → dist/jbot.js + dist/jbot.css
- react-router-dom added; BrowserRouter basename=/jbot for SPA routing
- core/settings.py: added jbot to INSTALLED_APPS
- core/urls.py: /jbot/ + /api/jbot/ URL includes
- pyproject.toml: websocket-client>=1.9.0 for BigASR binary WS protocol
- Dockerfile: ffmpeg added to production apt-get for WebM→PCM audio conversion
- k8s/manifest.yaml: VOLC_APP_ID, VOLC_ACCESS_TOKEN, OPENROUTER_API_KEY env vars
  via jbot-credentials Secret; LLM_MODEL, VOLC_TTS_VOICE, VOLC_ASR_RESOURCE defaults

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junv a369bc7975 Update UI 2026-05-16 09:12:18 +10:00
junv ec9cbf115a Update redis ur;l 2026-05-16 08:49:35 +10:00
junv 839aadcf21 Update ban ip form 2026-05-11 22:57:25 +10:00
junv f587ab3053 UI tweaks 2026-05-11 21:53:43 +10:00
junv 1b04d500d1 Add price check 2026-05-11 21:28:59 +10:00
junv 81c56207d4 update 2026-05-11 20:25:25 +10:00
junv cb81fa8601 update 2026-05-03 14:53:39 +10:00
junv b52b821baf test change rate 2026-05-02 20:55:02 +10:00
junv 68dd32d2cd update performance chart logic! 2026-05-02 20:36:32 +10:00
junvandGitHub e5ea1e9bf4 Merge pull request #85 from wahyd4/feature/invest-portfolio
fix: remove duplicate serializer classes causing 500 on Transaction API
2026-04-28 14:17:12 +10:00
junv e82a9c285e Update invest UI 2026-04-25 11:58:42 +10:00
junv 748a39975f Fix snapshot! 2026-04-25 11:58:42 +10:00
junv 971fa992dc fix: pull --rebase before manifest push to avoid rejection 2026-04-25 11:00:34 +10:00
junv 9646cd7c7a fix: derive portfolio header change from sum of per-stock changes for consistency 2026-04-25 10:58:24 +10:00
junv f3e9e13297 fix: compare live portfolio value against last week snapshot (not this week) 2026-04-25 10:46:55 +10:00
junv 18c5a554d2 feat: show dollar + % week change per stock in portfolio table 2026-04-25 10:45:30 +10:00
junv 3acaa328cb feat: add per-stock weekly price change column to portfolio table 2026-04-25 10:42:21 +10:00
junv d4183f30e6 fix: show live values and correct change% on invest dashboard cards 2026-04-25 10:40:46 +10:00
junvandGitHub c0093ac6ab Merge pull request #83 from wahyd4/feature/invest-portfolio
fix: API bugs in invest portfolio
2026-04-18 23:42:21 +10:00
junv e9f7c90420 add transaction history section to invest dashboard 2026-04-18 23:29:56 +10:00
junv 8209ac34c7 fix weekly overview: use asof snapshot lookup and 5-day gap for last_week 2026-04-18 23:03:33 +10:00
junv 56bb5df57c cache benchmark prices in DB, refresh weekly 2026-04-18 22:58:25 +10:00
junv 221cdcdbee add yfinance dependency 2026-04-18 22:56:16 +10:00
junv 3c2d97b31d update ui 2026-04-18 22:43:58 +10:00
junv b345534277 update dockerfile 2026-04-18 22:43:58 +10:00
junv 028b2eda76 Update ui 2026-04-18 22:43:58 +10:00
junv f6f9bd167d fix build 2026-04-18 22:30:41 +10:00
junvandGitHub 55c9285967 Merge pull request #82 from wahyd4/feature/invest-portfolio
feat(invest): Add investment portfolio management feature
2026-04-18 22:26:46 +10:00
junv 93bc6bd1e5 Update 2026-04-18 22:25:55 +10:00
junv a5e9fe9cbb Update UI 2026-04-18 22:24:17 +10:00
junv e202304043 ui update 2026-04-18 22:16:45 +10:00
junv 830171b38a UI update 2026-04-18 22:04:01 +10:00
junv d933a99b8d Update logic 2026-04-18 22:01:34 +10:00
junv f44d76e9ad Rebase 2026-04-18 14:19:51 +10:00
junvandCopilot 11fa2ad673 routermon: replace ISP column with Target Service badge in WAN table
- Remove ISP column (always empty with local MMDB — no ISP data)
- Add Target Service column with color-coded badges (SSH/RDP/VNC/SMB=red,
  HTTP/HTTPS=blue, MySQL/PostgreSQL/Redis/MongoDB=purple, SMTP/POP3/IMAP=green,
  DNS=yellow, FTP=orange)
- Restore inline service label in Dst Port cell (e.g. '22 SSH')
- Expand port mapping: 21 ports covered (FTP, Telnet, SMTP, DNS, POP3,
  IMAP, SMB, MSSQL, MySQL, RDP, PostgreSQL, VNC, Redis, HTTP-alt,
  HTTPS-alt, Elasticsearch, MongoDB)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-16 17:06:55 +10:00
junvandCopilot 3955e95b9e geo: replace ip-api.com with local DB-IP City Lite MMDB
- Rewrite nginxmon/geo.py to use maxminddb + DB-IP City Lite MMDB
  (https://cdn.jsdelivr.net/npm/dbip-city-lite/dbip-city-lite.mmdb.gz)
  instead of ip-api.com HTTP API — eliminates all network timeouts and
  rate-limit issues; lookups are now sub-millisecond in-process
- Keep _lookup_batch() signature unchanged so routermon receiver
  requires no changes
- MMDB downloaded on first use (lazy) and refreshed monthly via
  APScheduler; reader cached module-level (thread-safe for reads)
- Add management command: manage.py download_geo_db
- Update k8s init container to download MMDB on first deploy if absent
- Add maxminddb==3.1.1 dependency

CC BY 4.0 — DB-IP (https://db-ip.com)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-16 16:44:02 +10:00
junvandCopilot 60dd5aeffb routermon: break chunk loop early when circuit breaker fires
Stop sending further API requests mid-batch once the backoff is
triggered, saving remaining missing IPs as empty placeholders
immediately instead of waiting for them to timeout one-by-one.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-16 16:35:00 +10:00
junvandCopilot dde1c6640c routermon: add circuit breaker for ip-api.com geo lookups
After _GEO_MISS_THRESHOLD (2) consecutive large all-empty API responses,
assume ip-api.com is unreachable and enter a 5-minute backoff.
During backoff all missing IPs are immediately saved as empty IPGeoCache
placeholders so they are not re-queued. Resets automatically when the
backoff expires and any result is returned.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-16 16:24:33 +10:00
junvandCopilot cfca2c2ed7 routermon: add WAN geo map + fix batch geo enrichment
- Add WanGeoStatsView (api/wan/geo/) returning country choropleth +
  bubble data for WAN source IPs using IPGeoCache lat/lon
- Add wanGeoMap (420px) in dashboard between ports chart and live table:
  red colour scale by country, rose bubbles sized by attempt count,
  top-5 country legend
- Rewrite _geo_worker_loop to drain up to 200 queue items per cycle,
  bulk DB cache check, batch API calls (100 IPs per request) instead
  of 1 request per IP — fixes ip-api.com timeout pile-up under scan
  volumes
- Add _geo_skip in-memory set: IPs that return no geo data get an
  empty IPGeoCache placeholder saved to DB so they are never retried;
  skip set also prevents re-queuing known-empty IPs during flush
- Remove _fetch_geo / _enrich_dns_geo / _enrich_wan_geo helpers
  (logic consolidated into the new batch worker)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-16 16:13:30 +10:00
junvandCopilot 430ca18b41 routermon: add WAN incoming traffic monitoring
- Add WanEvent model (src_ip, protocol, dst_port, src_port, geo fields)
  with migration 0003_wanevent

- Extend parser to handle kernel:/iptables WAN_IN: syslog lines
  - Generalise _RE_SYSLOG to accept 'kernel' process name (no pid)
  - Parse KEY=value tokens from iptables log (robust vs monolithic regex)
  - Reject private source IPs silently

- Receiver: bulk-create WanEvent rows in _flush(); replace per-flush
  geo threads with a single bounded geo-enrichment worker (_geo_queue,
  max 500) to safely handle high-volume port scans

- Tasks: batch-delete WanEvent rows (<=3 day retention cap); batch-delete
  DnsQuery rows to avoid long SQLite locks

- Views: WanLivePartialView (filterable HTMX table), WanChartDataView
  (timeline JSON); dashboard context adds wan_total_24h,
  top_attacked_ports_json, top_wan_sources

- Templates:
  - _live_wan.html: live event table with color-coded protocol,
    clickable IP/port filters, well-known port labels
  - dashboard.html: WAN section with 24h counter, timeline chart,
    top attacked ports bar chart, top source IPs table, live event stream
  - settings.html: Step 5 guide for /jffs/scripts/firewall-start with
    rate-limited iptables LOG rules (INPUT + FORWARD chains, 60/min limit)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-16 15:34:39 +10:00
junvandCopilot 611305b0ab fix(routermon): fix receiver startup, logging config, and JS template escaping
- Remove SO_REUSEADDR from UDP socket so only one gunicorn worker binds
  port 5514 (second worker intentionally gets EADDRINUSE and skips)
- Add print() fallback for bind confirmation/errors so startup is visible
  even when Django logging config is not fully set up
- Fix duplicate LOGGING dict in settings.py: second assignment was
  overriding the first and removing the root logger handler, silently
  swallowing all routermon/core startup logs
- Add sys.stderr fallback in core/apps.py ready() for startup exceptions
- Fix JS SyntaxError: {{ top_domains_json }} needs |safe filter since
  Django auto-escapes quotes to &quot; inside script tags

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-16 14:58:39 +10:00
junv 3d481385b0 Update 2026-04-16 14:26:28 +10:00
junv 1fb54880d6 fix build 2026-04-16 14:18:46 +10:00
junv 7e0db3cdfb fix ci 2026-04-16 14:15:15 +10:00
junv 3409fc8045 fix build 2026-04-16 14:12:21 +10:00
junv 5b1df2ee5e Add router monitor 2026-04-16 14:01:43 +10:00
junv 864166ecbd ci: fix Python setup — use setup-uv python-version instead of separate install step 2026-04-14 14:54:48 +10:00
junvandGitHub af73cdf267 Merge pull request #77 from wahyd4/dependabot/uv/pillow-12.2.0
Bump pillow from 12.1.1 to 12.2.0
2026-04-14 14:44:04 +10:00
junvandGitHub 4cd667b067 Merge pull request #78 from wahyd4/dependabot/uv/pytest-9.0.3
Bump pytest from 8.3.4 to 9.0.3
2026-04-14 14:43:52 +10:00
junvandGitHub d85402e98a Merge pull request #57 from wahyd4/dependabot/npm_and_yarn/minimatch-9.0.9
Bump minimatch from 9.0.5 to 9.0.9
2026-04-14 14:43:38 +10:00
junv 04aad674e4 Test 2026-04-14 14:42:54 +10:00
junvandGitHub a8351dab76 Merge pull request #80 from wahyd4/remove-knowledge-graph
K8s template
2026-04-14 14:35:22 +10:00
junv 6a82b59863 ci: skip workflow when only k8s/ files change to prevent commit loop 2026-04-14 14:33:39 +10:00
junv a04aa1b828 chore: add manifest template and update deploy step to commit rendered manifest 2026-04-14 14:32:30 +10:00
junvandGitHub ba7daaa879 Merge pull request #79 from wahyd4/remove-knowledge-graph
Remove knowledge graph feature
2026-04-14 12:05:46 +10:00
junv 049d417cc8 Remove knowledge graph feature
The knowledge graph feature (links graph) was not functioning correctly
and was slowing down the application. This commit removes it entirely:

- Delete knowledge_graph_urls.py, knowledge_graph_views.py, llm_client.py
- Delete knowledge_graph.html template
- Remove KnowledgeGraphSnapshot model and all llm_*/kg_* fields from
  SiteSettings (migration 0048)
- Remove build_knowledge_graph() and schedule_kg_build() from tasks.py
- Remove KG settings save logic and bulk delete action from views.py
- Remove knowledge_graph_urls include from links/urls.py
- Remove schedule_kg_build scheduler job from core/apps.py
- Remove KG snapshot job registry from links/apps.py
- Remove Knowledge Graph nav item from base.html
- Remove KG settings cards and llmSettingsHelper JS from settings.html
2026-04-14 12:04:44 +10:00
junv bf11b675e8 Update ui 2026-04-01 21:52:29 +11:00
junv 3e6d348554 Update ui 2026-04-01 21:52:26 +11:00
junv e89825a114 Add auto ban feature! 2026-04-01 21:33:07 +11:00
junv 0d489e658e Update mini app 2026-04-01 21:06:22 +11:00
junv 70fbb78a54 ui update 2026-04-01 20:27:01 +11:00
junv 57516a3c9c minor fix! 2026-04-01 20:20:25 +11:00
junv 5fee444db9 Update 2026-04-01 20:02:57 +11:00
junv 1d1fb2db55 Exclude ips 2026-04-01 19:56:46 +11:00