This commit is contained in:
seb
2026-10-29 17:32:39 +01:00
parent 867d550814
commit f555a1c9dd
6 changed files with 99 additions and 47 deletions

File diff suppressed because one or more lines are too long

View File

@@ -11,8 +11,8 @@
<polygon fill="#1e2430" stroke="none" points="-36,36 -36,-2323.9 6416.78,-2323.9 6416.78,36 -36,36"/>
<g id="clust1" class="cluster">
<title>cluster_session_realtime</title>
<path fill="#202a3a" stroke="#3f4c5f" d="M1200.78,-1631C1200.78,-1631 2347.78,-1631 2347.78,-1631 2353.78,-1631 2359.78,-1637 2359.78,-1643 2359.78,-1643 2359.78,-1940 2359.78,-1940 2359.78,-1946 2353.78,-1952 2347.78,-1952 2347.78,-1952 1200.78,-1952 1200.78,-1952 1194.78,-1952 1188.78,-1946 1188.78,-1940 1188.78,-1940 1188.78,-1643 1188.78,-1643 1188.78,-1637 1194.78,-1631 1200.78,-1631"/>
<text xml:space="preserve" text-anchor="middle" x="1283.06" y="-1934.5" font-family="Helvetica,sans-Serif" font-size="15.00" fill="#e2e8f0">Session &amp; Real&#45;Time Hub</text>
<path fill="#202a3a" stroke="#3f4c5f" d="M1200.78,-1611C1200.78,-1611 2347.78,-1611 2347.78,-1611 2353.78,-1611 2359.78,-1617 2359.78,-1623 2359.78,-1623 2359.78,-1960 2359.78,-1960 2359.78,-1966 2353.78,-1972 2347.78,-1972 2347.78,-1972 1200.78,-1972 1200.78,-1972 1194.78,-1972 1188.78,-1966 1188.78,-1960 1188.78,-1960 1188.78,-1623 1188.78,-1623 1188.78,-1617 1194.78,-1611 1200.78,-1611"/>
<text xml:space="preserve" text-anchor="middle" x="1283.06" y="-1954.5" font-family="Helvetica,sans-Serif" font-size="15.00" fill="#e2e8f0">Session &amp; Real&#45;Time Hub</text>
</g>
<g id="clust2" class="cluster">
<title>cluster_company_basedata</title>
@@ -57,64 +57,76 @@
<!-- authAutomat -->
<g id="node1" class="node">
<title>authAutomat</title>
<polygon fill="#33415c" stroke="none" points="1210.95,-1849 1210.95,-1880 1530.61,-1880 1530.61,-1849 1210.95,-1849"/>
<polygon fill="none" stroke="#3f4c5f" points="1210.95,-1849 1210.95,-1880 1530.61,-1880 1530.61,-1849 1210.95,-1849"/>
<text xml:space="preserve" text-anchor="start" x="1333.78" y="-1865.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00" fill="#e2e8f0">authAutomat</text>
<text xml:space="preserve" text-anchor="start" x="1282.24" y="-1855.9" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">Always&#45;On Session &amp; Multi&#45;Tenant Gateway</text>
<polygon fill="#2a3242" stroke="none" points="1210.95,-1827 1210.95,-1849 1530.61,-1849 1530.61,-1827 1210.95,-1827"/>
<polygon fill="#33415c" stroke="none" points="1210.95,-1871 1210.95,-1902 1530.61,-1902 1530.61,-1871 1210.95,-1871"/>
<polygon fill="none" stroke="#3f4c5f" points="1210.95,-1871 1210.95,-1902 1530.61,-1902 1530.61,-1871 1210.95,-1871"/>
<text xml:space="preserve" text-anchor="start" x="1333.78" y="-1887.2" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="12.00" fill="#e2e8f0">authAutomat</text>
<text xml:space="preserve" text-anchor="start" x="1282.24" y="-1877.9" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#8b95a7">Always&#45;On Session &amp; Multi&#45;Tenant Gateway</text>
<polygon fill="#2a3242" stroke="none" points="1210.95,-1849 1210.95,-1871 1530.61,-1871 1530.61,-1849 1210.95,-1849"/>
<polygon fill="none" stroke="#3f4c5f" points="1210.95,-1849 1210.95,-1871 1530.61,-1871 1530.61,-1849 1210.95,-1849"/>
<polygon fill="#2b1a42" stroke="none" points="1211.95,-1850 1211.95,-1870 1286.71,-1870 1286.71,-1850 1211.95,-1850"/>
<text xml:space="preserve" text-anchor="start" x="1236.22" y="-1858.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="8.00" fill="#c084fc">STATE</text>
<text xml:space="preserve" text-anchor="start" x="1290.71" y="-1858" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="#d8dee9">hasAuth: boolean</text>
<text xml:space="preserve" text-anchor="start" x="1436.66" y="-1856.8" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#8b95a7">Authenticated session bit</text>
<polygon fill="#272e3d" stroke="none" points="1210.95,-1827 1210.95,-1849 1530.61,-1849 1530.61,-1827 1210.95,-1827"/>
<polygon fill="none" stroke="#3f4c5f" points="1210.95,-1827 1210.95,-1849 1530.61,-1849 1530.61,-1827 1210.95,-1827"/>
<polygon fill="#2b1a42" stroke="none" points="1211.95,-1828 1211.95,-1848 1286.71,-1848 1286.71,-1828 1211.95,-1828"/>
<text xml:space="preserve" text-anchor="start" x="1236.22" y="-1836.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="8.00" fill="#c084fc">STATE</text>
<text xml:space="preserve" text-anchor="start" x="1290.71" y="-1836" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="#d8dee9">hasAuth: boolean</text>
<text xml:space="preserve" text-anchor="start" x="1436.66" y="-1834.8" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#8b95a7">Authenticated session bit</text>
<polygon fill="#272e3d" stroke="none" points="1210.95,-1805 1210.95,-1827 1530.61,-1827 1530.61,-1805 1210.95,-1805"/>
<polygon fill="#2b1a42" stroke="none" points="1211.95,-1828 1211.95,-1848 1270.36,-1848 1270.36,-1828 1211.95,-1828"/>
<text xml:space="preserve" text-anchor="start" x="1228.05" y="-1836.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="8.00" fill="#c084fc">STATE</text>
<text xml:space="preserve" text-anchor="start" x="1274.36" y="-1836" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="#d8dee9">hasCompany: boolean</text>
<text xml:space="preserve" text-anchor="start" x="1410.43" y="-1834.8" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#8b95a7">hasAuth &amp;&amp; selectedCompanyId</text>
<polygon fill="#2a3242" stroke="none" points="1210.95,-1805 1210.95,-1827 1530.61,-1827 1530.61,-1805 1210.95,-1805"/>
<polygon fill="none" stroke="#3f4c5f" points="1210.95,-1805 1210.95,-1827 1530.61,-1827 1530.61,-1805 1210.95,-1805"/>
<polygon fill="#2b1a42" stroke="none" points="1211.95,-1806 1211.95,-1826 1270.36,-1826 1270.36,-1806 1211.95,-1806"/>
<text xml:space="preserve" text-anchor="start" x="1228.05" y="-1814.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="8.00" fill="#c084fc">STATE</text>
<text xml:space="preserve" text-anchor="start" x="1274.36" y="-1814" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="#d8dee9">hasCompany: boolean</text>
<text xml:space="preserve" text-anchor="start" x="1410.43" y="-1812.8" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#8b95a7">hasAuth &amp;&amp; selectedCompanyId</text>
<polygon fill="#2a3242" stroke="none" points="1210.95,-1783 1210.95,-1805 1530.61,-1805 1530.61,-1783 1210.95,-1783"/>
<polygon fill="#2b1a42" stroke="none" points="1211.95,-1806 1211.95,-1826 1291.52,-1826 1291.52,-1806 1211.95,-1806"/>
<text xml:space="preserve" text-anchor="start" x="1238.63" y="-1814.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="8.00" fill="#c084fc">STATE</text>
<text xml:space="preserve" text-anchor="start" x="1295.52" y="-1814" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="#d8dee9">user: AuthUser</text>
<text xml:space="preserve" text-anchor="start" x="1438.9" y="-1812.8" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#8b95a7">Current user &amp; company</text>
<polygon fill="#272e3d" stroke="none" points="1210.95,-1783 1210.95,-1805 1530.61,-1805 1530.61,-1783 1210.95,-1783"/>
<polygon fill="none" stroke="#3f4c5f" points="1210.95,-1783 1210.95,-1805 1530.61,-1805 1530.61,-1783 1210.95,-1783"/>
<polygon fill="#2b1a42" stroke="none" points="1211.95,-1784 1211.95,-1804 1291.52,-1804 1291.52,-1784 1211.95,-1784"/>
<text xml:space="preserve" text-anchor="start" x="1238.63" y="-1792.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="8.00" fill="#c084fc">STATE</text>
<text xml:space="preserve" text-anchor="start" x="1295.52" y="-1792" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="#d8dee9">user: AuthUser</text>
<text xml:space="preserve" text-anchor="start" x="1438.9" y="-1790.8" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#8b95a7">Current user &amp; company</text>
<polygon fill="#272e3d" stroke="none" points="1210.95,-1761 1210.95,-1783 1530.61,-1783 1530.61,-1761 1210.95,-1761"/>
<polygon fill="#2b1a42" stroke="none" points="1211.95,-1784 1211.95,-1804 1264.51,-1804 1264.51,-1784 1211.95,-1784"/>
<text xml:space="preserve" text-anchor="start" x="1225.12" y="-1792.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="8.00" fill="#c084fc">STATE</text>
<text xml:space="preserve" text-anchor="start" x="1268.51" y="-1792" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="#d8dee9">schemaVersion: number|null</text>
<text xml:space="preserve" text-anchor="start" x="1422.89" y="-1790.8" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#8b95a7">Backend DB schema version</text>
<polygon fill="#2a3242" stroke="none" points="1210.95,-1761 1210.95,-1783 1530.61,-1783 1530.61,-1761 1210.95,-1761"/>
<polygon fill="none" stroke="#3f4c5f" points="1210.95,-1761 1210.95,-1783 1530.61,-1783 1530.61,-1761 1210.95,-1761"/>
<polygon fill="#2b1a42" stroke="none" points="1211.95,-1762 1211.95,-1782 1304.59,-1782 1304.59,-1762 1211.95,-1762"/>
<text xml:space="preserve" text-anchor="start" x="1245.16" y="-1770.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="8.00" fill="#c084fc">STATE</text>
<text xml:space="preserve" text-anchor="start" x="1308.59" y="-1770" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="#d8dee9">status</text>
<text xml:space="preserve" text-anchor="start" x="1435.33" y="-1768.8" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#8b95a7">&#39;anon&#39; | &#39;authed&#39; | &#39;loading&#39;</text>
<polygon fill="#2a3242" stroke="none" points="1210.95,-1739 1210.95,-1761 1530.61,-1761 1530.61,-1739 1210.95,-1739"/>
<polygon fill="#272e3d" stroke="none" points="1210.95,-1739 1210.95,-1761 1530.61,-1761 1530.61,-1739 1210.95,-1739"/>
<polygon fill="none" stroke="#3f4c5f" points="1210.95,-1739 1210.95,-1761 1530.61,-1761 1530.61,-1739 1210.95,-1739"/>
<polygon fill="#143826" stroke="none" points="1211.95,-1740 1211.95,-1760 1285.38,-1760 1285.38,-1740 1211.95,-1740"/>
<text xml:space="preserve" text-anchor="start" x="1233.33" y="-1748.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="8.00" fill="#4ade80">ACTION</text>
<text xml:space="preserve" text-anchor="start" x="1289.38" y="-1748" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="#d8dee9">bootstrapSession()</text>
<text xml:space="preserve" text-anchor="start" x="1439.35" y="-1746.8" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#8b95a7">Restore or fetch session</text>
<polygon fill="#272e3d" stroke="none" points="1210.95,-1717 1210.95,-1739 1530.61,-1739 1530.61,-1717 1210.95,-1717"/>
<polygon fill="#2a3242" stroke="none" points="1210.95,-1717 1210.95,-1739 1530.61,-1739 1530.61,-1717 1210.95,-1717"/>
<polygon fill="none" stroke="#3f4c5f" points="1210.95,-1717 1210.95,-1739 1530.61,-1739 1530.61,-1717 1210.95,-1717"/>
<polygon fill="#143826" stroke="none" points="1211.95,-1718 1211.95,-1738 1291.68,-1738 1291.68,-1718 1211.95,-1718"/>
<text xml:space="preserve" text-anchor="start" x="1236.48" y="-1726.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="8.00" fill="#4ade80">ACTION</text>
<text xml:space="preserve" text-anchor="start" x="1295.68" y="-1726" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="#d8dee9">switchCompany(id)</text>
<text xml:space="preserve" text-anchor="start" x="1459.36" y="-1724.8" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#8b95a7">Multi&#45;tenant switch</text>
<polygon fill="#2a3242" stroke="none" points="1210.95,-1695 1210.95,-1717 1530.61,-1717 1530.61,-1695 1210.95,-1695"/>
<polygon fill="#272e3d" stroke="none" points="1210.95,-1695 1210.95,-1717 1530.61,-1717 1530.61,-1695 1210.95,-1695"/>
<polygon fill="none" stroke="#3f4c5f" points="1210.95,-1695 1210.95,-1717 1530.61,-1717 1530.61,-1695 1210.95,-1695"/>
<polygon fill="#143826" stroke="none" points="1211.95,-1696 1211.95,-1716 1307.5,-1716 1307.5,-1696 1211.95,-1696"/>
<text xml:space="preserve" text-anchor="start" x="1244.39" y="-1704.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="8.00" fill="#4ade80">ACTION</text>
<text xml:space="preserve" text-anchor="start" x="1311.5" y="-1704" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="#d8dee9">logout()</text>
<text xml:space="preserve" text-anchor="start" x="1451.8" y="-1702.8" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#8b95a7">Clear session cookie</text>
<polygon fill="#272e3d" stroke="none" points="1210.95,-1673 1210.95,-1695 1530.61,-1695 1530.61,-1673 1210.95,-1673"/>
<polygon fill="#2a3242" stroke="none" points="1210.95,-1673 1210.95,-1695 1530.61,-1695 1530.61,-1673 1210.95,-1673"/>
<polygon fill="none" stroke="#3f4c5f" points="1210.95,-1673 1210.95,-1695 1530.61,-1695 1530.61,-1673 1210.95,-1673"/>
<polygon fill="#182b40" stroke="none" points="1211.95,-1674 1211.95,-1694 1259.95,-1694 1259.95,-1674 1211.95,-1674"/>
<text xml:space="preserve" text-anchor="start" x="1220.17" y="-1682.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="8.00" fill="#38bdf8">SELECT</text>
<text xml:space="preserve" text-anchor="start" x="1263.95" y="-1682" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="#d8dee9">selectHasAuth() / selectHasCompany()</text>
<text xml:space="preserve" text-anchor="start" x="1455.33" y="-1680.8" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#8b95a7">Auth &amp; tenant gates</text>
<polygon fill="#2a3242" stroke="none" points="1210.95,-1651 1210.95,-1673 1530.61,-1673 1530.61,-1651 1210.95,-1651"/>
<polygon fill="#272e3d" stroke="none" points="1210.95,-1651 1210.95,-1673 1530.61,-1673 1530.61,-1651 1210.95,-1651"/>
<polygon fill="none" stroke="#3f4c5f" points="1210.95,-1651 1210.95,-1673 1530.61,-1673 1530.61,-1651 1210.95,-1651"/>
<polygon fill="#3b2c12" stroke="none" points="1211.95,-1652 1211.95,-1672 1283.89,-1672 1283.89,-1652 1211.95,-1652"/>
<text xml:space="preserve" text-anchor="start" x="1241.25" y="-1660.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="8.00" fill="#fbbf24">API</text>
<text xml:space="preserve" text-anchor="start" x="1287.89" y="-1660" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="#d8dee9">GET /api/auth/session</text>
<text xml:space="preserve" text-anchor="start" x="1448.24" y="-1658.8" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#8b95a7">Cookie session check</text>
<polygon fill="#182b40" stroke="none" points="1211.95,-1652 1211.95,-1672 1276.7,-1672 1276.7,-1652 1211.95,-1652"/>
<text xml:space="preserve" text-anchor="start" x="1228.54" y="-1660.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="8.00" fill="#38bdf8">SELECT</text>
<text xml:space="preserve" text-anchor="start" x="1280.7" y="-1660" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="#d8dee9">selectSchemaVersion()</text>
<text xml:space="preserve" text-anchor="start" x="1432.24" y="-1658.8" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#8b95a7">Active DB schema version</text>
<polygon fill="#2a3242" stroke="none" points="1210.95,-1629 1210.95,-1651 1530.61,-1651 1530.61,-1629 1210.95,-1629"/>
<polygon fill="none" stroke="#3f4c5f" points="1210.95,-1629 1210.95,-1651 1530.61,-1651 1530.61,-1629 1210.95,-1629"/>
<polygon fill="#3b2c12" stroke="none" points="1211.95,-1630 1211.95,-1650 1283.89,-1650 1283.89,-1630 1211.95,-1630"/>
<text xml:space="preserve" text-anchor="start" x="1241.25" y="-1638.6" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="8.00" fill="#fbbf24">API</text>
<text xml:space="preserve" text-anchor="start" x="1287.89" y="-1638" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="10.00" fill="#d8dee9">GET /api/auth/session</text>
<text xml:space="preserve" text-anchor="start" x="1448.24" y="-1636.8" font-family="Helvetica,sans-Serif" font-size="8.00" fill="#8b95a7">Cookie session check</text>
</g>
<!-- sseAutomat -->
<g id="node2" class="node">
@@ -184,11 +196,11 @@
<g id="edge1" class="edge">
<title>authAutomat:e&#45;&gt;sseAutomat:n</title>
<g id="a_edge1"><a xlink:title="authAutomat → sseAutomat (hasAuth &#45;&gt; connect SSE)">
<path fill="none" stroke="#5aa7ff" stroke-width="1.4" d="M1531.61,-1838C1597.66,-1838 2098.6,-1938.58 2155.13,-1891.19"/>
<polygon fill="#5aa7ff" stroke="#5aa7ff" stroke-width="1.4" points="2158.36,-1890.24 2159.36,-1882.89 2154,-1888.02 2158.36,-1890.24"/>
<path fill="none" stroke="#5aa7ff" stroke-width="1.4" d="M1531.61,-1860C1667.44,-1860 2132.91,-2011.81 2159.16,-1892.16"/>
<polygon fill="#5aa7ff" stroke="#5aa7ff" stroke-width="1.4" points="2161.81,-1890.32 2160.1,-1883.11 2156.94,-1889.82 2161.81,-1890.32"/>
</a>
</g>
<text xml:space="preserve" text-anchor="middle" x="1815.23" y="-1886.08" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#5aa7ff"> &#160;hasAuth &#45;&gt; connect SSE &#160;</text>
<text xml:space="preserve" text-anchor="middle" x="1815.23" y="-1919.86" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#5aa7ff"> &#160;hasAuth &#45;&gt; connect SSE &#160;</text>
</g>
<!-- companyAutomat -->
<g id="node3" class="node">
@@ -301,7 +313,7 @@
<g id="edge2" class="edge">
<title>authAutomat:e&#45;&gt;companyAutomat:n</title>
<g id="a_edge2"><a xlink:title="authAutomat → companyAutomat (hasCompany &#45;&gt; loadAll())">
<path fill="none" stroke="#6bdf9a" stroke-width="1.4" d="M1531.61,-1816C1778.34,-1816 1814.29,-1953.13 2057.78,-1992.98 2197.7,-2015.88 2235.25,-2001.4 2376.78,-1992.98 2504.73,-1985.38 2662.43,-2084.44 2669.75,-1966.85"/>
<path fill="none" stroke="#6bdf9a" stroke-width="1.4" d="M1531.61,-1838C1775.4,-1838 1816.49,-1958.19 2057.78,-1992.98 2198.11,-2013.22 2235.25,-2001.4 2376.78,-1992.98 2504.73,-1985.38 2662.43,-2084.44 2669.75,-1966.85"/>
<polygon fill="#6bdf9a" stroke="#6bdf9a" stroke-width="1.4" points="2672.19,-1967.19 2669.95,-1960.12 2667.29,-1967.04 2672.19,-1967.19"/>
</a>
</g>
@@ -379,7 +391,7 @@
<g id="edge3" class="edge">
<title>authAutomat:w&#45;&gt;categoryAutomat:n</title>
<g id="a_edge3"><a xlink:title="authAutomat → categoryAutomat (sync tree on hasCompany)">
<path fill="none" stroke="#f5c15c" stroke-width="1.4" d="M1209.95,-1816C1048.33,-1816 1105.62,-1619.29 957.78,-1554 867.57,-1514.16 132.3,-1590.62 71.22,-1513.2 -17.14,-1401.19 -29.32,-1293.41 71.22,-1192.2 136.5,-1126.49 3340.5,-1235.91 3405.78,-1170.2 3409.16,-1166.79 3409.13,-1162.83 3405.78,-1159.4 3399.24,-1152.7 3078.14,-1151.63 3068.78,-1151.4 3003.45,-1149.82 786.43,-1158.3 647.93,-1097.5"/>
<path fill="none" stroke="#f5c15c" stroke-width="1.4" d="M1209.95,-1838C1041.15,-1838 1110.79,-1625.28 957.78,-1554 868.39,-1512.36 132.3,-1590.62 71.22,-1513.2 -17.14,-1401.19 -29.32,-1293.41 71.22,-1192.2 136.5,-1126.49 3340.5,-1235.91 3405.78,-1170.2 3409.16,-1166.79 3409.13,-1162.83 3405.78,-1159.4 3399.24,-1152.7 3078.14,-1151.63 3068.78,-1151.4 3003.45,-1149.82 786.43,-1158.3 647.93,-1097.5"/>
<polygon fill="#f5c15c" stroke="#f5c15c" stroke-width="1.4" points="649.57,-1095.68 642.73,-1092.82 646.29,-1099.32 649.57,-1095.68"/>
</a>
</g>
@@ -429,7 +441,7 @@
<g id="edge4" class="edge">
<title>authAutomat:e&#45;&gt;deliveryAutomat:n</title>
<g id="a_edge4"><a xlink:title="authAutomat → deliveryAutomat (sync deliveries on authed)">
<path fill="none" stroke="#ff7fc2" stroke-width="1.4" d="M1531.61,-1772C1587.17,-1772 1566.78,-1689.17 1530.61,-1647 1362.69,-1451.22 1214.44,-1579.47 957.78,-1554 924.81,-1550.73 384.77,-1555.86 361.78,-1532 250.74,-1416.78 350.13,-1284.6 480.78,-1192.2 514.55,-1168.32 3418.71,-1180.47 3458.78,-1170.2 3744.41,-1097 3758.45,-933.49 4042.78,-855.4 4135.32,-829.98 4839.35,-888.66 4905.78,-819.4 4974.8,-747.44 4704.08,-842.24 4687.09,-754.66"/>
<path fill="none" stroke="#ff7fc2" stroke-width="1.4" d="M1531.61,-1772C1596.94,-1772 1574.04,-1673.81 1530.61,-1625 1445.34,-1529.17 1085.64,-1564.26 957.78,-1554 924.75,-1551.35 384.77,-1555.86 361.78,-1532 250.74,-1416.78 350.13,-1284.6 480.78,-1192.2 514.55,-1168.32 3418.71,-1180.47 3458.78,-1170.2 3744.41,-1097 3758.45,-933.49 4042.78,-855.4 4135.32,-829.98 4839.35,-888.66 4905.78,-819.4 4974.8,-747.44 4704.08,-842.24 4687.09,-754.66"/>
<polygon fill="#ff7fc2" stroke="#ff7fc2" stroke-width="1.4" points="4689.54,-754.46 4686.46,-747.71 4684.66,-754.9 4689.54,-754.46"/>
</a>
</g>
@@ -541,7 +553,7 @@
<g id="edge6" class="edge">
<title>authAutomat:e&#45;&gt;orderAutomat:n</title>
<g id="a_edge6"><a xlink:title="authAutomat → orderAutomat (sync orders on authed)">
<path fill="none" stroke="#5cd6d6" stroke-width="1.4" d="M1531.61,-1772C1790.16,-1772 2107.92,-1668.89 957.78,-1554 925.91,-1550.82 403.93,-1555.07 381.72,-1532 378.39,-1528.54 378.33,-1524.6 381.72,-1521.2 393.82,-1509.06 2832.79,-1515.48 2849.78,-1513.2 2985.29,-1495.01 3146.2,-1567.68 3153.36,-1440.3"/>
<path fill="none" stroke="#5cd6d6" stroke-width="1.4" d="M1531.61,-1772C1873.92,-1772 2250.85,-1658.64 957.78,-1554 925.86,-1551.42 403.93,-1555.07 381.72,-1532 378.39,-1528.54 378.33,-1524.6 381.72,-1521.2 393.82,-1509.06 2832.79,-1515.48 2849.78,-1513.2 2985.29,-1495.01 3146.2,-1567.68 3153.36,-1440.3"/>
<polygon fill="#5cd6d6" stroke="#5cd6d6" stroke-width="1.4" points="3155.81,-1440.38 3153.55,-1433.32 3150.91,-1440.25 3155.81,-1440.38"/>
</a>
</g>
@@ -585,7 +597,7 @@
<g id="edge7" class="edge">
<title>authAutomat:e&#45;&gt;entityAutomat:n</title>
<g id="a_edge7"><a xlink:title="authAutomat → entityAutomat (sync entities on authed)">
<path fill="none" stroke="#ff9f5a" stroke-width="1.4" d="M1531.61,-1772C1779.83,-1772 2059.97,-1668.89 957.78,-1554 931.78,-1551.29 505.83,-1550.91 487.78,-1532 484.47,-1528.53 484.41,-1524.62 487.78,-1521.2 496.93,-1511.89 945.01,-1515.93 957.78,-1513.2 1312.73,-1437.36 1353.55,-1267.01 1708.72,-1192.2 1761.92,-1180.99 3627.55,-1208.86 3665.78,-1170.2 3815.97,-1018.31 3214.43,-1246.44 3197.36,-1045.33"/>
<path fill="none" stroke="#ff9f5a" stroke-width="1.4" d="M1531.61,-1772C1596.94,-1772 1574.04,-1673.81 1530.61,-1625 1445.34,-1529.17 1085.58,-1564.9 957.78,-1554 931.73,-1551.78 505.83,-1550.91 487.78,-1532 484.47,-1528.53 484.41,-1524.62 487.78,-1521.2 496.93,-1511.89 945.01,-1515.93 957.78,-1513.2 1312.73,-1437.36 1353.55,-1267.01 1708.72,-1192.2 1761.92,-1180.99 3627.55,-1208.86 3665.78,-1170.2 3815.97,-1018.31 3214.43,-1246.44 3197.36,-1045.33"/>
<polygon fill="#ff9f5a" stroke="#ff9f5a" stroke-width="1.4" points="3199.82,-1045.41 3197.08,-1038.52 3194.92,-1045.61 3199.82,-1045.41"/>
</a>
</g>
@@ -723,7 +735,7 @@
<g id="edge9" class="edge">
<title>authAutomat:e&#45;&gt;vfsAutomat:n</title>
<g id="a_edge9"><a xlink:title="authAutomat → vfsAutomat (load company vfs)">
<path fill="none" stroke="#ff6b6b" stroke-width="1.4" d="M1531.61,-1772C1587.17,-1772 1565.57,-1690.18 1530.61,-1647 1381.03,-1462.24 843.22,-1689.87 1010.74,-1521.2 1022.22,-1509.64 2167.97,-1517.14 2183.78,-1513.2 2267.24,-1492.38 2355.1,-1508.15 2361.17,-1429.21"/>
<path fill="none" stroke="#ff6b6b" stroke-width="1.4" d="M1531.61,-1772C1596.94,-1772 1572.71,-1674.96 1530.61,-1625 1378.77,-1444.84 844.71,-1688.37 1010.74,-1521.2 1022.22,-1509.64 2167.97,-1517.14 2183.78,-1513.2 2267.24,-1492.38 2355.1,-1508.15 2361.17,-1429.21"/>
<polygon fill="#ff6b6b" stroke="#ff6b6b" stroke-width="1.4" points="2363.62,-1429.4 2361.43,-1422.32 2358.72,-1429.22 2363.62,-1429.4"/>
</a>
</g>

Before

Width:  |  Height:  |  Size: 197 KiB

After

Width:  |  Height:  |  Size: 199 KiB

View File

@@ -126,11 +126,13 @@ const AUTOMAT_SPECS = [
{ type: 'STATE', name: 'hasAuth: boolean', desc: 'Authenticated session bit', port: 'p_auth_hasAuth' },
{ type: 'STATE', name: 'hasCompany: boolean', desc: 'hasAuth && selectedCompanyId', port: 'p_auth_hasCompany' },
{ type: 'STATE', name: 'user: AuthUser', desc: 'Current user & company', port: 'p_auth_user' },
{ type: 'STATE', name: 'schemaVersion: number|null', desc: 'Backend DB schema version', port: 'p_auth_schemaVersion' },
{ type: 'STATE', name: 'status', desc: '\'anon\' | \'authed\' | \'loading\'', port: 'p_auth_status' },
{ type: 'ACTION', name: 'bootstrapSession()', desc: 'Restore or fetch session' },
{ type: 'ACTION', name: 'switchCompany(id)', desc: 'Multi-tenant switch', port: 'p_auth_switchCompany' },
{ type: 'ACTION', name: 'logout()', desc: 'Clear session cookie' },
{ type: 'SELECT', name: 'selectHasAuth() / selectHasCompany()', desc: 'Auth & tenant gates' },
{ type: 'SELECT', name: 'selectSchemaVersion()', desc: 'Active DB schema version' },
{ type: 'API', name: 'GET /api/auth/session', desc: 'Cookie session check' },
],
},

View File

@@ -8,12 +8,14 @@ export const auth = {
return {
user: data.user ?? null,
googleClientId: data.googleClientId ?? null,
schemaVersion: data.schemaVersion ?? null,
};
} catch (err) {
if (err.status === 401 || err.status === 403) {
return {
user: null,
googleClientId: err.data?.googleClientId ?? null,
schemaVersion: err.data?.schemaVersion ?? null,
forbidden: err.status === 403,
message: err.message,
};

View File

@@ -30,6 +30,7 @@ import { auth, company, number as numberApi } from '../api';
* @typedef {Object} AuthState
* @property {AuthUser|null} user Authenticated user, or `null` for anonymous sessions.
* @property {string|null} googleClientId Google Identity Services client ID.
* @property {number|null} schemaVersion Database schema version returned by the backend.
* @property {'loading'|'anon'|'authed'} status Current authentication lifecycle state.
* @property {boolean} hasAuth True when the session has a valid authenticated user.
* @property {boolean} hasCompany True when `hasAuth` is true AND a company is selected.
@@ -47,6 +48,7 @@ export const AUTH_LOGGED_OUT = 'auth/loggedOut';
const initialState = {
user: null,
googleClientId: null,
schemaVersion: null,
status: 'loading',
hasAuth: false,
hasCompany: false,
@@ -137,22 +139,26 @@ function syncSse(state) {
function applySession(nextSession) {
const googleClientId =
nextSession.googleClientId ?? authAutomat.state.googleClientId;
const schemaVersion =
nextSession.schemaVersion !== undefined
? nextSession.schemaVersion
: (authAutomat.state.schemaVersion ?? null);
if (!nextSession.user) {
if (authAutomat.state.status !== 'authed') {
const nextState = authAutomat.setState(
buildAuthState(null, 'anon', { googleClientId, retryNotification: null })
buildAuthState(null, 'anon', { googleClientId, schemaVersion, retryNotification: null })
);
syncSse(nextState);
} else {
authAutomat.setState({ googleClientId, retryNotification: null });
authAutomat.setState({ googleClientId, schemaVersion, retryNotification: null });
}
return nextSession;
}
const user = mergeUser(authAutomat.state.user, nextSession.user);
const nextState = authAutomat.setState(
buildAuthState(user, 'authed', { googleClientId, retryNotification: null })
buildAuthState(user, 'authed', { googleClientId, schemaVersion, retryNotification: null })
);
syncSse(nextState);
emit({ type: AUTH_SESSION_ESTABLISHED });
@@ -234,6 +240,7 @@ const actions = {
const nextState = authAutomat.setState(
buildAuthState(user, 'authed', {
googleClientId: session.googleClientId ?? authAutomat.state.googleClientId,
schemaVersion: session.schemaVersion ?? authAutomat.state.schemaVersion ?? null,
})
);
syncSse(nextState);
@@ -344,6 +351,7 @@ export const selectHasCompany = () => Boolean(authAutomat.state.hasCompany);
export const selectSelectedCompanyId = () =>
authAutomat.state.hasCompany ? authAutomat.state.user?.selectedCompanyId ?? null : null;
export const selectRetryNotification = () => authAutomat.state.retryNotification;
export const selectSchemaVersion = () => authAutomat.state.schemaVersion;
/**
* Re-reads `GET /api/auth/me` from the server.
@@ -351,7 +359,7 @@ export const selectRetryNotification = () => authAutomat.state.retryNotification
* Called after login, logout, and company switches so the automat reflects
* the current server session rather than an optimistic guess.
*
* @returns {Promise<{user: AuthUser|null, googleClientId: string|null}>} Session.
* @returns {Promise<{user: AuthUser|null, googleClientId: string|null, schemaVersion?: number|null}>} Session.
*/
export function bootstrapAuthMe() {
return authAutomat.actions.bootstrapSession();

View File

@@ -13,6 +13,8 @@ import PointOfSaleIcon from '@mui/icons-material/PointOfSale';
import ImportExportIcon from '@mui/icons-material/ImportExport';
import CloseIcon from '@mui/icons-material/Close';
import IconButton from '@mui/material/IconButton';
import Typography from '@mui/material/Typography';
import authAutomat from '../../automats/authAutomat';
import CompanySettingsPanel from './CompanySettingsPanel';
import NumberSettingsPanel from './NumberSettingsPanel';
import PrinterSettingsPanel from './PrinterSettingsPanel';
@@ -54,15 +56,31 @@ const TABS = [
export default class SettingsDialog extends Component {
constructor(props) {
super(props);
this.state = { tab: TAB_KEYS.health };
this.state = {
tab: TAB_KEYS.health,
schemaVersion: authAutomat.state.schemaVersion,
};
this.handleTabChange = this.handleTabChange.bind(this);
}
componentDidMount() {
this.unsubAuth = authAutomat.subscribe(this, ['schemaVersion']);
}
componentWillUnmount() {
this.unsubAuth?.();
}
componentDidUpdate(prevProps) {
const { open } = this.props;
if (!open && prevProps.open) {
this.setState({ tab: TAB_KEYS.health });
}
if (open && !prevProps.open) {
if (this.state.schemaVersion !== authAutomat.state.schemaVersion) {
this.setState({ schemaVersion: authAutomat.state.schemaVersion });
}
}
}
handleTabChange(_event, value) {
@@ -71,7 +89,7 @@ export default class SettingsDialog extends Component {
render() {
const { open, onClose } = this.props;
const { tab } = this.state;
const { tab, schemaVersion } = this.state;
const paperSx = {
height: '85vh',
@@ -120,6 +138,9 @@ export default class SettingsDialog extends Component {
borderColor: 'divider',
bgcolor: SURFACE_BASE,
py: 2,
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
overflowY: 'auto',
}}
>
@@ -172,6 +193,13 @@ export default class SettingsDialog extends Component {
/>
))}
</Tabs>
{schemaVersion != null && (
<Box sx={{ px: 2.5, py: 1.5, mt: 'auto', borderTop: '1px solid', borderColor: 'divider' }}>
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', px: 0.5 }}>
DB-Version: {schemaVersion}
</Typography>
</Box>
)}
</Box>
{/* Right content panel */}