Install ReUI (MCP, skill, @reui registry, Tailwind v4, shadcn base-nova) and move the shared UI onto it: Button, Tag, Check, Input/Textarea, Segmented, Alert, Panel, Dialog, Tooltip, Menu and Select (Popover), Toast (sonner), the Table frame (DataGrid), the Dashboard KPI strip, the shnt Ops palette, Geist and the type scale. Design only: behaviour, texts and roles are unchanged. Tests that asserted the old DOM were updated; the tests now format numbers in English wherever they run. docs/reui-migration-plan.md records the plan and its decisions. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
136 lines
6.2 KiB
Bash
136 lines
6.2 KiB
Bash
#!/usr/bin/env bash
|
|
# Side-by-side comparison by ACTION, not by bytes of the page.
|
|
#
|
|
# The project's governing constraint is "design only, zero functionality", and
|
|
# that enables the check that actually counts: run the same action on both
|
|
# instances and verify the RESULTING SERVER STATE is the same. If the body the
|
|
# new console sends differs by one parameter, the record left behind differs and
|
|
# it shows up here.
|
|
#
|
|
# The bodies are the ones upstream builds, copied from its JS. The new console
|
|
# sends the same ones: that is what is being verified.
|
|
#
|
|
# Usage: ./check-parity-actions.sh
|
|
set -uo pipefail
|
|
|
|
DEV=http://127.0.0.1:5380
|
|
REF=http://127.0.0.1:5381
|
|
USERNAME=admin
|
|
PASSWORD=technitium-ui-dev
|
|
ZONE=paridad.test
|
|
|
|
failures=0
|
|
|
|
token() {
|
|
curl -s "$1/api/user/login?user=$USERNAME&pass=$PASSWORD&includeInfo=false" |
|
|
python3 -c 'import sys,json;print(json.load(sys.stdin)["token"])'
|
|
}
|
|
|
|
TD=$(token "$DEV")
|
|
TR=$(token "$REF")
|
|
|
|
# Leaves the zone identical on both before each comparison block.
|
|
reset_zone() {
|
|
for pair in "$DEV $TD" "$REF $TR"; do
|
|
set -- $pair
|
|
curl -s "$1/api/zones/delete?token=$2&zone=$ZONE" >/dev/null
|
|
curl -s "$1/api/zones/create?token=$2&zone=$ZONE&type=Primary" >/dev/null
|
|
done
|
|
}
|
|
|
|
# What is compared: the zone's records, minus whatever changes with the
|
|
# environment.
|
|
#
|
|
# Three things are normalised, all three for the same reason: they are not parity.
|
|
# · `lastModified` and the SOA serial change with the clock.
|
|
# · The SERVER NAME is embedded in the NS and the SOA of every zone, and the
|
|
# two containers deliberately have a different `DNS_SERVER_DOMAIN`
|
|
# (dev.technitium-ui.test / ref.technitium-ui.test). Without substituting it,
|
|
# all fourteen comparisons come out different over the hostname and not over
|
|
# the action.
|
|
fingerprint() {
|
|
curl -s "$1/api/zones/records/get?token=$2&domain=$ZONE&zone=$ZONE&listZone=true" |
|
|
python3 -c '
|
|
import sys, json, re
|
|
d = json.load(sys.stdin)["response"]
|
|
out = []
|
|
for r in sorted(d["records"], key=lambda x: (x["type"], x["name"])):
|
|
r.pop("lastModified", None)
|
|
if r["type"] == "SOA":
|
|
r["rData"].pop("serial", None)
|
|
out.append(r)
|
|
text = json.dumps(out, sort_keys=True)
|
|
print(re.sub(r"(dev|ref)\.technitium-ui\.test", "THIS-SERVER", text))'
|
|
}
|
|
|
|
# compare <name> <body-or-query> [method]
|
|
compare() {
|
|
local name=$1 path=$2 body=${3:-} method=${4:-GET}
|
|
|
|
for pair in "$DEV $TD" "$REF $TR"; do
|
|
set -- $pair
|
|
if [ "$method" = POST ]; then
|
|
curl -s -X POST "$1/api/$path?token=$2&node=" --data "$body" >/dev/null
|
|
else
|
|
curl -s "$1/api/$path?token=$2&$body" >/dev/null
|
|
fi
|
|
done
|
|
|
|
local d r
|
|
d=$(fingerprint "$DEV" "$TD")
|
|
r=$(fingerprint "$REF" "$TR")
|
|
|
|
if [ "$d" = "$r" ]; then
|
|
echo " OK $name"
|
|
else
|
|
echo " DIFFERENT $name"
|
|
diff <(echo "$r" | python3 -m json.tool) <(echo "$d" | python3 -m json.tool) | head -20
|
|
failures=$((failures + 1))
|
|
fi
|
|
}
|
|
|
|
echo "== record creates =="
|
|
reset_zone
|
|
compare "A with PTR" "zones/records/add" \
|
|
"zone=$ZONE&domain=www.$ZONE&type=A&ttl=3600&overwrite=false&comments=&expiryTtl=&ipAddress=10.0.0.1&ptr=false&createPtrZone=false&updateSvcbHints=false" POST
|
|
compare "MX" "zones/records/add" \
|
|
"zone=$ZONE&domain=$ZONE&type=MX&ttl=3600&overwrite=false&comments=&expiryTtl=&preference=10&exchange=mail.$ZONE" POST
|
|
compare "split TXT" "zones/records/add" \
|
|
"zone=$ZONE&domain=$ZONE&type=TXT&ttl=3600&overwrite=false&comments=note&expiryTtl=&text=v=spf1 -all&splitText=true" POST
|
|
compare "SRV" "zones/records/add" \
|
|
"zone=$ZONE&domain=_s._tcp.$ZONE&type=SRV&ttl=3600&overwrite=false&comments=&expiryTtl=&priority=1&weight=2&port=443&target=www.$ZONE" POST
|
|
compare "CAA with default values" "zones/records/add" \
|
|
"zone=$ZONE&domain=$ZONE&type=CAA&ttl=3600&overwrite=false&comments=&expiryTtl=&flags=0&tag=issue&value=letsencrypt.org" POST
|
|
compare "SVCB with params and automatic hint" "zones/records/add" \
|
|
"zone=$ZONE&domain=svc.$ZONE&type=SVCB&ttl=3600&overwrite=false&comments=&expiryTtl=&svcPriority=1&svcTargetName=www.$ZONE&svcParams=alpn|h2&autoIpv4Hint=true&autoIpv6Hint=false" POST
|
|
compare "CNAME" "zones/records/add" \
|
|
"zone=$ZONE&domain=alias.$ZONE&type=CNAME&ttl=3600&overwrite=false&comments=&expiryTtl=&cname=www.$ZONE" POST
|
|
|
|
echo "== edits =="
|
|
compare "edit the A: old and new" "zones/records/update" \
|
|
"zone=$ZONE&type=A&domain=www.$ZONE&newDomain=www.$ZONE&ttl=1800&disable=false&comments=&expiryTtl=&ipAddress=10.0.0.1&newIpAddress=10.0.0.2&ptr=false&createPtrZone=false&updateSvcbHints=false" POST
|
|
compare "disable the MX by resending it whole" "zones/records/update" \
|
|
"zone=$ZONE&type=MX&domain=$ZONE&ttl=3600&disable=true&comments=&expiryTtl=&preference=10&exchange=mail.$ZONE" POST
|
|
compare "edit the SOA" "zones/records/update" \
|
|
"zone=$ZONE&type=SOA&domain=$ZONE&newDomain=$ZONE&ttl=900&disable=false&comments=&expiryTtl=&primaryNameServer=ns1.$ZONE&responsiblePerson=hostadmin@$ZONE&serial=42&refresh=900&retry=300&expire=604800&minimum=900&useSerialDateScheme=false" POST
|
|
|
|
echo "== deletes =="
|
|
compare "delete the CNAME (no parameters: falls to default)" "zones/records/delete" \
|
|
"zone=$ZONE&domain=alias.$ZONE&type=CNAME" POST
|
|
compare "delete the SVCB by its flattened svcParams" "zones/records/delete" \
|
|
"zone=$ZONE&domain=svc.$ZONE&type=SVCB&svcPriority=1&svcTargetName=www.$ZONE&svcParams=alpn|h2" POST
|
|
compare "delete the SRV by its four fields" "zones/records/delete" \
|
|
"zone=$ZONE&domain=_s._tcp.$ZONE&type=SRV&priority=1&weight=2&port=443&target=www.$ZONE" POST
|
|
|
|
echo "== zone options =="
|
|
compare "options/set with the empty lists as \"false\"" "zones/options/set" \
|
|
"zone=$ZONE&catalog=&overrideCatalogQueryAccess=false&overrideCatalogZoneTransfer=false&overrideCatalogNotify=false&primaryNameServerAddresses=&primaryZoneTransferProtocol=Tcp&primaryZoneTransferTsigKeyName=&validateZone=false&queryAccess=Allow&queryAccessNetworkACL=&zoneTransfer=AllowOnlyZoneNameServers&zoneTransferNetworkACL=false&zoneTransferTsigKeyNames=false¬ify=ZoneNameServers¬ifyNameServers=false¬ifySecondaryCatalogsNameServers=false&update=Deny&updateNetworkACL=false&updateSecurityPolicies=false"
|
|
|
|
echo
|
|
if [ "$failures" -eq 0 ]; then
|
|
echo "PARITY: every action leaves the same state"
|
|
exit 0
|
|
fi
|
|
echo "PARITY: $failures actions diverge"
|
|
exit 2
|