1
0
Fork 0
mirror of https://github.com/binwiederhier/ntfy.git synced 2024-09-28 19:31:59 +02:00

Login page of "subscribe dialog", still WIP, but looking nice

This commit is contained in:
Philipp Heckel 2022-02-25 16:07:25 -05:00
parent 1599793de2
commit 6d343c0f1a
8 changed files with 366 additions and 11 deletions

View file

@ -860,8 +860,9 @@ func parseSince(r *http.Request, poll bool) (sinceTime, error) {
} }
func (s *Server) handleOptions(w http.ResponseWriter, _ *http.Request) error { func (s *Server) handleOptions(w http.ResponseWriter, _ *http.Request) error {
w.Header().Set("Access-Control-Allow-Origin", "*") // CORS, allow cross-origin requests
w.Header().Set("Access-Control-Allow-Methods", "GET, PUT, POST") w.Header().Set("Access-Control-Allow-Methods", "GET, PUT, POST")
w.Header().Set("Access-Control-Allow-Origin", "*") // CORS, allow cross-origin requests
w.Header().Set("Access-Control-Allow-Headers", "Authorization") // CORS, allow auth
return nil return nil
} }

273
web/package-lock.json generated
View file

@ -11,6 +11,7 @@
"@emotion/styled": "latest", "@emotion/styled": "latest",
"@mui/icons-material": "^5.4.2", "@mui/icons-material": "^5.4.2",
"@mui/material": "latest", "@mui/material": "latest",
"@mui/styles": "^5.4.2",
"react": "latest", "react": "latest",
"react-dom": "latest", "react-dom": "latest",
"react-router-dom": "^6.2.1", "react-router-dom": "^6.2.1",
@ -2363,6 +2364,46 @@
} }
} }
}, },
"node_modules/@mui/styles": {
"version": "5.4.2",
"resolved": "https://registry.npmjs.org/@mui/styles/-/styles-5.4.2.tgz",
"integrity": "sha512-BX75fNHmRF51yove9dBkH28gpSFjClOPDEnUwLTghPYN913OsqViS/iuCd61dxzygtEEmmeYuWfQjxu/F6vF5g==",
"dependencies": {
"@babel/runtime": "^7.17.0",
"@emotion/hash": "^0.8.0",
"@mui/private-theming": "^5.4.2",
"@mui/types": "^7.1.2",
"@mui/utils": "^5.4.2",
"clsx": "^1.1.1",
"csstype": "^3.0.10",
"hoist-non-react-statics": "^3.3.2",
"jss": "^10.8.2",
"jss-plugin-camel-case": "^10.8.2",
"jss-plugin-default-unit": "^10.8.2",
"jss-plugin-global": "^10.8.2",
"jss-plugin-nested": "^10.8.2",
"jss-plugin-props-sort": "^10.8.2",
"jss-plugin-rule-value-function": "^10.8.2",
"jss-plugin-vendor-prefixer": "^10.8.2",
"prop-types": "^15.7.2"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui"
},
"peerDependencies": {
"@types/react": "^16.8.6 || ^17.0.0",
"react": "^17.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@mui/system": { "node_modules/@mui/system": {
"version": "5.4.2", "version": "5.4.2",
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.4.2.tgz", "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.4.2.tgz",
@ -5642,6 +5683,15 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/css-vendor": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz",
"integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==",
"dependencies": {
"@babel/runtime": "^7.8.3",
"is-in-browser": "^1.0.2"
}
},
"node_modules/css-what": { "node_modules/css-what": {
"version": "5.1.0", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz",
@ -8521,6 +8571,11 @@
"resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM="
}, },
"node_modules/hyphenate-style-name": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",
"integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ=="
},
"node_modules/iconv-lite": { "node_modules/iconv-lite": {
"version": "0.4.24", "version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@ -9113,6 +9168,11 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/is-in-browser": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz",
"integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU="
},
"node_modules/is-negative-zero": { "node_modules/is-negative-zero": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
@ -10230,6 +10290,88 @@
"node": ">=0.6.0" "node": ">=0.6.0"
} }
}, },
"node_modules/jss": {
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/jss/-/jss-10.9.0.tgz",
"integrity": "sha512-YpzpreB6kUunQBbrlArlsMpXYyndt9JATbt95tajx0t4MTJJcCJdd4hdNpHmOIDiUJrF/oX5wtVFrS3uofWfGw==",
"dependencies": {
"@babel/runtime": "^7.3.1",
"csstype": "^3.0.2",
"is-in-browser": "^1.1.3",
"tiny-warning": "^1.0.2"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/jss"
}
},
"node_modules/jss-plugin-camel-case": {
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.9.0.tgz",
"integrity": "sha512-UH6uPpnDk413/r/2Olmw4+y54yEF2lRIV8XIZyuYpgPYTITLlPOsq6XB9qeqv+75SQSg3KLocq5jUBXW8qWWww==",
"dependencies": {
"@babel/runtime": "^7.3.1",
"hyphenate-style-name": "^1.0.3",
"jss": "10.9.0"
}
},
"node_modules/jss-plugin-default-unit": {
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.9.0.tgz",
"integrity": "sha512-7Ju4Q9wJ/MZPsxfu4T84mzdn7pLHWeqoGd/D8O3eDNNJ93Xc8PxnLmV8s8ZPNRYkLdxZqKtm1nPQ0BM4JRlq2w==",
"dependencies": {
"@babel/runtime": "^7.3.1",
"jss": "10.9.0"
}
},
"node_modules/jss-plugin-global": {
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.9.0.tgz",
"integrity": "sha512-4G8PHNJ0x6nwAFsEzcuVDiBlyMsj2y3VjmFAx/uHk/R/gzJV+yRHICjT4MKGGu1cJq2hfowFWCyrr/Gg37FbgQ==",
"dependencies": {
"@babel/runtime": "^7.3.1",
"jss": "10.9.0"
}
},
"node_modules/jss-plugin-nested": {
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.9.0.tgz",
"integrity": "sha512-2UJnDrfCZpMYcpPYR16oZB7VAC6b/1QLsRiAutOt7wJaaqwCBvNsosLEu/fUyKNQNGdvg2PPJFDO5AX7dwxtoA==",
"dependencies": {
"@babel/runtime": "^7.3.1",
"jss": "10.9.0",
"tiny-warning": "^1.0.2"
}
},
"node_modules/jss-plugin-props-sort": {
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.9.0.tgz",
"integrity": "sha512-7A76HI8bzwqrsMOJTWKx/uD5v+U8piLnp5bvru7g/3ZEQOu1+PjHvv7bFdNO3DwNPC9oM0a//KwIJsIcDCjDzw==",
"dependencies": {
"@babel/runtime": "^7.3.1",
"jss": "10.9.0"
}
},
"node_modules/jss-plugin-rule-value-function": {
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.9.0.tgz",
"integrity": "sha512-IHJv6YrEf8pRzkY207cPmdbBstBaE+z8pazhPShfz0tZSDtRdQua5jjg6NMz3IbTasVx9FdnmptxPqSWL5tyJg==",
"dependencies": {
"@babel/runtime": "^7.3.1",
"jss": "10.9.0",
"tiny-warning": "^1.0.2"
}
},
"node_modules/jss-plugin-vendor-prefixer": {
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.9.0.tgz",
"integrity": "sha512-MbvsaXP7iiVdYVSEoi+blrW+AYnTDvHTW6I6zqi7JcwXdc6I9Kbm234nEblayhF38EftoenbM+5218pidmC5gA==",
"dependencies": {
"@babel/runtime": "^7.3.1",
"css-vendor": "^2.0.8",
"jss": "10.9.0"
}
},
"node_modules/jsx-ast-utils": { "node_modules/jsx-ast-utils": {
"version": "2.4.1", "version": "2.4.1",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz",
@ -16215,6 +16357,11 @@
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
}, },
"node_modules/tiny-warning": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
},
"node_modules/tmp": { "node_modules/tmp": {
"version": "0.0.33", "version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@ -19371,6 +19518,30 @@
"prop-types": "^15.7.2" "prop-types": "^15.7.2"
} }
}, },
"@mui/styles": {
"version": "5.4.2",
"resolved": "https://registry.npmjs.org/@mui/styles/-/styles-5.4.2.tgz",
"integrity": "sha512-BX75fNHmRF51yove9dBkH28gpSFjClOPDEnUwLTghPYN913OsqViS/iuCd61dxzygtEEmmeYuWfQjxu/F6vF5g==",
"requires": {
"@babel/runtime": "^7.17.0",
"@emotion/hash": "^0.8.0",
"@mui/private-theming": "^5.4.2",
"@mui/types": "^7.1.2",
"@mui/utils": "^5.4.2",
"clsx": "^1.1.1",
"csstype": "^3.0.10",
"hoist-non-react-statics": "^3.3.2",
"jss": "^10.8.2",
"jss-plugin-camel-case": "^10.8.2",
"jss-plugin-default-unit": "^10.8.2",
"jss-plugin-global": "^10.8.2",
"jss-plugin-nested": "^10.8.2",
"jss-plugin-props-sort": "^10.8.2",
"jss-plugin-rule-value-function": "^10.8.2",
"jss-plugin-vendor-prefixer": "^10.8.2",
"prop-types": "^15.7.2"
}
},
"@mui/system": { "@mui/system": {
"version": "5.4.2", "version": "5.4.2",
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.4.2.tgz", "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.4.2.tgz",
@ -21984,6 +22155,15 @@
} }
} }
}, },
"css-vendor": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz",
"integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==",
"requires": {
"@babel/runtime": "^7.8.3",
"is-in-browser": "^1.0.2"
}
},
"css-what": { "css-what": {
"version": "5.1.0", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz",
@ -24238,6 +24418,11 @@
"resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM="
}, },
"hyphenate-style-name": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",
"integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ=="
},
"iconv-lite": { "iconv-lite": {
"version": "0.4.24", "version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@ -24661,6 +24846,11 @@
"is-extglob": "^2.1.1" "is-extglob": "^2.1.1"
} }
}, },
"is-in-browser": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz",
"integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU="
},
"is-negative-zero": { "is-negative-zero": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
@ -25539,6 +25729,84 @@
"verror": "1.10.0" "verror": "1.10.0"
} }
}, },
"jss": {
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/jss/-/jss-10.9.0.tgz",
"integrity": "sha512-YpzpreB6kUunQBbrlArlsMpXYyndt9JATbt95tajx0t4MTJJcCJdd4hdNpHmOIDiUJrF/oX5wtVFrS3uofWfGw==",
"requires": {
"@babel/runtime": "^7.3.1",
"csstype": "^3.0.2",
"is-in-browser": "^1.1.3",
"tiny-warning": "^1.0.2"
}
},
"jss-plugin-camel-case": {
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.9.0.tgz",
"integrity": "sha512-UH6uPpnDk413/r/2Olmw4+y54yEF2lRIV8XIZyuYpgPYTITLlPOsq6XB9qeqv+75SQSg3KLocq5jUBXW8qWWww==",
"requires": {
"@babel/runtime": "^7.3.1",
"hyphenate-style-name": "^1.0.3",
"jss": "10.9.0"
}
},
"jss-plugin-default-unit": {
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.9.0.tgz",
"integrity": "sha512-7Ju4Q9wJ/MZPsxfu4T84mzdn7pLHWeqoGd/D8O3eDNNJ93Xc8PxnLmV8s8ZPNRYkLdxZqKtm1nPQ0BM4JRlq2w==",
"requires": {
"@babel/runtime": "^7.3.1",
"jss": "10.9.0"
}
},
"jss-plugin-global": {
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.9.0.tgz",
"integrity": "sha512-4G8PHNJ0x6nwAFsEzcuVDiBlyMsj2y3VjmFAx/uHk/R/gzJV+yRHICjT4MKGGu1cJq2hfowFWCyrr/Gg37FbgQ==",
"requires": {
"@babel/runtime": "^7.3.1",
"jss": "10.9.0"
}
},
"jss-plugin-nested": {
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.9.0.tgz",
"integrity": "sha512-2UJnDrfCZpMYcpPYR16oZB7VAC6b/1QLsRiAutOt7wJaaqwCBvNsosLEu/fUyKNQNGdvg2PPJFDO5AX7dwxtoA==",
"requires": {
"@babel/runtime": "^7.3.1",
"jss": "10.9.0",
"tiny-warning": "^1.0.2"
}
},
"jss-plugin-props-sort": {
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.9.0.tgz",
"integrity": "sha512-7A76HI8bzwqrsMOJTWKx/uD5v+U8piLnp5bvru7g/3ZEQOu1+PjHvv7bFdNO3DwNPC9oM0a//KwIJsIcDCjDzw==",
"requires": {
"@babel/runtime": "^7.3.1",
"jss": "10.9.0"
}
},
"jss-plugin-rule-value-function": {
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.9.0.tgz",
"integrity": "sha512-IHJv6YrEf8pRzkY207cPmdbBstBaE+z8pazhPShfz0tZSDtRdQua5jjg6NMz3IbTasVx9FdnmptxPqSWL5tyJg==",
"requires": {
"@babel/runtime": "^7.3.1",
"jss": "10.9.0",
"tiny-warning": "^1.0.2"
}
},
"jss-plugin-vendor-prefixer": {
"version": "10.9.0",
"resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.9.0.tgz",
"integrity": "sha512-MbvsaXP7iiVdYVSEoi+blrW+AYnTDvHTW6I6zqi7JcwXdc6I9Kbm234nEblayhF38EftoenbM+5218pidmC5gA==",
"requires": {
"@babel/runtime": "^7.3.1",
"css-vendor": "^2.0.8",
"jss": "10.9.0"
}
},
"jsx-ast-utils": { "jsx-ast-utils": {
"version": "2.4.1", "version": "2.4.1",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz",
@ -30352,6 +30620,11 @@
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
}, },
"tiny-warning": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
},
"tmp": { "tmp": {
"version": "0.0.33", "version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",

View file

@ -12,6 +12,7 @@
"@emotion/styled": "latest", "@emotion/styled": "latest",
"@mui/icons-material": "^5.4.2", "@mui/icons-material": "^5.4.2",
"@mui/material": "latest", "@mui/material": "latest",
"@mui/styles": "^5.4.2",
"react": "latest", "react": "latest",
"react-dom": "latest", "react-dom": "latest",
"react-router-dom": "^6.2.1", "react-router-dom": "^6.2.1",

View file

@ -23,7 +23,10 @@ class Api {
async auth(baseUrl, topic, user) { async auth(baseUrl, topic, user) {
const url = topicUrlAuth(baseUrl, topic); const url = topicUrlAuth(baseUrl, topic);
console.log(`[Api] Checking auth for ${url}`); console.log(`[Api] Checking auth for ${url}`);
const response = await fetch(url); const headers = this.maybeAddAuthorization({}, user);
const response = await fetch(url, {
headers: headers
});
if (response.status >= 200 && response.status <= 299) { if (response.status >= 200 && response.status <= 299) {
return true; return true;
} else if (!user && response.status === 404) { } else if (!user && response.status === 404) {
@ -33,6 +36,14 @@ class Api {
} }
throw new Error(`Unexpected server response ${response.status}`); throw new Error(`Unexpected server response ${response.status}`);
} }
maybeAddAuthorization(headers, user) {
if (user) {
const encoded = new Buffer(`${user.username}:${user.password}`).toString('base64');
headers['Authorization'] = `Basic ${encoded}`;
}
return headers;
}
} }
const api = new Api(); const api = new Api();

View file

@ -4,11 +4,11 @@ import Subscriptions from "./Subscriptions";
export class Repository { export class Repository {
loadSubscriptions() { loadSubscriptions() {
console.log(`[Repository] Loading subscriptions from localStorage`); console.log(`[Repository] Loading subscriptions from localStorage`);
const subscriptions = new Subscriptions(); const subscriptions = new Subscriptions();
const serialized = localStorage.getItem('subscriptions'); const serialized = localStorage.getItem('subscriptions');
if (serialized === null) return subscriptions; if (serialized === null) {
return subscriptions;
}
try { try {
const serializedSubscriptions = JSON.parse(serialized); const serializedSubscriptions = JSON.parse(serialized);
serializedSubscriptions.forEach(s => { serializedSubscriptions.forEach(s => {
@ -26,7 +26,6 @@ export class Repository {
saveSubscriptions(subscriptions) { saveSubscriptions(subscriptions) {
console.log(`[Repository] Saving ${subscriptions.size()} subscription(s) to localStorage`); console.log(`[Repository] Saving ${subscriptions.size()} subscription(s) to localStorage`);
const serialized = JSON.stringify(subscriptions.map( (id, subscription) => { const serialized = JSON.stringify(subscriptions.map( (id, subscription) => {
return { return {
baseUrl: subscription.baseUrl, baseUrl: subscription.baseUrl,
@ -37,6 +36,30 @@ export class Repository {
})); }));
localStorage.setItem('subscriptions', serialized); localStorage.setItem('subscriptions', serialized);
} }
loadUsers() {
console.log(`[Repository] Loading users from localStorage`);
const serialized = localStorage.getItem('users');
if (serialized === null) {
return {};
}
try {
return JSON.parse(serialized);
} catch (e) {
console.log(`[Repository] Unable to deserialize users: ${e.message}`);
return {};
}
}
saveUser(baseUrl, username, password) {
console.log(`[Repository] Saving users to localStorage`);
const users = this.loadUsers();
users[baseUrl] = {
username: username,
password: password
};
localStorage.setItem('users', users);
}
} }
const repository = new Repository(); const repository = new Repository();

View file

@ -12,6 +12,7 @@ import {useMediaQuery} from "@mui/material";
import theme from "./theme"; import theme from "./theme";
import api from "../app/Api"; import api from "../app/Api";
import {topicUrl} from "../app/utils"; import {topicUrl} from "../app/utils";
import useStyles from "./styles";
const defaultBaseUrl = "http://127.0.0.1" const defaultBaseUrl = "http://127.0.0.1"
//const defaultBaseUrl = "https://ntfy.sh" //const defaultBaseUrl = "https://ntfy.sh"
@ -19,6 +20,7 @@ const defaultBaseUrl = "http://127.0.0.1"
const SubscribeDialog = (props) => { const SubscribeDialog = (props) => {
const [baseUrl, setBaseUrl] = useState(defaultBaseUrl); // FIXME const [baseUrl, setBaseUrl] = useState(defaultBaseUrl); // FIXME
const [topic, setTopic] = useState(""); const [topic, setTopic] = useState("");
const [user, setUser] = useState(null);
const [showLoginPage, setShowLoginPage] = useState(false); const [showLoginPage, setShowLoginPage] = useState(false);
const fullScreen = useMediaQuery(theme.breakpoints.down('sm')); const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
const handleCancel = () => { const handleCancel = () => {
@ -45,8 +47,10 @@ const SubscribeDialog = (props) => {
onSubmit={handleSubmit} onSubmit={handleSubmit}
/>} />}
{showLoginPage && <LoginPage {showLoginPage && <LoginPage
baseUrl={baseUrl}
topic={topic} topic={topic}
onBack={() => setShowLoginPage(false)} onBack={() => setShowLoginPage(false)}
onSubmit={handleSubmit}
/>} />}
</Dialog> </Dialog>
); );
@ -82,6 +86,22 @@ const SubscribePage = (props) => {
}; };
const LoginPage = (props) => { const LoginPage = (props) => {
const styles = useStyles();
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [errorText, setErrorText] = useState("");
const baseUrl = props.baseUrl;
const topic = props.topic;
const handleLogin = async () => {
const user = {username: username, password: password};
const success = await api.auth(baseUrl, topic, user);
if (!success) {
console.log(`[SubscribeDialog] Login to ${topicUrl(baseUrl, topic)} failed for user ${username}`);
setErrorText(`User ${username} not authorized`);
return;
}
console.log(`[SubscribeDialog] Login to ${topicUrl(baseUrl, topic)} successful for user ${username}`);
};
return ( return (
<> <>
<DialogTitle>Login required</DialogTitle> <DialogTitle>Login required</DialogTitle>
@ -95,6 +115,8 @@ const LoginPage = (props) => {
margin="dense" margin="dense"
id="username" id="username"
label="Username, e.g. phil" label="Username, e.g. phil"
value={username}
onChange={ev => setUsername(ev.target.value)}
type="text" type="text"
fullWidth fullWidth
variant="standard" variant="standard"
@ -104,14 +126,21 @@ const LoginPage = (props) => {
id="password" id="password"
label="Password" label="Password"
type="password" type="password"
value={password}
onChange={ev => setPassword(ev.target.value)}
fullWidth fullWidth
variant="standard" variant="standard"
/> />
</DialogContent> </DialogContent>
<DialogActions> <div className={styles.bottomBar}>
<Button onClick={props.onBack}>Back</Button> <DialogContentText className={styles.statusText}>
<Button>Login</Button> {errorText}
</DialogActions> </DialogContentText>
<DialogActions>
<Button onClick={props.onBack}>Back</Button>
<Button onClick={handleLogin}>Login</Button>
</DialogActions>
</div>
</> </>
); );
}; };

View file

@ -0,0 +1,18 @@
import {makeStyles} from "@mui/styles";
const useStyles = makeStyles(theme => ({
bottomBar: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
paddingLeft: '24px',
paddingTop: '8px 24px',
paddingBottom: '8px 24px',
},
statusText: {
margin: '0px',
paddingTop: '8px',
}
}));
export default useStyles;

View file

@ -1,7 +1,6 @@
import { red } from '@mui/material/colors'; import { red } from '@mui/material/colors';
import { createTheme } from '@mui/material/styles'; import { createTheme } from '@mui/material/styles';
// A custom theme for this app
const theme = createTheme({ const theme = createTheme({
palette: { palette: {
primary: { primary: {