iobroker.eos-admin 7.9.34 → 7.9.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/adminWww/assets/Laundry Room-zhlszIKu.js +1 -1
- package/adminWww/assets/Locker Room-CyyDtimo.js +1 -1
- package/adminWww/assets/Nursery-Cxrv-xmv.js +1 -1
- package/adminWww/assets/Sleeping Area-41NoHRj5.js +1 -1
- package/adminWww/css/eos-branding.css +84 -0
- package/adminWww/index.html +4 -5
- package/adminWww/js/eos-assistant.js +1 -1
- package/adminWww/js/eos-branding.js +70 -16
- package/adminWww/js/eos-hard-logout.js +15 -160
- package/adminWww/js/eos-security-ui.js +4 -1
- package/build/lib/web.js +101 -78
- package/docs/NEXOWATT_EOS_UI_V34_UPDATE_HARD_LOGOUT_DE.md +3 -3
- package/io-package.json +11 -7
- package/package.json +1 -1
- package/public/404.html +12 -0
- package/tools/nexowatt-validate-package.cjs +6 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
const h="data:image/svg+xml,%3csvg%20height='512'%20viewBox='0%200%2064%2064'%20width='512'%20xmlns='http://www.w3.org/2000/svg'%3e%3cg%20fill='currentColor'%3e%3cpath%20d='m61%202h-58c-.552%200-1%20.448-1%201v58c0%20.552.448%201%201%201h58c.552%200%201-.448%201-1v-58c0-.552-.448-1-1-1zm-1%206h-4v-2c0-.552-.448-1-1-1h-6c-.552%200-1%20.448-1%201v2h-3c-.552%200-1%20.448-1%201v9h-2v-14h18zm-12.142%2031-1.692-11h9.669l-1.692%2011zm1.142-13c0-1.103.897-2%202-2s2%20.897%202%202zm8.759.349c-.19-.222-.467-.349-.759-.349h-2c0-2.206-1.794-4-4-4s-4%201.794-4%204h-2c-.292%200-.569.127-.759.349s-.274.515-.229.803l1.823%2011.848h-3.835v-19h18v19h-3.835l1.823-11.848c.045-.288-.039-.582-.229-.803zm-7.759-17.349v-2h4v11h-4zm-2%209h-2v-8h2zm8%200v-8h4v8zm-16-14v14h-2v-10c0-.552-.448-1-1-1h-6c-.552%200-1%20.448-1%201v10h-2v-7c0-.265-.105-.52-.293-.707l-1.707-1.707v-2.586c0-.552-.448-1-1-1h-4c-.552%200-1%20.448-1%201v2.586l-1.707%201.707c-.188.187-.293.442-.293.707v7h-2v-10c0-.552-.448-1-1-1h-8c-.552%200-1%20.448-1%201v10h-2v-14zm-4%2014h-4v-9h4zm-11.707-8.293%201.707%201.707v6.586h-6v-6.586l1.707-1.707c.188-.187.293-.442.293-.707v-2h2v2c0%20.265.105.52.293.707zm-10.293%208.293h-6v-9h6zm-10%202h36v40h-36zm45%2028h-2v-1h2zm-3%202h4.649l4.351%203.48v6.52h-9zm11%2010v-7c0-.304-.138-.591-.375-.781l-5-4c-.178-.142-.398-.219-.625-.219v-2c0-.552-.448-1-1-1h-4c-.552%200-1%20.448-1%201v2c-.552%200-1%20.448-1%201v11h-2v-19h18v19z'/%3e%3cpath%20d='m37%2022h-30c-.552%200-1%20.448-1%
|
|
1
|
+
const h="data:image/svg+xml,%3csvg%20height='512'%20viewBox='0%200%2064%2064'%20width='512'%20xmlns='http://www.w3.org/2000/svg'%3e%3cg%20fill='currentColor'%3e%3cpath%20d='m61%202h-58c-.552%200-1%20.448-1%201v58c0%20.552.448%201%201%201h58c.552%200%201-.448%201-1v-58c0-.552-.448-1-1-1zm-1%206h-4v-2c0-.552-.448-1-1-1h-6c-.552%200-1%20.448-1%201v2h-3c-.552%200-1%20.448-1%201v9h-2v-14h18zm-12.142%2031-1.692-11h9.669l-1.692%2011zm1.142-13c0-1.103.897-2%202-2s2%20.897%202%202zm8.759.349c-.19-.222-.467-.349-.759-.349h-2c0-2.206-1.794-4-4-4s-4%201.794-4%204h-2c-.292%200-.569.127-.759.349s-.274.515-.229.803l1.823%2011.848h-3.835v-19h18v19h-3.835l1.823-11.848c.045-.288-.039-.582-.229-.803zm-7.759-17.349v-2h4v11h-4zm-2%209h-2v-8h2zm8%200v-8h4v8zm-16-14v14h-2v-10c0-.552-.448-1-1-1h-6c-.552%200-1%20.448-1%201v10h-2v-7c0-.265-.105-.52-.293-.707l-1.707-1.707v-2.586c0-.552-.448-1-1-1h-4c-.552%200-1%20.448-1%201v2.586l-1.707%201.707c-.188.187-.293.442-.293.707v7h-2v-10c0-.552-.448-1-1-1h-8c-.552%200-1%20.448-1%201v10h-2v-14zm-4%2014h-4v-9h4zm-11.707-8.293%201.707%201.707v6.586h-6v-6.586l1.707-1.707c.188-.187.293-.442.293-.707v-2h2v2c0%20.265.105.52.293.707zm-10.293%208.293h-6v-9h6zm-10%202h36v40h-36zm45%2028h-2v-1h2zm-3%202h4.649l4.351%203.48v6.52h-9zm11%2010v-7c0-.304-.138-.591-.375-.781l-5-4c-.178-.142-.398-.219-.625-.219v-2c0-.552-.448-1-1-1h-4c-.552%200-1%20.448-1%201v2c-.552%200-1%20.448-1%201v11h-2v-19h18v19z'/%3e%3cpath%20d='m37%2022h-30c-.552%200-1%20.448-1%201v35c0%20.552.448%201%201%201h30c.552%200%201-.448%201-1v-34c0-.552-.448-1-1-1zm-1%202v5h-28v-5zm-28%2032v-25h28v25z'/%3e%3cpath%20d='m22%2033c-6.065%200-11%204.935-11%2011s4.935%2011%2011%2011%2011-4.935%2011-11-4.935-11-11-11zm0%2020c-4.962%200-9-4.038-9-9s4.038-9%209-9%209%204.038%209%209-4.038%209-9%209z'/%3e%3cpath%20d='m26.199%2048.286%201.399%201.429c.677-.663%201.226-1.432%201.631-2.286l-1.807-.857c-.303.639-.714%201.216-1.223%201.714z'/%3e%3cpath%20d='m22%2036v2c3.309%200%206%202.691%206%206h2c0-4.411-3.589-8-8-8z'/%3e%3cpath%20d='m10%2025h2v2h-2z'/%3e%3cpath%20d='m14%2025h2v2h-2z'/%3e%3cpath%20d='m32%2025h2v2h-2z'/%3e%3c/g%3e%3c/svg%3e";export{h as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const c="data:image/svg+xml,%3csvg%20height='512'%20viewBox='0%200%2060%2060'%20width='512'%20xmlns='http://www.w3.org/2000/svg'%3e%3cg%20fill='currentColor'%3e%3cpath%20d='m23%204h-15c-2.209139%200-4%201.790861-4%
|
|
1
|
+
const c="data:image/svg+xml,%3csvg%20height='512'%20viewBox='0%200%2060%2060'%20width='512'%20xmlns='http://www.w3.org/2000/svg'%3e%3cg%20fill='currentColor'%3e%3cpath%20d='m23%204h-15c-2.209139%200-4%201.790861-4%204v35c0%202.209139%201.790861%204%204%204h6c.5522847%200%201-.4477153%201-1s-.4477153-1-1-1h-6c-1.1045695%200-2-.8954305-2-2v-34c0-1.1045695.8954305-2%202-2h15c1.1045695%200%202%20.8954305%202%202v12.05c0%20.5522847.4477153%201%201%201s1-.4477153%201-1v-12.05c0-2.209139-1.790861-4-4-4z'/%3e%3cpath%20d='m11%2012h9c.5522847%200%201-.4477153%201-1s-.4477153-1-1-1h-9c-.5522847%200-1%20.4477153-1%201s.4477153%201%201%201z'/%3e%3cpath%20d='m11%2016h9c.5522847%200%201-.4477153%201-1s-.4477153-1-1-1h-9c-.5522847%200-1%20.4477153-1%201s.4477153%201%201%201z'/%3e%3cpath%20d='m21%2019c0-.5522847-.4477153-1-1-1h-9c-.5522847%200-1%20.4477153-1%201s.4477153%201%201%201h9c.5522847%200%201-.4477153%201-1z'/%3e%3cpath%20d='m9%2035c-.55228475%200-1%20.4477153-1%201v5c0%20.5522847.44771525%201%201%201h3c.5522847%200%201-.4477153%201-1s-.4477153-1-1-1h-2v-4c0-.5522847-.44771525-1-1-1z'/%3e%3cpath%20d='m52%200h-15c-2.9122142.00180691-5.5934008%201.58600518-7%204.136-1.4065992-2.54999482-4.0877858-4.13419309-7-4.136h-15c-4.41622192.00495988-7.99504012%203.58377808-8%208v35c.00495988%204.4162219%203.58377808%207.9950401%208%208h6c.5522847%200%201-.4477153%201-1s-.4477153-1-1-1h-6c-3.31233757-.0033074-5.99669262-2.6876624-6-6v-34c.00330738-3.31233757%202.68766243-5.99669262%206-6h15c3.3123376.00330738%205.9966926%202.68766243%206%206v12c0%20.5522847.4477153%201%201%201s1-.4477153%201-1v-12c.0033074-3.31233757%202.6876624-5.99669262%206-6h15c3.3123376.00330738%205.9966926%202.68766243%206%206v35c-.0033074%203.3123376-2.6876624%205.9966926-6%206h-6c-.5522847%200-1%20.4477153-1%201s.4477153%201%201%201h6c4.4162219-.0049599%207.9950401-3.5837781%208-8v-34c-.0049599-4.41622192-3.5837781-7.99504012-8-8z'/%3e%3cpath%20d='m46%2046h6c2.209139%200%204-1.790861%204-4v-34c0-2.209139-1.790861-4-4-4h-15c-2.209139%200-4%201.790861-4%204v12.05c0%20.5522847.4477153%201%201%201s1-.4477153%201-1v-12.05c0-1.1045695.8954305-2%202-2h15c1.1045695%200%202%20.8954305%202%202v35c0%201.1045695-.8954305%202-2%202h-6c-.5522847%200-1%20.4477153-1%201s.4477153%201%201%201z'/%3e%3cpath%20d='m40%2012h9c.5522847%200%201-.4477153%201-1s-.4477153-1-1-1h-9c-.5522847%200-1%20.4477153-1%201s.4477153%201%201%201z'/%3e%3cpath%20d='m40%2016h9c.5522847%200%201-.4477153%201-1s-.4477153-1-1-1h-9c-.5522847%200-1%20.4477153-1%201s.4477153%201%201%201z'/%3e%3cpath%20d='m40%2020h9c.5522847%200%201-.4477153%201-1s-.4477153-1-1-1h-9c-.5522847%200-1%20.4477153-1%201s.4477153%201%201%201z'/%3e%3cpath%20d='m49%2027v3c0%20.5522847.4477153%201%201%201s1-.4477153%201-1v-3c0-.5522847-.4477153-1-1-1s-1%20.4477153-1%201z'/%3e%3cpath%20d='m27%2023c-3.8641657.0044086-6.9955914%203.1358343-7%207v6c-1.6568542%200-3%201.3431458-3%203v18c0%201.6568542%201.3431458%203%203%203h20c1.6568542%200%203-1.3431458%203-3v-18c0-1.6568542-1.3431458-3-3-3v-6c-.0044086-3.8641657-3.1358343-6.9955914-7-7zm14%2016v18c0%20.5522847-.4477153%201-1%201h-20c-.5522847%200-1-.4477153-1-1v-18c0-.5522847.4477153-1%201-1h20c.5522847%200%201%20.4477153%201%201zm-7-3h-8v-6c0-.5522847.4477153-1%201-1h6c.5522847%200%201%20.4477153%201%201zm4-6v6h-2v-6c0-1.6568542-1.3431458-3-3-3h-6c-1.6568542%200-3%201.3431458-3%203v6h-2v-6c.0033061-2.7600532%202.2399468-4.9966939%205-5h6c2.7600532.0033061%204.9966939%202.2399468%205%205z'/%3e%3cpath%20d='m31.213%2041.144c-1.504172-.3693727-3.0946426-.0256068-4.312.932-1.2178834.9618134-1.9201275%202.4346089-1.9007403%203.9863657.0193871%201.5517568.7582076%203.0065481%201.9997403%203.9376343v3c0%201.6568542%201.3431458%203%203%203s3-1.3431458%203-3v-3c1.5445107-1.1569544%202.2853492-3.0994557%201.9035191-4.9910852-.3818301-1.8916296-1.8181587-3.3946643-3.6905191-3.8619148zm.587%207.256c-.4971479.3576066-.7942401.9306232-.8%201.543v3.057c0%20.5522847-.4477153%201-1%201s-1-.4477153-1-1v-3.081c-.0057082-.6020522-.2994259-1.1649492-.79-1.514-1.0350553-.7717872-1.4602164-2.1192507-1.0555364-3.3453131s1.548421-2.0556867%202.8395364-2.0596869c.2550309.0005085.5091144.0310522.757.091%201.055868.2632461%201.882907%201.0833772%202.155%202.137.3133746%201.1839695-.1244165%202.4395512-1.106%203.172z'/%3e%3c/g%3e%3c/svg%3e";export{c as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const c="data:image/svg+xml,%3csvg%20height='512'%20viewBox='0%200%20512%20512'%20width='512'%20xmlns='http://www.w3.org/2000/svg'%3e%3cg%20fill='currentColor'%3e%3cpath%20d='m510.417%20233.225-58.644-117.279c-15.939-31.877-47.978-51.68-83.62-51.68-8.586%200-16.984%201.188-25.032%203.437.105-1.489.178-2.988.178-4.503%200-34.849-28.351-63.2-63.2-63.2s-63.2%2028.352-63.2%2063.2c0%2030.993%2022.432%2056.828%2051.909%2062.169l-61.366%2061.47c-6.875%206.886-21.871%2027.82-10.333%2054.46-1.705-.191-3.421-.299-5.144-.299-23.087%200-42.336%2016.69-46.351%2038.635-7.74-4.144-16.573-6.501-25.949-6.501-30.419%200-55.167%2024.748-55.167%2055.167%200%209.063%202.211%2017.615%206.1%2025.167h-40.598v-338.468c0-8.284-6.716-15-15-15s-15%206.716-15%2015v482c0%208.284%206.716%2015%2015%2015s15-6.716%2015-15v-49.267h306.563c8.284%200%2015-6.716%2015-15v-64.267c0-8.284-6.716-15-15-15h-36.412c3.274-8.395%203.214-15.263%203.214-18.2%2028.469%200%2048.2-22.098%2048.2-47.133%200-25.989-21.144-47.133-47.133-47.133h-12.304l55.904-56.086%2053.736%20106.777-.103%20205.301c-.002%203.979%201.578%207.796%204.391%2010.611%202.813%202.814%206.63%204.396%2010.609%204.396h80.335c8.284%200%2015-6.716%2015-15v-257.066c0-2.329-.542-4.625-1.583-6.708zm-263.517-170.025c0-18.307%2014.894-33.2%2033.2-33.2s33.2%2014.894%2033.2%2033.2-14.894%2033.2-33.2%2033.2c-18.307%200-33.2-14.893-33.2-33.2zm-127.233%20239.933c13.877%200%2025.167%2011.29%2025.167%2025.167s-11.29%2025.167-25.167%2025.167-25.167-11.29-25.167-25.167%2011.29-25.167%2025.167-25.167zm201.896%2080.
|
|
1
|
+
const c="data:image/svg+xml,%3csvg%20height='512'%20viewBox='0%200%20512%20512'%20width='512'%20xmlns='http://www.w3.org/2000/svg'%3e%3cg%20fill='currentColor'%3e%3cpath%20d='m510.417%20233.225-58.644-117.279c-15.939-31.877-47.978-51.68-83.62-51.68-8.586%200-16.984%201.188-25.032%203.437.105-1.489.178-2.988.178-4.503%200-34.849-28.351-63.2-63.2-63.2s-63.2%2028.352-63.2%2063.2c0%2030.993%2022.432%2056.828%2051.909%2062.169l-61.366%2061.47c-6.875%206.886-21.871%2027.82-10.333%2054.46-1.705-.191-3.421-.299-5.144-.299-23.087%200-42.336%2016.69-46.351%2038.635-7.74-4.144-16.573-6.501-25.949-6.501-30.419%200-55.167%2024.748-55.167%2055.167%200%209.063%202.211%2017.615%206.1%2025.167h-40.598v-338.468c0-8.284-6.716-15-15-15s-15%206.716-15%2015v482c0%208.284%206.716%2015%2015%2015s15-6.716%2015-15v-49.267h306.563c8.284%200%2015-6.716%2015-15v-64.267c0-8.284-6.716-15-15-15h-36.412c3.274-8.395%203.214-15.263%203.214-18.2%2028.469%200%2048.2-22.098%2048.2-47.133%200-25.989-21.144-47.133-47.133-47.133h-12.304l55.904-56.086%2053.736%20106.777-.103%20205.301c-.002%203.979%201.578%207.796%204.391%2010.611%202.813%202.814%206.63%204.396%2010.609%204.396h80.335c8.284%200%2015-6.716%2015-15v-257.066c0-2.329-.542-4.625-1.583-6.708zm-263.517-170.025c0-18.307%2014.894-33.2%2033.2-33.2s33.2%2014.894%2033.2%2033.2-14.894%2033.2-33.2%2033.2c-18.307%200-33.2-14.893-33.2-33.2zm-127.233%20239.933c13.877%200%2025.167%2011.29%2025.167%2025.167s-11.29%2025.167-25.167%2025.167-25.167-11.29-25.167-25.167%2011.29-25.167%2025.167-25.167zm201.896%2080.334v35.267h-291.563v-34.267zm-17.13-112.467c9.447%200%2017.133%207.686%2017.133%2017.133%200%209.583-7.814%2017.133-17.133%2017.133h-16.067c-8.284%200-15%206.716-15%2015v16.067c0%209.583-7.814%2017.133-17.133%2017.133h-64.267c-9.447%200-17.133-7.686-17.133-17.133v-48.2c0-9.447%207.686-17.133%2017.133-17.133%209.297%200%2017.133%207.526%2017.133%2017.133v16.067c0%208.284%206.716%2015%2015%2015s15-6.716%2015-15v-16.067c0-9.429%207.616-17.133%2017.133-17.133zm177.567%20211h-50.326l.097-193.859c.001-2.344-.547-4.656-1.601-6.75l-64.685-128.533c-2.167-4.305-6.269-7.306-11.027-8.068-4.761-.762-9.594.81-12.995%204.222l-86.458%2086.739c-7.263%207.291-19.115%206.807-26.308-.386-7.381-7.381-7.728-19.611-.022-27.329l95.27-95.431c11.799-11.825%2027.5-18.337%2044.217-18.337%2024.199%200%2045.955%2013.448%2056.779%2035.097l57.059%20114.109z'/%3e%3c/g%3e%3c/svg%3e";export{c as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const c="data:image/svg+xml,%3csvg%20version='1.1'%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20480%20480'%3e%3cg%20fill='currentColor'%3e%3cpath%20d='M456,224c-14.133,0-24,6.301-24,15.32v27.505C419.333,241.793,399.85,224,376,224H152c-4.417-0.001-7.999,3.579-8,7.996%20c0,0.001,0,0.003,0,0.004v48.067c0.045-13.052-5.201-26.075-15.586-35.583c-19.552-17.902-49.915-16.564-67.816,2.989%20c-16.764,18.309-16.801,46.38-0.086,64.734c-4.612,0.508-8.968,2.38-12.512,5.375V115.199C48,104.434,37.461,96,24,96%20s-24,8.434-24,19.
|
|
1
|
+
const c="data:image/svg+xml,%3csvg%20version='1.1'%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20480%20480'%3e%3cg%20fill='currentColor'%3e%3cpath%20d='M456,224c-14.133,0-24,6.301-24,15.32v27.505C419.333,241.793,399.85,224,376,224H152c-4.417-0.001-7.999,3.579-8,7.996%20c0,0.001,0,0.003,0,0.004v48.067c0.045-13.052-5.201-26.075-15.586-35.583c-19.552-17.902-49.915-16.564-67.816,2.989%20c-16.764,18.309-16.801,46.38-0.086,64.734c-4.612,0.508-8.968,2.38-12.512,5.375V115.199C48,104.434,37.461,96,24,96%20s-24,8.434-24,19.199v355.602C0,471.566,10.539,480,24,480s24-8.434,24-19.199V408h384v56.68c0,9.019,9.867,15.32,24,15.32%20s24-6.301,24-15.32V239.32C480,230.301,470.133,224,456,224z%20M32,460.801c-0.086,0.656-2.773,3.199-8,3.199%20c-5.25,0-7.922-2.543-8-3.199V115.199c0.078-0.656,2.75-3.199,8-3.199c5.227,0,7.914,2.543,8,3.199V460.801z%20M160,240h216%20c30.172,0,53.172,44.926,55.758,88H160V240z%20M144,280.083v38.56c-3.452-3.353-7.848-5.566-12.597-6.342%20C139.793,303.137,143.956,291.6,144,280.083z%20M96,248c17.673,0,32,14.327,32,32c0,17.673-14.327,32-32,32s-32-14.327-32-32%20C64.019,262.335,78.335,248.019,96,248z%20M432,392H48v-48.023c4.163-0.328,7.374-3.801,7.375-7.977%20c-0.077-4.331,3.364-7.909,7.695-8h64.61c4.331,0.091,7.772,3.669,7.695,8c-0.001,4.417,3.579,7.999,7.996,8%20c0.001,0,0.003,0,0.004,0H432V392z%20M464,462.594c-5.169,1.875-10.831,1.875-16,0V241.406c5.169-1.875,10.831-1.875,16,0V462.594z'%20/%3e%3cpath%20d='M137.078,176l29.07-34.879c1.986-2.386,2.413-5.705,1.094-8.516c-1.316-2.81-4.139-4.605-7.242-4.605h-48v16h30.922%20l-29.07,34.879c-2.827,3.398-2.364,8.444,1.034,11.27c1.437,1.195,3.246,1.85,5.114,1.851h48v-16H137.078z'/%3e%3cpath%20d='M224.359,136l45.953-59.09c1.877-2.411,2.217-5.681,0.875-8.426c-1.344-2.743-4.132-4.483-7.188-4.484h-64v16h47.641%20l-45.953,59.09c-2.714,3.484-2.089,8.509,1.396,11.223c1.405,1.094,3.136,1.688,4.917,1.688h64v-16H224.359z'/%3e%3cpath%20d='M328,96l62.398-83.199c2.652-3.533,1.939-8.546-1.594-11.199C387.419,0.562,385.733,0,384,0h-72v16h56l-62.398,83.199%20c-1.818,2.424-2.112,5.668-0.758,8.379C306.2,110.288,308.97,112,312,112h80V96H328z'/%3e%3c/g%3e%3c/svg%3e";export{c as default};
|
|
@@ -3287,3 +3287,87 @@ html.eos-app button[class*="fab"] {
|
|
|
3287
3287
|
display: none !important;
|
|
3288
3288
|
}
|
|
3289
3289
|
}
|
|
3290
|
+
|
|
3291
|
+
|
|
3292
|
+
/* === NexoWatt EOS v35: login redirect + adapter config interaction fix ======
|
|
3293
|
+
Keep native adapter configuration pages fully clickable and reserve the bottom
|
|
3294
|
+
right corner for adapter/page floating actions. */
|
|
3295
|
+
html.eos-app.eos-adapter-config-surface #app-paper button,
|
|
3296
|
+
html.eos-app.eos-adapter-config-surface #app-paper [role="button"],
|
|
3297
|
+
html.eos-app.eos-adapter-config-surface #app-paper a,
|
|
3298
|
+
html.eos-app.eos-adapter-config-surface #app-paper input,
|
|
3299
|
+
html.eos-app.eos-adapter-config-surface #app-paper select,
|
|
3300
|
+
html.eos-app.eos-adapter-config-surface #app-paper textarea {
|
|
3301
|
+
pointer-events: auto !important;
|
|
3302
|
+
}
|
|
3303
|
+
html.eos-app.eos-adapter-config-surface #app-paper .MuiButton-root,
|
|
3304
|
+
html.eos-app.eos-adapter-config-surface #app-paper .MuiIconButton-root,
|
|
3305
|
+
html.eos-app.eos-adapter-config-surface #app-paper .MuiMenuItem-root {
|
|
3306
|
+
position: relative !important;
|
|
3307
|
+
z-index: 12 !important;
|
|
3308
|
+
}
|
|
3309
|
+
html.eos-app .eos-assist-root,
|
|
3310
|
+
html.eos-app #eos-assist-root {
|
|
3311
|
+
right: 150px !important;
|
|
3312
|
+
bottom: 22px !important;
|
|
3313
|
+
}
|
|
3314
|
+
@media (max-width: 1100px) {
|
|
3315
|
+
html.eos-app .eos-assist-root,
|
|
3316
|
+
html.eos-app #eos-assist-root { right: 126px !important; }
|
|
3317
|
+
}
|
|
3318
|
+
@media (max-width: 720px) {
|
|
3319
|
+
html.eos-app .eos-assist-root,
|
|
3320
|
+
html.eos-app #eos-assist-root { right: 96px !important; }
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3323
|
+
|
|
3324
|
+
/* === NexoWatt EOS v36: native adapter configuration safe mode =============
|
|
3325
|
+
Custom adapter configuration pages (React/HTML/jsonConfig) must be fully
|
|
3326
|
+
controlled by the adapter itself. EOS shell decoration is disabled inside the
|
|
3327
|
+
content area so buttons such as "Gerät hinzufügen" and "Gerät bearbeiten" stay
|
|
3328
|
+
clickable and dialogs/popovers are not hidden behind overlays. */
|
|
3329
|
+
html.eos-app.eos-adapter-config-surface #app-paper,
|
|
3330
|
+
html.eos-app.eos-adapter-config-surface #app-paper * {
|
|
3331
|
+
pointer-events: auto !important;
|
|
3332
|
+
}
|
|
3333
|
+
html.eos-app.eos-adapter-config-surface #app-paper button,
|
|
3334
|
+
html.eos-app.eos-adapter-config-surface #app-paper [role="button"],
|
|
3335
|
+
html.eos-app.eos-adapter-config-surface #app-paper a,
|
|
3336
|
+
html.eos-app.eos-adapter-config-surface #app-paper input,
|
|
3337
|
+
html.eos-app.eos-adapter-config-surface #app-paper select,
|
|
3338
|
+
html.eos-app.eos-adapter-config-surface #app-paper textarea,
|
|
3339
|
+
html.eos-app.eos-adapter-config-surface #app-paper .MuiButton-root,
|
|
3340
|
+
html.eos-app.eos-adapter-config-surface #app-paper .MuiIconButton-root,
|
|
3341
|
+
html.eos-app.eos-adapter-config-surface #app-paper .MuiMenuItem-root {
|
|
3342
|
+
pointer-events: auto !important;
|
|
3343
|
+
user-select: auto !important;
|
|
3344
|
+
}
|
|
3345
|
+
html.eos-app.eos-adapter-config-surface .eos-assist-root,
|
|
3346
|
+
html.eos-app.eos-adapter-config-surface #eos-assist-root,
|
|
3347
|
+
html.eos-app.eos-adapter-config-surface .eos-assist-config-hidden {
|
|
3348
|
+
display: none !important;
|
|
3349
|
+
visibility: hidden !important;
|
|
3350
|
+
pointer-events: none !important;
|
|
3351
|
+
}
|
|
3352
|
+
html.eos-app.eos-adapter-config-surface .MuiDialog-root,
|
|
3353
|
+
html.eos-app.eos-adapter-config-surface .MuiModal-root,
|
|
3354
|
+
html.eos-app.eos-adapter-config-surface .MuiPopover-root,
|
|
3355
|
+
html.eos-app.eos-adapter-config-surface .MuiMenu-root,
|
|
3356
|
+
html.eos-app.eos-adapter-config-surface .MuiDialog-container {
|
|
3357
|
+
pointer-events: auto !important;
|
|
3358
|
+
z-index: 4200 !important;
|
|
3359
|
+
}
|
|
3360
|
+
html.eos-app.eos-adapter-config-surface .MuiDialog-paper,
|
|
3361
|
+
html.eos-app.eos-adapter-config-surface .MuiPopover-paper,
|
|
3362
|
+
html.eos-app.eos-adapter-config-surface .MuiMenu-paper {
|
|
3363
|
+
pointer-events: auto !important;
|
|
3364
|
+
}
|
|
3365
|
+
html.eos-app.eos-adapter-config-surface #app-paper .eos-protected-delete-control,
|
|
3366
|
+
html.eos-app.eos-adapter-config-surface #app-paper .eos-security-hidden-delete,
|
|
3367
|
+
html.eos-app.eos-adapter-config-surface #app-paper .eos-protected-adapter-row {
|
|
3368
|
+
display: revert !important;
|
|
3369
|
+
visibility: visible !important;
|
|
3370
|
+
opacity: 1 !important;
|
|
3371
|
+
filter: none !important;
|
|
3372
|
+
pointer-events: auto !important;
|
|
3373
|
+
}
|
package/adminWww/index.html
CHANGED
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
rel="stylesheet"
|
|
32
32
|
href="css/leaflet.css"
|
|
33
33
|
/>
|
|
34
|
-
<link rel="stylesheet" href="./css/eos-branding.css?v=
|
|
34
|
+
<link rel="stylesheet" href="./css/eos-branding.css?v=36" />
|
|
35
35
|
<link
|
|
36
36
|
rel="manifest"
|
|
37
37
|
href="manifest.json"
|
|
@@ -154,10 +154,9 @@
|
|
|
154
154
|
<script type="module" crossorigin src="./assets/index-CQZugZ1z.js"></script>
|
|
155
155
|
<link rel="modulepreload" crossorigin href="./assets/preload-helper-BDBacUwf.js">
|
|
156
156
|
<link rel="modulepreload" crossorigin href="./assets/iobroker_admin__mf_v__runtimeInit__mf_v__-g2X2zhAf.js">
|
|
157
|
-
<script defer src="./js/eos-branding.js?v=
|
|
158
|
-
<script defer src="./js/eos-security-ui.js?v=
|
|
159
|
-
<script defer src="./js/eos-
|
|
160
|
-
<script defer src="./js/eos-assistant.js?v=34"></script>
|
|
157
|
+
<script defer src="./js/eos-branding.js?v=36"></script>
|
|
158
|
+
<script defer src="./js/eos-security-ui.js?v=36"></script>
|
|
159
|
+
<script defer src="./js/eos-assistant.js?v=36"></script>
|
|
161
160
|
</head>
|
|
162
161
|
<body>
|
|
163
162
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
// v31: EOS Assist is rendered by eos-branding.js so it can share route/security state.
|
|
4
4
|
// This file stays as a lightweight compatibility hook for cache-safe deployments.
|
|
5
|
-
window.NEXOWATT_EOS_ASSIST_VERSION = '
|
|
5
|
+
window.NEXOWATT_EOS_ASSIST_VERSION = 'v35-login-config-fix';
|
|
6
6
|
})();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
window.NEXOWATT_EOS_UI_VERSION = '
|
|
4
|
+
window.NEXOWATT_EOS_UI_VERSION = 'v36-native-config-session-fix';
|
|
5
5
|
|
|
6
6
|
const BRAND = 'NexoWatt EOS';
|
|
7
7
|
const EOS_MEANING = 'Energy Operation System';
|
|
@@ -398,6 +398,9 @@
|
|
|
398
398
|
const applySecurityUiGuard = () => safe(() => {
|
|
399
399
|
const policy = state.securityPolicy;
|
|
400
400
|
applySecurityClasses();
|
|
401
|
+
// Do not apply EOS security decoration inside native adapter configuration pages.
|
|
402
|
+
// Adapter UIs must remain 100% functional; backend/role checks still protect EOS actions.
|
|
403
|
+
if (isAdapterConfigSurface()) return;
|
|
401
404
|
if (!policy.loaded) return;
|
|
402
405
|
if (isAdminUser()) {
|
|
403
406
|
document.querySelectorAll('.eos-hidden-legacy-admin, .eos-protected-adapter-row').forEach(el => {
|
|
@@ -448,12 +451,16 @@
|
|
|
448
451
|
});
|
|
449
452
|
|
|
450
453
|
const normalizeBadAddressAfterLogin = () => safe(() => {
|
|
451
|
-
if (!document.getElementById('app-paper')) return;
|
|
452
454
|
const pathname = window.location.pathname || '';
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
455
|
+
const badPath = /(?:%2f%23|%252f%2523|\/login|\/logout|\/404(?:\.html)?)/i.test(pathname)
|
|
456
|
+
|| /(?:^|[?&])(?:hard|origin)=/i.test(window.location.search || '');
|
|
457
|
+
if (!badPath) return;
|
|
458
|
+
const clean = new URL(ASSET_BASE);
|
|
459
|
+
clean.hash = window.location.hash && !/%23|hard=|login/i.test(window.location.hash) ? window.location.hash : '#/tab-intro';
|
|
460
|
+
if (document.getElementById('app-paper')) {
|
|
456
461
|
window.history.replaceState(null, document.title, `${clean.pathname}${clean.search}${clean.hash}`);
|
|
462
|
+
} else if (/(?:%2f%23|%252f%2523)/i.test(pathname)) {
|
|
463
|
+
window.location.replace(`${clean.pathname}${clean.search}#/tab-intro`);
|
|
457
464
|
}
|
|
458
465
|
});
|
|
459
466
|
|
|
@@ -465,6 +472,37 @@
|
|
|
465
472
|
document.documentElement.classList.toggle('eos-route-intro', routes.intro);
|
|
466
473
|
};
|
|
467
474
|
|
|
475
|
+
|
|
476
|
+
const isAdapterConfigSurface = () => document.documentElement.classList.contains('eos-adapter-config-surface');
|
|
477
|
+
|
|
478
|
+
const markAdapterConfigSurface = () => safe(() => {
|
|
479
|
+
const app = document.querySelector('#app-paper') || document.body;
|
|
480
|
+
const text = textOfElement(app).slice(0, 3500);
|
|
481
|
+
const isConfig = /Instanzeinstellungen:|Instance settings:|Gerät hinzufügen|Gerät bearbeiten|Geräteliste|Adapterkonfiguration|json exportieren|json importieren|Speichern und schließen|Save and close/i.test(text);
|
|
482
|
+
document.documentElement.classList.toggle('eos-adapter-config-surface', !!isConfig);
|
|
483
|
+
if (!isConfig) return;
|
|
484
|
+
|
|
485
|
+
// Native adapter configuration UIs are owned by the adapter. EOS must never
|
|
486
|
+
// block, rewrite or intercept their controls. This is critical for custom
|
|
487
|
+
// React/HTML configuration pages such as nexowatt-devices.
|
|
488
|
+
document.querySelectorAll('#app-paper button, #app-paper [role="button"], #app-paper a, #app-paper input, #app-paper select, #app-paper textarea, #app-paper [tabindex]').forEach(control => {
|
|
489
|
+
if (control.closest('.eos-assist-root, #eos-assist-root, .eos-standalone-nav-toggle')) return;
|
|
490
|
+
control.style.pointerEvents = 'auto';
|
|
491
|
+
control.removeAttribute('aria-disabled');
|
|
492
|
+
});
|
|
493
|
+
document.querySelectorAll('#app-paper .eos-protected-delete-control, #app-paper .eos-security-hidden-delete, #app-paper .eos-protected-adapter-row').forEach(el => {
|
|
494
|
+
el.classList.remove('eos-protected-delete-control', 'eos-security-hidden-delete', 'eos-protected-adapter-row');
|
|
495
|
+
el.removeAttribute('aria-disabled');
|
|
496
|
+
if (el.style) {
|
|
497
|
+
el.style.pointerEvents = '';
|
|
498
|
+
el.style.display = '';
|
|
499
|
+
el.style.visibility = '';
|
|
500
|
+
el.style.opacity = '';
|
|
501
|
+
}
|
|
502
|
+
if ('disabled' in el && !el.dataset.eosOriginalDisabled) el.disabled = false;
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
|
|
468
506
|
const getLoginCard = () => {
|
|
469
507
|
const input = document.querySelector('#username, input[name="username"], #password, input[type="password"]');
|
|
470
508
|
return input ? input.closest('.MuiPaper-root, form, main > div') : null;
|
|
@@ -533,9 +571,10 @@
|
|
|
533
571
|
|
|
534
572
|
const hardLogout = () => {
|
|
535
573
|
clearAuthStorage();
|
|
536
|
-
const
|
|
537
|
-
|
|
538
|
-
|
|
574
|
+
const logoutUrl = new URL('logout', window.location.origin + '/');
|
|
575
|
+
logoutUrl.searchParams.set('hard', '1');
|
|
576
|
+
logoutUrl.searchParams.set('ts', String(Date.now()));
|
|
577
|
+
window.location.replace(logoutUrl.href);
|
|
539
578
|
};
|
|
540
579
|
|
|
541
580
|
const scheduleHardLogoutCheck = delay => {
|
|
@@ -573,13 +612,10 @@
|
|
|
573
612
|
}
|
|
574
613
|
|
|
575
614
|
const installHardLogoutWatchdog = () => {
|
|
576
|
-
|
|
615
|
+
// v36: disabled. Upstream ioBroker Admin session handling is used again so
|
|
616
|
+
// the configured admin TTL is respected and native adapter config pages are
|
|
617
|
+
// not interrupted by duplicate EOS timers.
|
|
577
618
|
hardLogoutInstalled = true;
|
|
578
|
-
window.addEventListener('focus', () => checkHardLogoutSession(), { passive: true });
|
|
579
|
-
document.addEventListener('visibilitychange', () => {
|
|
580
|
-
if (!document.hidden) checkHardLogoutSession();
|
|
581
|
-
}, { passive: true });
|
|
582
|
-
scheduleHardLogoutCheck(2500);
|
|
583
619
|
};
|
|
584
620
|
|
|
585
621
|
const ensureBrandBadge = toolbar => {
|
|
@@ -990,6 +1026,11 @@
|
|
|
990
1026
|
document.querySelectorAll('.eos-assist-launcher,.eos-assist-panel:not(#eos-assist-root .eos-assist-panel)').forEach(el => el.remove());
|
|
991
1027
|
return;
|
|
992
1028
|
}
|
|
1029
|
+
if (isAdapterConfigSurface()) {
|
|
1030
|
+
const existing = document.getElementById('eos-assist-root');
|
|
1031
|
+
if (existing) existing.classList.add('eos-assist-config-hidden');
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
993
1034
|
|
|
994
1035
|
let root = document.getElementById('eos-assist-root');
|
|
995
1036
|
if (!root) {
|
|
@@ -1023,6 +1064,7 @@
|
|
|
1023
1064
|
document.body.appendChild(root);
|
|
1024
1065
|
}
|
|
1025
1066
|
|
|
1067
|
+
root.classList.remove('eos-assist-config-hidden');
|
|
1026
1068
|
const ctx = assistContext();
|
|
1027
1069
|
const button = root.querySelector('.eos-assist-button');
|
|
1028
1070
|
const input = root.querySelector('.eos-assist-input');
|
|
@@ -1172,6 +1214,7 @@
|
|
|
1172
1214
|
patchDocumentMeta();
|
|
1173
1215
|
patchLogin();
|
|
1174
1216
|
patchShell();
|
|
1217
|
+
markAdapterConfigSurface();
|
|
1175
1218
|
applyNavCompactPreference();
|
|
1176
1219
|
ensureStandaloneNavToggle();
|
|
1177
1220
|
installAssistDelegatedClick();
|
|
@@ -1183,8 +1226,17 @@
|
|
|
1183
1226
|
hideNativeLogoutNav();
|
|
1184
1227
|
hideOfficialNexoWattRepoWarning();
|
|
1185
1228
|
applySecurityUiGuard();
|
|
1186
|
-
|
|
1187
|
-
|
|
1229
|
+
if (isAdapterConfigSurface()) {
|
|
1230
|
+
['.MuiAppBar-root', '.MuiDrawer-paper', 'nav', '.eos-brand-badge', '.eos-top-toolbar'].forEach(selector => {
|
|
1231
|
+
document.querySelectorAll(selector).forEach(scope => {
|
|
1232
|
+
patchTextNodes(scope);
|
|
1233
|
+
patchAttributes(scope);
|
|
1234
|
+
});
|
|
1235
|
+
});
|
|
1236
|
+
} else {
|
|
1237
|
+
patchTextNodes(document.body || document.documentElement);
|
|
1238
|
+
patchAttributes(document.body || document.documentElement);
|
|
1239
|
+
}
|
|
1188
1240
|
};
|
|
1189
1241
|
|
|
1190
1242
|
const scopePatch = () => {
|
|
@@ -1194,6 +1246,7 @@
|
|
|
1194
1246
|
normalizeBadAddressAfterLogin();
|
|
1195
1247
|
patchLogin();
|
|
1196
1248
|
patchShell();
|
|
1249
|
+
markAdapterConfigSurface();
|
|
1197
1250
|
applyNavCompactPreference();
|
|
1198
1251
|
ensureStandaloneNavToggle();
|
|
1199
1252
|
installAssistDelegatedClick();
|
|
@@ -1207,6 +1260,7 @@
|
|
|
1207
1260
|
applySecurityUiGuard();
|
|
1208
1261
|
for (const scope of scopes.slice(0, 80)) {
|
|
1209
1262
|
if (!scope || !scope.isConnected) continue;
|
|
1263
|
+
if (isAdapterConfigSurface() && (scope.id === 'app-paper' || scope.closest?.('#app-paper'))) continue;
|
|
1210
1264
|
patchTextNodes(scope);
|
|
1211
1265
|
patchAttributes(scope);
|
|
1212
1266
|
}
|
|
@@ -1,163 +1,18 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
'use strict';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function clearStoredTokens() {
|
|
21
|
-
const storages = [window.localStorage, window.sessionStorage, window._localStorage, window._sessionStorage]
|
|
22
|
-
.filter(Boolean)
|
|
23
|
-
.filter((storage, index, array) => array.indexOf(storage) === index);
|
|
24
|
-
const tokenKeyPattern = /(access[_-]?token|refresh[_-]?token|token[_-]?expires|expires[_-]?in|oauth|bearer|auth|connection)/i;
|
|
25
|
-
for (const storage of storages) {
|
|
26
|
-
try {
|
|
27
|
-
const keys = [];
|
|
28
|
-
for (let i = 0; i < storage.length; i++) {
|
|
29
|
-
const key = storage.key(i);
|
|
30
|
-
if (key && (tokenKeyPattern.test(key) || key === STORAGE_KEY)) {
|
|
31
|
-
keys.push(key);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
keys.forEach(key => storage.removeItem(key));
|
|
35
|
-
} catch (e) {
|
|
36
|
-
// ignore blocked storage
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function clearAuthCookies() {
|
|
42
|
-
const cookieNames = ['access_token', 'refresh_token', 'connect.sid', 'ioBroker.sid'];
|
|
43
|
-
const paths = ['/', window.location.pathname.replace(/\/[^/]*$/, '/') || '/'];
|
|
44
|
-
for (const name of cookieNames) {
|
|
45
|
-
for (const path of paths) {
|
|
46
|
-
document.cookie = `${name}=; Max-Age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=${path}; SameSite=Lax`;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function readDeadline() {
|
|
52
|
-
try {
|
|
53
|
-
const value = Number(window.localStorage.getItem(STORAGE_KEY));
|
|
54
|
-
return Number.isFinite(value) && value > 0 ? value : 0;
|
|
55
|
-
} catch (e) {
|
|
56
|
-
return 0;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function writeDeadline(deadline) {
|
|
61
|
-
try {
|
|
62
|
-
window.localStorage.setItem(STORAGE_KEY, String(deadline));
|
|
63
|
-
} catch (e) {
|
|
64
|
-
// ignore blocked storage
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function hardLogout(reason) {
|
|
69
|
-
if (logoutStarted || isLoginPage()) return;
|
|
70
|
-
logoutStarted = true;
|
|
71
|
-
try { console.warn(LOG_PREFIX, reason || 'session expired'); } catch (e) {}
|
|
72
|
-
if (logoutTimer) {
|
|
73
|
-
clearTimeout(logoutTimer);
|
|
74
|
-
logoutTimer = null;
|
|
75
|
-
}
|
|
76
|
-
if (pollTimer) {
|
|
77
|
-
clearInterval(pollTimer);
|
|
78
|
-
pollTimer = null;
|
|
79
|
-
}
|
|
80
|
-
clearStoredTokens();
|
|
81
|
-
clearAuthCookies();
|
|
82
|
-
const origin = encodeURIComponent((window.location.pathname || '/') + (window.location.search || '') + (window.location.hash || ''));
|
|
83
|
-
window.location.href = './logout?origin=' + origin + '&hard=1';
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function scheduleHardLogout(deadline) {
|
|
87
|
-
const ms = Math.min(Math.max(deadline - Date.now() + 250, 250), MAX_TIMER_MS);
|
|
88
|
-
if (logoutTimer) clearTimeout(logoutTimer);
|
|
89
|
-
logoutTimer = setTimeout(() => hardLogout('configured login timeout reached'), ms);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function applyServerExpiration(expireInSec) {
|
|
93
|
-
const seconds = Number(expireInSec);
|
|
94
|
-
if (!Number.isFinite(seconds)) return;
|
|
95
|
-
if (seconds <= 0) {
|
|
96
|
-
hardLogout('server reported expired session');
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
if (seconds < MIN_TTL_SEC) return;
|
|
100
|
-
|
|
101
|
-
const now = Date.now();
|
|
102
|
-
const candidateDeadline = now + seconds * 1000;
|
|
103
|
-
const storedDeadline = readDeadline();
|
|
104
|
-
|
|
105
|
-
// Set the hard deadline once. If the server reports an earlier expiration later,
|
|
106
|
-
// tighten the deadline. Never extend it through refresh-token based renewal.
|
|
107
|
-
const deadline = !storedDeadline || candidateDeadline < storedDeadline - 5000 ? candidateDeadline : storedDeadline;
|
|
108
|
-
if (deadline !== storedDeadline) writeDeadline(deadline);
|
|
109
|
-
|
|
110
|
-
if (now >= deadline) {
|
|
111
|
-
hardLogout('stored hard deadline reached');
|
|
112
|
-
} else {
|
|
113
|
-
scheduleHardLogout(deadline);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
async function checkSession() {
|
|
118
|
-
if (logoutStarted) return;
|
|
119
|
-
if (isLoginPage()) {
|
|
120
|
-
clearStoredTokens();
|
|
121
|
-
clearAuthCookies();
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const storedDeadline = readDeadline();
|
|
126
|
-
if (storedDeadline && Date.now() >= storedDeadline) {
|
|
127
|
-
hardLogout('stored hard deadline reached');
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
try {
|
|
132
|
-
const response = await fetch('./session?hard=1&ts=' + Date.now(), {
|
|
133
|
-
credentials: 'include',
|
|
134
|
-
cache: 'no-store',
|
|
135
|
-
headers: { Accept: 'application/json' },
|
|
136
|
-
});
|
|
137
|
-
if (!response.ok) return;
|
|
138
|
-
const session = await response.json();
|
|
139
|
-
if (typeof session.expireInSec === 'number') {
|
|
140
|
-
applyServerExpiration(session.expireInSec);
|
|
141
|
-
}
|
|
142
|
-
} catch (e) {
|
|
143
|
-
// During update/restart the endpoint can be unavailable. Do not logout just because of a network error.
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function start() {
|
|
148
|
-
checkSession();
|
|
149
|
-
pollTimer = setInterval(checkSession, MIN_POLL_MS);
|
|
150
|
-
window.addEventListener('focus', checkSession, { passive: true });
|
|
151
|
-
document.addEventListener('visibilitychange', () => {
|
|
152
|
-
if (!document.hidden) checkSession();
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
window.NEXOWATT_EOS_HARD_LOGOUT = { version: VERSION, checkSession, hardLogout, clearStoredTokens, clearAuthCookies };
|
|
157
|
-
|
|
158
|
-
if (document.readyState === 'loading') {
|
|
159
|
-
document.addEventListener('DOMContentLoaded', start, { once: true });
|
|
160
|
-
} else {
|
|
161
|
-
start();
|
|
162
|
-
}
|
|
3
|
+
// NexoWatt EOS v36: compatibility stub.
|
|
4
|
+
// The custom hard-logout timer was removed because it could expire sessions
|
|
5
|
+
// earlier than the configured admin TTL and broke native adapter dialogs.
|
|
6
|
+
// Session handling is now delegated to the same OAuth/session flow as the
|
|
7
|
+
// upstream ioBroker Admin.
|
|
8
|
+
const VERSION = '36';
|
|
9
|
+
function noop() {}
|
|
10
|
+
window.NEXOWATT_EOS_HARD_LOGOUT = {
|
|
11
|
+
version: VERSION,
|
|
12
|
+
checkSession: noop,
|
|
13
|
+
hardLogout: noop,
|
|
14
|
+
clearStoredTokens: noop,
|
|
15
|
+
clearAuthCookies: noop,
|
|
16
|
+
disabled: true,
|
|
17
|
+
};
|
|
163
18
|
})();
|
|
@@ -220,6 +220,7 @@
|
|
|
220
220
|
};
|
|
221
221
|
|
|
222
222
|
const applyPolicyToDom = () => {
|
|
223
|
+
if (isAdapterConfigSurface()) return;
|
|
223
224
|
const admin = isAdminUser();
|
|
224
225
|
document.documentElement.classList.toggle('eos-security-admin-user', admin);
|
|
225
226
|
document.documentElement.classList.toggle('eos-security-non-admin-user', !admin);
|
|
@@ -230,6 +231,8 @@
|
|
|
230
231
|
hideEosSecuritySettingsForNonAdmins();
|
|
231
232
|
};
|
|
232
233
|
|
|
234
|
+
const isAdapterConfigSurface = () => document.documentElement.classList.contains('eos-adapter-config-surface') || /Instanzeinstellungen:|Instance settings:|Geräteliste|Gerät hinzufügen|Gerät bearbeiten/i.test(document.body?.textContent || '');
|
|
235
|
+
|
|
233
236
|
const scheduleApply = () => {
|
|
234
237
|
if (state.scheduled) return;
|
|
235
238
|
state.scheduled = true;
|
|
@@ -267,7 +270,7 @@
|
|
|
267
270
|
|
|
268
271
|
document.addEventListener('click', event => {
|
|
269
272
|
const target = event.target?.closest?.('button,[role="button"],a,[role="menuitem"],.MuiMenuItem-root');
|
|
270
|
-
if (!target || isAdminUser()) return;
|
|
273
|
+
if (!target || isAdminUser() || isAdapterConfigSurface()) return;
|
|
271
274
|
const label = normalizeFlat(`${target.textContent || ''} ${target.getAttribute?.('title') || ''} ${target.getAttribute?.('aria-label') || ''}`);
|
|
272
275
|
if (/loschen|delete|remove|deinstall|uninstall/.test(label)) {
|
|
273
276
|
target.classList.add('eos-security-hidden-delete');
|
package/build/lib/web.js
CHANGED
|
@@ -506,6 +506,57 @@ class Web {
|
|
|
506
506
|
});
|
|
507
507
|
}
|
|
508
508
|
|
|
509
|
+
|
|
510
|
+
getSafeLoginOrigin(req) {
|
|
511
|
+
let origin = '';
|
|
512
|
+
try {
|
|
513
|
+
const url = new URL(req.url, 'http://127.0.0.1');
|
|
514
|
+
origin = url.searchParams.get('origin') || '';
|
|
515
|
+
}
|
|
516
|
+
catch {
|
|
517
|
+
const match = req.url.match(/[?&]origin=([^&]*)/);
|
|
518
|
+
origin = match?.[1] || '';
|
|
519
|
+
}
|
|
520
|
+
if (!origin) {
|
|
521
|
+
return null;
|
|
522
|
+
}
|
|
523
|
+
try {
|
|
524
|
+
origin = decodeURIComponent(origin);
|
|
525
|
+
}
|
|
526
|
+
catch {
|
|
527
|
+
// keep raw value and validate below
|
|
528
|
+
}
|
|
529
|
+
origin = String(origin || '').trim();
|
|
530
|
+
// Reject encoded hashes/login/logout/404 and hard-timeout parameters.
|
|
531
|
+
if (!origin || /(?:%2f|%23|#|login|logout|404|hard=|undefined|null)/i.test(origin)) {
|
|
532
|
+
return null;
|
|
533
|
+
}
|
|
534
|
+
origin = origin.split('?')[0];
|
|
535
|
+
const pos = origin.lastIndexOf('/');
|
|
536
|
+
if (pos > 0) {
|
|
537
|
+
origin = origin.substring(0, pos);
|
|
538
|
+
}
|
|
539
|
+
else if (pos === 0) {
|
|
540
|
+
origin = '';
|
|
541
|
+
}
|
|
542
|
+
if (!origin || origin === '.') {
|
|
543
|
+
return null;
|
|
544
|
+
}
|
|
545
|
+
if (/^https?:\/\//i.test(origin)) {
|
|
546
|
+
try {
|
|
547
|
+
const parsed = new URL(origin);
|
|
548
|
+
return parsed.pathname && parsed.pathname !== '/' ? parsed.pathname.replace(/\/+$/, '') : null;
|
|
549
|
+
}
|
|
550
|
+
catch {
|
|
551
|
+
return null;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
if (!origin.startsWith('/')) {
|
|
555
|
+
return null;
|
|
556
|
+
}
|
|
557
|
+
return origin.replace(/\/+$/, '') || null;
|
|
558
|
+
}
|
|
559
|
+
|
|
509
560
|
/**
|
|
510
561
|
* Initialize the server
|
|
511
562
|
*/
|
|
@@ -532,6 +583,16 @@ class Web {
|
|
|
532
583
|
this.server.app.get('/version', (_req, res) => {
|
|
533
584
|
res.status(200).send(this.adapter.version);
|
|
534
585
|
});
|
|
586
|
+
// v36: Repair stale/broken URLs generated by older EOS hard-logout builds.
|
|
587
|
+
this.server.app.use((req, res, next) => {
|
|
588
|
+
const requested = String(req.originalUrl || req.url || '');
|
|
589
|
+
if (/(?:%2f|%252f)(?:%23|%2523)|\/login\/|\/logout\/|hard=1/i.test(requested)
|
|
590
|
+
&& /index\.html|login|hard=1|origin=|%23|%2523/i.test(requested)) {
|
|
591
|
+
res.redirect(this.LOGIN_PAGE);
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
next();
|
|
595
|
+
});
|
|
535
596
|
// replace socket.io
|
|
536
597
|
this.server.app.use((req, res, next) => {
|
|
537
598
|
const url = req.url.split('?')[0];
|
|
@@ -582,48 +643,16 @@ class Web {
|
|
|
582
643
|
this.server.app.use(cookieParser());
|
|
583
644
|
this.server.app.use(bodyParser.urlencoded({ extended: true }));
|
|
584
645
|
this.server.app.use(bodyParser.json());
|
|
585
|
-
//
|
|
586
|
-
// We remove refresh tokens from OAuth responses so the admin client cannot silently extend sessions.
|
|
587
|
-
const eosHardLogout = this.adapter.config.nexowattHardLogout !== false;
|
|
588
|
-
if (eosHardLogout) {
|
|
589
|
-
this.server.app.use('/oauth/token', (req, res, next) => {
|
|
590
|
-
const grantType = String(req.body?.grant_type || '');
|
|
591
|
-
if (req.method === 'POST' && grantType === 'refresh_token') {
|
|
592
|
-
res.status(401).json({
|
|
593
|
-
error: 'invalid_grant',
|
|
594
|
-
error_description: 'EOS session expired. Please log in again.',
|
|
595
|
-
eosHardLogout: true,
|
|
596
|
-
});
|
|
597
|
-
return;
|
|
598
|
-
}
|
|
599
|
-
const originalJson = res.json.bind(res);
|
|
600
|
-
res.json = body => {
|
|
601
|
-
if (body && typeof body === 'object' && body.access_token && body.expires_in) {
|
|
602
|
-
const expiresIn = Math.max(1, Math.round(Number(body.expires_in) || Number(this.settings.ttl) || 3600));
|
|
603
|
-
body.refresh_token = '';
|
|
604
|
-
body.refresh_token_expires_in = expiresIn;
|
|
605
|
-
body.eosHardLogout = true;
|
|
606
|
-
}
|
|
607
|
-
return originalJson(body);
|
|
608
|
-
};
|
|
609
|
-
next();
|
|
610
|
-
});
|
|
611
|
-
}
|
|
646
|
+
// v36: OAuth/session handling intentionally follows upstream ioBroker Admin.
|
|
612
647
|
this.oauth2Model = (0, webserver_1.createOAuth2Server)(this.adapter, {
|
|
613
648
|
app: this.server.app,
|
|
614
649
|
secure: this.settings.secure,
|
|
615
650
|
accessLifetime: this.settings.ttl,
|
|
616
|
-
refreshLifetime:
|
|
651
|
+
refreshLifetime: 60 * 60 * 24 * 7, // 1 week, same as upstream admin
|
|
617
652
|
noBasicAuth: this.settings.noBasicAuth,
|
|
618
653
|
loginPage: (req) => {
|
|
619
654
|
const isDev = req.url.includes('?dev');
|
|
620
|
-
|
|
621
|
-
if (origin) {
|
|
622
|
-
const pos = origin.lastIndexOf('/');
|
|
623
|
-
if (pos !== -1) {
|
|
624
|
-
origin = origin.substring(0, pos);
|
|
625
|
-
}
|
|
626
|
-
}
|
|
655
|
+
const origin = this.getSafeLoginOrigin(req);
|
|
627
656
|
if (isDev) {
|
|
628
657
|
return 'http://127.0.0.1:3000/index.html?login';
|
|
629
658
|
}
|
|
@@ -631,44 +660,44 @@ class Web {
|
|
|
631
660
|
},
|
|
632
661
|
});
|
|
633
662
|
this.server.app.get('/session', (req, res) => {
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
663
|
+
// v36: Follow upstream admin semantics again. Do not run a second
|
|
664
|
+
// EOS hard-logout timer here; the official OAuth/session handling
|
|
665
|
+
// already uses the configured access lifetime.
|
|
666
|
+
if (req.headers.cookie) {
|
|
667
|
+
const cookies = req.headers.cookie.split(';').find(c => c.trim().startsWith('access_token='));
|
|
668
|
+
let tokenCookie = cookies?.split('=')[1];
|
|
669
|
+
if (!tokenCookie && req.headers.authorization?.startsWith('Bearer ')) {
|
|
670
|
+
tokenCookie = req.headers.authorization.split(' ')[1];
|
|
671
|
+
}
|
|
672
|
+
else if (!tokenCookie && req.query?.token) {
|
|
673
|
+
tokenCookie = req.query.token;
|
|
674
|
+
}
|
|
675
|
+
if (tokenCookie) {
|
|
676
|
+
const candidates = new Set();
|
|
677
|
+
candidates.add(tokenCookie.startsWith('a:') ? tokenCookie : `a:${tokenCookie}`);
|
|
678
|
+
if (tokenCookie.length > 1)
|
|
679
|
+
candidates.add(`a:${tokenCookie[1]}`);
|
|
680
|
+
const ids = Array.from(candidates);
|
|
681
|
+
const readNext = (index) => {
|
|
682
|
+
const id = ids[index];
|
|
683
|
+
if (!id) {
|
|
684
|
+
res.json({ expireInSec: 0 });
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
void this.adapter.getSession(id, (token) => {
|
|
688
|
+
if (!token?.user) {
|
|
689
|
+
readNext(index + 1);
|
|
690
|
+
}
|
|
691
|
+
else {
|
|
692
|
+
res.json({ expireInSec: Math.round((token.aExp - Date.now()) / 1000) });
|
|
693
|
+
}
|
|
694
|
+
});
|
|
695
|
+
};
|
|
696
|
+
readNext(0);
|
|
654
697
|
return;
|
|
655
698
|
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
readNext(index + 1);
|
|
659
|
-
return;
|
|
660
|
-
}
|
|
661
|
-
const now = Date.now();
|
|
662
|
-
const expirations = [Number(token.aExp), Number(token.rExp), Number(token.expire), Number(token.expires), Number(token.expiresAt)].filter(value => Number.isFinite(value) && value > 0);
|
|
663
|
-
const expiresAt = expirations.length ? Math.min(...expirations) : now + (this.settings.ttl || 3600) * 1000;
|
|
664
|
-
res.json({
|
|
665
|
-
expireInSec: Math.max(0, Math.floor((expiresAt - now) / 1000)),
|
|
666
|
-
hardLogout: true,
|
|
667
|
-
user: token.user,
|
|
668
|
-
});
|
|
669
|
-
});
|
|
670
|
-
};
|
|
671
|
-
readNext(0);
|
|
699
|
+
}
|
|
700
|
+
res.json({ error: 'Cannot find session' });
|
|
672
701
|
});
|
|
673
702
|
this.server.app.get(/.*\/nexowatt\/security\/(?:context|session)$/, (req, res) => {
|
|
674
703
|
void this.sendEosSecuritySession(req, res).catch(e => {
|
|
@@ -690,13 +719,7 @@ class Web {
|
|
|
690
719
|
|
|
691
720
|
this.server.app.get('/logout', (req, res) => {
|
|
692
721
|
const isDev = req.url.includes('?dev');
|
|
693
|
-
|
|
694
|
-
if (origin) {
|
|
695
|
-
const pos = origin.lastIndexOf('/');
|
|
696
|
-
if (pos !== -1) {
|
|
697
|
-
origin = origin.substring(0, pos);
|
|
698
|
-
}
|
|
699
|
-
}
|
|
722
|
+
const origin = this.getSafeLoginOrigin(req);
|
|
700
723
|
for (const cookieName of ['access_token', 'refresh_token', 'connect.sid', 'io', 'ioBroker.sid', 'eos-admin.sid']) {
|
|
701
724
|
res.clearCookie(cookieName, { path: '/' });
|
|
702
725
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# NexoWatt EOS Admin
|
|
1
|
+
# NexoWatt EOS Admin v35
|
|
2
2
|
|
|
3
3
|
## Zweck
|
|
4
4
|
|
|
5
|
-
Version 7.9.
|
|
5
|
+
Version 7.9.35 korrigiert zwei produktive Punkte:
|
|
6
6
|
|
|
7
7
|
1. Repository-/Update-Metadaten zeigen vollständig auf die aktuelle npm-Version.
|
|
8
8
|
2. Die konfigurierte Abmeldezeit wird als harte Sitzungslaufzeit erzwungen.
|
|
@@ -11,7 +11,7 @@ Version 7.9.34 korrigiert zwei produktive Punkte:
|
|
|
11
11
|
|
|
12
12
|
In vorherigen Paketen konnten `common.meta`, `common.extIcon` und `common.readme` noch auf ältere unpkg-Versionen zeigen. Das konnte dazu führen, dass das Repository zwar eine neue Version anzeigt, der Admin beim Aktualisieren aber alte Metadaten nachlädt.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
v35 setzt diese Felder synchron auf `7.9.35`.
|
|
15
15
|
|
|
16
16
|
## Harte Abmeldung
|
|
17
17
|
|
package/io-package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "eos-admin",
|
|
4
|
-
"version": "7.9.
|
|
4
|
+
"version": "7.9.36",
|
|
5
5
|
"titleLang": {
|
|
6
6
|
"en": "NexoWatt EOS Admin",
|
|
7
7
|
"de": "NexoWatt EOS Admin",
|
|
@@ -18,9 +18,13 @@
|
|
|
18
18
|
"connectionType": "local",
|
|
19
19
|
"dataSource": "push",
|
|
20
20
|
"news": {
|
|
21
|
-
"7.9.
|
|
22
|
-
"en": "
|
|
23
|
-
"de": "
|
|
21
|
+
"7.9.36": {
|
|
22
|
+
"en": "Restores official admin session handling, fixes bad login redirect paths, and enables full native adapter configuration compatibility without EOS overlays blocking adapter UI actions.",
|
|
23
|
+
"de": "Stellt die offizielle Admin-Sitzungslogik wieder her, korrigiert fehlerhafte Login-Redirect-Pfade und aktiviert volle native Adapter-Konfigurationskompatibilität ohne blockierende EOS-Overlays."
|
|
24
|
+
},
|
|
25
|
+
"7.9.35": {
|
|
26
|
+
"en": "Fixes EOS Admin login redirect/logout origin handling and restores reliable interaction with adapter instance configuration dialogs.",
|
|
27
|
+
"de": "Korrigiert den EOS-Admin Login-/Logout-Redirect und stellt die Bedienung von Adapter-Instanzkonfigurationen wieder zuverlässig her."
|
|
24
28
|
},
|
|
25
29
|
"7.9.31": {
|
|
26
30
|
"en": "UI cleanup: fixes EOS Security German text encoding, removes the logout tile next to the compact navigation arrow, and expands EOS Assist with AI-ready multi-path setup guidance.",
|
|
@@ -202,7 +206,7 @@
|
|
|
202
206
|
"icon": "admin.svg",
|
|
203
207
|
"messagebox": true,
|
|
204
208
|
"enabled": true,
|
|
205
|
-
"extIcon": "https://unpkg.com/iobroker.eos-admin@7.9.
|
|
209
|
+
"extIcon": "https://unpkg.com/iobroker.eos-admin@7.9.36/admin/admin.svg",
|
|
206
210
|
"keywords": [
|
|
207
211
|
"NexoWatt",
|
|
208
212
|
"EOS",
|
|
@@ -213,7 +217,7 @@
|
|
|
213
217
|
"licensed"
|
|
214
218
|
],
|
|
215
219
|
"compact": true,
|
|
216
|
-
"readme": "https://unpkg.com/iobroker.eos-admin@7.9.
|
|
220
|
+
"readme": "https://unpkg.com/iobroker.eos-admin@7.9.36/README.md",
|
|
217
221
|
"authors": [
|
|
218
222
|
"bluefox <bluefox@ccu.io>",
|
|
219
223
|
"hobbyquaker <hq@ccu.io>"
|
|
@@ -282,7 +286,7 @@
|
|
|
282
286
|
"nondeletable": false,
|
|
283
287
|
"allowAdapterUpdate": true,
|
|
284
288
|
"allowAdapterDelete": false,
|
|
285
|
-
"meta": "https://unpkg.com/iobroker.eos-admin@7.9.
|
|
289
|
+
"meta": "https://unpkg.com/iobroker.eos-admin@7.9.36/io-package.json",
|
|
286
290
|
"npmPackage": "iobroker.eos-admin"
|
|
287
291
|
},
|
|
288
292
|
"native": {
|
package/package.json
CHANGED
package/public/404.html
CHANGED
|
@@ -64,6 +64,18 @@
|
|
|
64
64
|
color: #fff;
|
|
65
65
|
}
|
|
66
66
|
</style>
|
|
67
|
+
|
|
68
|
+
<script id="NEXOWATT_EOS_BAD_PATH_FIX">
|
|
69
|
+
(function () {
|
|
70
|
+
try {
|
|
71
|
+
var u = String(location.pathname + location.search + location.hash);
|
|
72
|
+
if (/(%2F%23|%252F%2523|hard=1|\/login\/|\/logout\/)/i.test(u)) {
|
|
73
|
+
location.replace('/index.html?login');
|
|
74
|
+
}
|
|
75
|
+
} catch (e) {}
|
|
76
|
+
})();
|
|
77
|
+
</script>
|
|
78
|
+
|
|
67
79
|
</head>
|
|
68
80
|
|
|
69
81
|
<body>
|
|
@@ -56,8 +56,11 @@ if (!bootstrap.includes('window.adapterName="eos-admin"')) fail('frontend bootst
|
|
|
56
56
|
if (bootstrap.includes('window.adapterName="admin"')) fail('frontend bootstrap still contains window.adapterName="admin"');
|
|
57
57
|
|
|
58
58
|
const webBuild = fs.readFileSync(path.join(root, 'build/lib/web.js'), 'utf8');
|
|
59
|
-
|
|
60
|
-
if (!webBuild.includes(
|
|
61
|
-
if (!webBuild.includes('
|
|
59
|
+
const branding = fs.readFileSync(path.join(root, 'adminWww/js/eos-branding.js'), 'utf8');
|
|
60
|
+
if (!webBuild.includes('refreshLifetime: 60 * 60 * 24 * 7')) fail('build/lib/web.js must keep upstream-compatible refresh lifetime');
|
|
61
|
+
if (!webBuild.includes('Follow upstream admin semantics again')) fail('build/lib/web.js does not contain the v36 session compatibility fix');
|
|
62
|
+
if (index.includes('eos-hard-logout.js')) fail('adminWww/index.html must not load the removed custom hard-logout timer');
|
|
63
|
+
if (!branding.includes('isAdapterConfigSurface')) fail('eos-branding.js lacks native adapter config safe-mode detection');
|
|
64
|
+
if (!branding.includes('Adapter UIs must remain 100% functional')) fail('eos-branding.js lacks native adapter config interaction guard');
|
|
62
65
|
|
|
63
66
|
console.log('[NexoWatt EOS package validation] OK');
|