docs: Add RPCD and LuCI frontend guidelines to CLAUDE.md
Document lessons learned from WireGuard dashboard development: - jshn argument size limits and manual JSON workaround - UCI underscore-prefixed private data storage - LuCI RPC expect field unwrapping behavior - LuCI JS module caching and cache-busting strategies - Quick deploy workflow for JS/RPCD changes - Common pitfalls (params ordering, sessionStorage, interface naming) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
2ed00d1967
commit
bb0f24d93a
61
CLAUDE.md
61
CLAUDE.md
@ -108,3 +108,64 @@ ssh root@192.168.255.1 '/etc/init.d/rpcd restart'
|
||||
```
|
||||
|
||||
- If unsure, check `OPENWRT_ONLY_PACKAGES` in `secubox-tools/local-build.sh`
|
||||
|
||||
## RPCD Backend Scripting (Shell-based RPC handlers)
|
||||
|
||||
### jshn Argument Size Limits
|
||||
- **`json_add_string` cannot handle large values** (e.g., base64-encoded images/SVGs)
|
||||
- jshn passes values as shell arguments, which hit BusyBox's argument size limit ("Argument list too long")
|
||||
- **Workaround**: Build JSON output manually via file I/O instead of jshn:
|
||||
```sh
|
||||
local tmpfile="/tmp/wg_output_$$.json"
|
||||
printf '{"field":"' > "$tmpfile"
|
||||
# Stream large data via pipe/redirect (never as argument)
|
||||
some_command | base64 -w 0 >> "$tmpfile"
|
||||
printf '"}\n' >> "$tmpfile"
|
||||
cat "$tmpfile"
|
||||
rm -f "$tmpfile"
|
||||
```
|
||||
- This applies to any RPCD method that returns large blobs (QR codes, certificates, etc.)
|
||||
|
||||
### UCI Private Data Storage
|
||||
- Use underscore-prefixed option names for internal/hidden data: `uci set network.section._private_field="value"`
|
||||
- These are not shown in standard LuCI forms but are accessible via `uci -q get`
|
||||
- Useful for storing client private keys, internal state, etc.
|
||||
|
||||
## LuCI JavaScript Frontend
|
||||
|
||||
### RPC `expect` Field Behavior
|
||||
- **`rpc.declare({ expect: { field: '' } })` unwraps the response** — it returns ONLY the value of `field`, not the full object
|
||||
- If the backend returns `{"config": "...", "error": "..."}` and expect is `{ config: '' }`, the result is just the config string — `result.error` is undefined
|
||||
- **Use `expect: { }` (empty object) when you need the full response** including error fields
|
||||
- Use `expect: { field: default }` only when you always want just that one field and don't need error handling
|
||||
|
||||
### Module Caching
|
||||
- **LuCI's JS module loader caches parsed modules in memory** — `Ctrl+Shift+R` does NOT clear this
|
||||
- Clearing browser cache, `rm /tmp/luci-indexcache*`, and `rm /tmp/luci-modulecache/*` may not be enough
|
||||
- **Reliable fix**: Force full page navigation with cache-busting query param:
|
||||
```js
|
||||
window.location.href = window.location.pathname + '?' + Date.now();
|
||||
```
|
||||
- For development, set `uci set uhttpd.main.no_cache=1 && uci commit uhttpd && /etc/init.d/uhttpd restart`
|
||||
|
||||
### Quick Deploy for LuCI JS/RPCD Changes
|
||||
- LuCI JS views and shared resources can be deployed directly to the router without rebuilding:
|
||||
```bash
|
||||
# Deploy JS views
|
||||
scp htdocs/luci-static/resources/view/<app>/*.js root@192.168.255.1:/www/luci-static/resources/view/<app>/
|
||||
|
||||
# Deploy shared JS libraries
|
||||
scp htdocs/luci-static/resources/<app>/*.js root@192.168.255.1:/www/luci-static/resources/<app>/
|
||||
|
||||
# Deploy RPCD handler and restart
|
||||
scp root/usr/libexec/rpcd/<handler> root@192.168.255.1:/usr/libexec/rpcd/
|
||||
ssh root@192.168.255.1 '/etc/init.d/rpcd restart'
|
||||
|
||||
# Clear LuCI caches on router
|
||||
ssh root@192.168.255.1 'rm -f /tmp/luci-indexcache* /tmp/luci-modulecache/*'
|
||||
```
|
||||
|
||||
### Common Pitfalls
|
||||
- **RPC params order matters**: The `params` array in `rpc.declare()` must match the positional arguments in `addPeer(arg1, arg2, ...)` calls — adding a new param means updating ALL callers
|
||||
- **sessionStorage is volatile**: Data stored in `sessionStorage` is lost on tab close/refresh — don't rely on it for persistent data; use UCI backend storage instead
|
||||
- **Interface name conflicts**: When creating WireGuard interfaces, always check for existing names (wg0, wg1, etc.) and auto-increment to the next available name
|
||||
|
||||
Loading…
Reference in New Issue
Block a user