mirror of
				https://github.com/steveiliop56/tinyauth.git
				synced 2025-10-31 14:15:50 +00:00 
			
		
		
		
	Compare commits
	
		
			15 Commits
		
	
	
		
			feat/oauth
			...
			v3.3.0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | d1eeb8c7f7 | ||
|   | a98a91a394 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 5278fbea68 | ||
|   | 773942dc3b | ||
|   | 83483d6374 | ||
|   | aab01b3195 | ||
| ![github-actions[bot]](/assets/img/avatar_default.png)  | fe5e07139f | ||
|   | 93a75324b8 | ||
| ![github-actions[bot]](/assets/img/avatar_default.png)  | 67a01c196f | ||
|   | 483b1de701 | ||
|   | 40ceed6686 | ||
|   | 3878c629c6 | ||
|   | 31e874a34f | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 74a346349a | ||
|   | a9e8bf89a9 | 
| @@ -27,4 +27,5 @@ LOGIN_TIMEOUT=300 | ||||
| LOGIN_MAX_RETRIES=5 | ||||
| LOG_LEVEL=0 | ||||
| APP_TITLE=Tinyauth SSO | ||||
| FORGOT_PASSWORD_MESSAGE=Some message about resetting the password | ||||
| FORGOT_PASSWORD_MESSAGE=Some message about resetting the password | ||||
| OAUTH_AUTO_REDIRECT=none | ||||
							
								
								
									
										30
									
								
								.github/workflows/sponsors.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								.github/workflows/sponsors.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| name: Generate Sponsors List | ||||
| on: | ||||
|   workflow_dispatch: | ||||
|  | ||||
| jobs: | ||||
|   generate-sponsors: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@v4 | ||||
|  | ||||
|       - name: Generate Sponsors | ||||
|         uses: JamesIves/github-sponsors-readme-action@v1 | ||||
|         with: | ||||
|           token: ${{ secrets.SPONSORS_GENERATOR_PAT }} | ||||
|           file: README.md | ||||
|           template: '<a href="https://github.com/{{{ login }}}"><img src="{{{ avatarUrl }}}" width="64px" alt="User avatar: {{{ login }}}" /></a>  ' | ||||
|  | ||||
|       - name: Create Pull Request | ||||
|         uses: peter-evans/create-pull-request@v7 | ||||
|         with: | ||||
|           token: ${{ secrets.GITHUB_TOKEN }} | ||||
|           commit-message: | | ||||
|             docs: regenerate readme sponsors list | ||||
|           committer: GitHub <noreply@github.com> | ||||
|           author: GitHub <noreply@github.com> | ||||
|           branch: docs/update-readme | ||||
|           title: | | ||||
|             docs: regenerate readme sponsors list | ||||
|           labels: bot | ||||
| @@ -1,5 +1,5 @@ | ||||
| # Site builder | ||||
| FROM oven/bun:1.2.10-alpine AS frontend-builder | ||||
| FROM oven/bun:1.2.11-alpine AS frontend-builder | ||||
|  | ||||
| WORKDIR /frontend | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <div align="center"> | ||||
|     <img alt="Tinyauth" title="Tinyauth" width="256" src="frontend/public/logo.png"> | ||||
|     <img alt="Tinyauth" title="Tinyauth" height="256" src="frontend/public/logo.png"> | ||||
|     <h1>Tinyauth</h1> | ||||
|     <p>The easiest way to secure your apps with a login screen.</p> | ||||
| </div> | ||||
| @@ -53,9 +53,7 @@ Tinyauth is licensed under the GNU General Public License v3.0. TL;DR — You ma | ||||
|  | ||||
| Thanks a lot to the following people for providing me with more coffee: | ||||
|  | ||||
| | <div align="center"><img height="64" src="https://avatars.githubusercontent.com/u/47644445?v=4" alt="Nicolas"></div> | <div align="center"><img height="64" src="https://avatars.githubusercontent.com/u/4255748?v=4" alt="Erwin"></div> | <div align="center"><img height="64" src="https://avatars.githubusercontent.com/u/7935041?v=4" alt="SimpleHomelab"></div> | <div align="center"><img height="64" src="https://avatars.githubusercontent.com/u/30562276?v=4" alt="jmadden91"></div> | | ||||
| | -------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | | ||||
| | <div align="center"><a href="https://github.com/nicotsx">Nicolas</a></div>                                           | <div align="center"><a href="https://github.com/erwinkramer">Erwin</a></div>                                      | <div align="center"><a href="https://github.com/SimpleHomelab">SimpleHomelab</a></div>                                    | <div align="center"><a href="https://github.com/jmadden91">jmadden91</a></div>                                         | | ||||
| <!-- sponsors --><a href="https://github.com/nicotsx"><img src="https://github.com/nicotsx.png" width="64px" alt="User avatar: nicotsx" /></a>  <a href="https://github.com/SimpleHomelab"><img src="https://github.com/SimpleHomelab.png" width="64px" alt="User avatar: SimpleHomelab" /></a>  <a href="https://github.com/jmadden91"><img src="https://github.com/jmadden91.png" width="64px" alt="User avatar: jmadden91" /></a>  <a href="https://github.com/tribor"><img src="https://github.com/tribor.png" width="64px" alt="User avatar: tribor" /></a>  <!-- sponsors --> | ||||
|  | ||||
| ## Acknowledgements | ||||
|  | ||||
|   | ||||
| @@ -91,6 +91,7 @@ var rootCmd = &cobra.Command{ | ||||
| 			CookieSecure:          config.CookieSecure, | ||||
| 			Domain:                domain, | ||||
| 			ForgotPasswordMessage: config.FogotPasswordMessage, | ||||
| 			OAuthAutoRedirect:     config.OAuthAutoRedirect, | ||||
| 		} | ||||
|  | ||||
| 		// Create api config | ||||
| @@ -197,6 +198,7 @@ func init() { | ||||
| 	rootCmd.Flags().String("generic-name", "Generic", "Generic OAuth provider name.") | ||||
| 	rootCmd.Flags().Bool("disable-continue", false, "Disable continue screen and redirect to app directly.") | ||||
| 	rootCmd.Flags().String("oauth-whitelist", "", "Comma separated list of email addresses to whitelist when using OAuth.") | ||||
| 	rootCmd.Flags().String("oauth-auto-redirect", "none", "Auto redirect to the specified OAuth provider if configured. (available providers: github, google, generic)") | ||||
| 	rootCmd.Flags().Int("session-expiry", 86400, "Session (cookie) expiration time in seconds.") | ||||
| 	rootCmd.Flags().Int("login-timeout", 300, "Login timeout in seconds after max retries reached (0 to disable).") | ||||
| 	rootCmd.Flags().Int("login-max-retries", 5, "Maximum login attempts before timeout (0 to disable).") | ||||
| @@ -229,6 +231,7 @@ func init() { | ||||
| 	viper.BindEnv("generic-name", "GENERIC_NAME") | ||||
| 	viper.BindEnv("disable-continue", "DISABLE_CONTINUE") | ||||
| 	viper.BindEnv("oauth-whitelist", "OAUTH_WHITELIST") | ||||
| 	viper.BindEnv("oauth-auto-redirect", "OAUTH_AUTO_REDIRECT") | ||||
| 	viper.BindEnv("session-expiry", "SESSION_EXPIRY") | ||||
| 	viper.BindEnv("log-level", "LOG_LEVEL") | ||||
| 	viper.BindEnv("app-title", "APP_TITLE") | ||||
|   | ||||
							
								
								
									
										882
									
								
								frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										882
									
								
								frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -41,7 +41,7 @@ | ||||
|         "prettier": "3.5.3", | ||||
|         "typescript": "~5.8.3", | ||||
|         "typescript-eslint": "^8.18.2", | ||||
|         "vite": "^6.0.5" | ||||
|         "vite": "^6.3.4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@babel/runtime": { | ||||
| @@ -56,8 +56,282 @@ | ||||
|         "node": ">=6.9.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/aix-ppc64": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz", | ||||
|       "integrity": "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==", | ||||
|       "cpu": [ | ||||
|         "ppc64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "aix" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/android-arm": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.3.tgz", | ||||
|       "integrity": "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==", | ||||
|       "cpu": [ | ||||
|         "arm" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "android" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/android-arm64": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz", | ||||
|       "integrity": "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==", | ||||
|       "cpu": [ | ||||
|         "arm64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "android" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/android-x64": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.3.tgz", | ||||
|       "integrity": "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==", | ||||
|       "cpu": [ | ||||
|         "x64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "android" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/darwin-arm64": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz", | ||||
|       "integrity": "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==", | ||||
|       "cpu": [ | ||||
|         "arm64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "darwin" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/darwin-x64": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz", | ||||
|       "integrity": "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==", | ||||
|       "cpu": [ | ||||
|         "x64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "darwin" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/freebsd-arm64": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz", | ||||
|       "integrity": "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==", | ||||
|       "cpu": [ | ||||
|         "arm64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "freebsd" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/freebsd-x64": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz", | ||||
|       "integrity": "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==", | ||||
|       "cpu": [ | ||||
|         "x64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "freebsd" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/linux-arm": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz", | ||||
|       "integrity": "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==", | ||||
|       "cpu": [ | ||||
|         "arm" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "linux" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/linux-arm64": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz", | ||||
|       "integrity": "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==", | ||||
|       "cpu": [ | ||||
|         "arm64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "linux" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/linux-ia32": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz", | ||||
|       "integrity": "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==", | ||||
|       "cpu": [ | ||||
|         "ia32" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "linux" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/linux-loong64": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz", | ||||
|       "integrity": "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==", | ||||
|       "cpu": [ | ||||
|         "loong64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "linux" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/linux-mips64el": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz", | ||||
|       "integrity": "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==", | ||||
|       "cpu": [ | ||||
|         "mips64el" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "linux" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/linux-ppc64": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz", | ||||
|       "integrity": "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==", | ||||
|       "cpu": [ | ||||
|         "ppc64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "linux" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/linux-riscv64": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz", | ||||
|       "integrity": "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==", | ||||
|       "cpu": [ | ||||
|         "riscv64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "linux" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/linux-s390x": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz", | ||||
|       "integrity": "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==", | ||||
|       "cpu": [ | ||||
|         "s390x" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "linux" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/linux-x64": { | ||||
|       "version": "0.24.2", | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz", | ||||
|       "integrity": "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==", | ||||
|       "cpu": [ | ||||
|         "x64" | ||||
|       ], | ||||
| @@ -71,6 +345,142 @@ | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/netbsd-arm64": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz", | ||||
|       "integrity": "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==", | ||||
|       "cpu": [ | ||||
|         "arm64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "netbsd" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/netbsd-x64": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz", | ||||
|       "integrity": "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==", | ||||
|       "cpu": [ | ||||
|         "x64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "netbsd" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/openbsd-arm64": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz", | ||||
|       "integrity": "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==", | ||||
|       "cpu": [ | ||||
|         "arm64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "openbsd" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/openbsd-x64": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz", | ||||
|       "integrity": "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==", | ||||
|       "cpu": [ | ||||
|         "x64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "openbsd" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/sunos-x64": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz", | ||||
|       "integrity": "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==", | ||||
|       "cpu": [ | ||||
|         "x64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "sunos" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/win32-arm64": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz", | ||||
|       "integrity": "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==", | ||||
|       "cpu": [ | ||||
|         "arm64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "win32" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/win32-ia32": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz", | ||||
|       "integrity": "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==", | ||||
|       "cpu": [ | ||||
|         "ia32" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "win32" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@esbuild/win32-x64": { | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz", | ||||
|       "integrity": "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==", | ||||
|       "cpu": [ | ||||
|         "x64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "win32" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@eslint-community/eslint-utils": { | ||||
|       "version": "4.4.1", | ||||
|       "dev": true, | ||||
| @@ -399,8 +809,220 @@ | ||||
|         "node": ">= 8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@rollup/rollup-android-arm-eabi": { | ||||
|       "version": "4.40.1", | ||||
|       "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.1.tgz", | ||||
|       "integrity": "sha512-kxz0YeeCrRUHz3zyqvd7n+TVRlNyTifBsmnmNPtk3hQURUyG9eAB+usz6DAwagMusjx/zb3AjvDUvhFGDAexGw==", | ||||
|       "cpu": [ | ||||
|         "arm" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "android" | ||||
|       ] | ||||
|     }, | ||||
|     "node_modules/@rollup/rollup-android-arm64": { | ||||
|       "version": "4.40.1", | ||||
|       "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.1.tgz", | ||||
|       "integrity": "sha512-PPkxTOisoNC6TpnDKatjKkjRMsdaWIhyuMkA4UsBXT9WEZY4uHezBTjs6Vl4PbqQQeu6oION1w2voYZv9yquCw==", | ||||
|       "cpu": [ | ||||
|         "arm64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "android" | ||||
|       ] | ||||
|     }, | ||||
|     "node_modules/@rollup/rollup-darwin-arm64": { | ||||
|       "version": "4.40.1", | ||||
|       "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.1.tgz", | ||||
|       "integrity": "sha512-VWXGISWFY18v/0JyNUy4A46KCFCb9NVsH+1100XP31lud+TzlezBbz24CYzbnA4x6w4hx+NYCXDfnvDVO6lcAA==", | ||||
|       "cpu": [ | ||||
|         "arm64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "darwin" | ||||
|       ] | ||||
|     }, | ||||
|     "node_modules/@rollup/rollup-darwin-x64": { | ||||
|       "version": "4.40.1", | ||||
|       "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.1.tgz", | ||||
|       "integrity": "sha512-nIwkXafAI1/QCS7pxSpv/ZtFW6TXcNUEHAIA9EIyw5OzxJZQ1YDrX+CL6JAIQgZ33CInl1R6mHet9Y/UZTg2Bw==", | ||||
|       "cpu": [ | ||||
|         "x64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "darwin" | ||||
|       ] | ||||
|     }, | ||||
|     "node_modules/@rollup/rollup-freebsd-arm64": { | ||||
|       "version": "4.40.1", | ||||
|       "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.1.tgz", | ||||
|       "integrity": "sha512-BdrLJ2mHTrIYdaS2I99mriyJfGGenSaP+UwGi1kB9BLOCu9SR8ZpbkmmalKIALnRw24kM7qCN0IOm6L0S44iWw==", | ||||
|       "cpu": [ | ||||
|         "arm64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "freebsd" | ||||
|       ] | ||||
|     }, | ||||
|     "node_modules/@rollup/rollup-freebsd-x64": { | ||||
|       "version": "4.40.1", | ||||
|       "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.1.tgz", | ||||
|       "integrity": "sha512-VXeo/puqvCG8JBPNZXZf5Dqq7BzElNJzHRRw3vjBE27WujdzuOPecDPc/+1DcdcTptNBep3861jNq0mYkT8Z6Q==", | ||||
|       "cpu": [ | ||||
|         "x64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "freebsd" | ||||
|       ] | ||||
|     }, | ||||
|     "node_modules/@rollup/rollup-linux-arm-gnueabihf": { | ||||
|       "version": "4.40.1", | ||||
|       "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.1.tgz", | ||||
|       "integrity": "sha512-ehSKrewwsESPt1TgSE/na9nIhWCosfGSFqv7vwEtjyAqZcvbGIg4JAcV7ZEh2tfj/IlfBeZjgOXm35iOOjadcg==", | ||||
|       "cpu": [ | ||||
|         "arm" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "linux" | ||||
|       ] | ||||
|     }, | ||||
|     "node_modules/@rollup/rollup-linux-arm-musleabihf": { | ||||
|       "version": "4.40.1", | ||||
|       "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.1.tgz", | ||||
|       "integrity": "sha512-m39iO/aaurh5FVIu/F4/Zsl8xppd76S4qoID8E+dSRQvTyZTOI2gVk3T4oqzfq1PtcvOfAVlwLMK3KRQMaR8lg==", | ||||
|       "cpu": [ | ||||
|         "arm" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "linux" | ||||
|       ] | ||||
|     }, | ||||
|     "node_modules/@rollup/rollup-linux-arm64-gnu": { | ||||
|       "version": "4.40.1", | ||||
|       "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.1.tgz", | ||||
|       "integrity": "sha512-Y+GHnGaku4aVLSgrT0uWe2o2Rq8te9hi+MwqGF9r9ORgXhmHK5Q71N757u0F8yU1OIwUIFy6YiJtKjtyktk5hg==", | ||||
|       "cpu": [ | ||||
|         "arm64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "linux" | ||||
|       ] | ||||
|     }, | ||||
|     "node_modules/@rollup/rollup-linux-arm64-musl": { | ||||
|       "version": "4.40.1", | ||||
|       "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.1.tgz", | ||||
|       "integrity": "sha512-jEwjn3jCA+tQGswK3aEWcD09/7M5wGwc6+flhva7dsQNRZZTe30vkalgIzV4tjkopsTS9Jd7Y1Bsj6a4lzz8gQ==", | ||||
|       "cpu": [ | ||||
|         "arm64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "linux" | ||||
|       ] | ||||
|     }, | ||||
|     "node_modules/@rollup/rollup-linux-loongarch64-gnu": { | ||||
|       "version": "4.40.1", | ||||
|       "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.1.tgz", | ||||
|       "integrity": "sha512-ySyWikVhNzv+BV/IDCsrraOAZ3UaC8SZB67FZlqVwXwnFhPihOso9rPOxzZbjp81suB1O2Topw+6Ug3JNegejQ==", | ||||
|       "cpu": [ | ||||
|         "loong64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "linux" | ||||
|       ] | ||||
|     }, | ||||
|     "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { | ||||
|       "version": "4.40.1", | ||||
|       "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.1.tgz", | ||||
|       "integrity": "sha512-BvvA64QxZlh7WZWqDPPdt0GH4bznuL6uOO1pmgPnnv86rpUpc8ZxgZwcEgXvo02GRIZX1hQ0j0pAnhwkhwPqWg==", | ||||
|       "cpu": [ | ||||
|         "ppc64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "linux" | ||||
|       ] | ||||
|     }, | ||||
|     "node_modules/@rollup/rollup-linux-riscv64-gnu": { | ||||
|       "version": "4.40.1", | ||||
|       "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.1.tgz", | ||||
|       "integrity": "sha512-EQSP+8+1VuSulm9RKSMKitTav89fKbHymTf25n5+Yr6gAPZxYWpj3DzAsQqoaHAk9YX2lwEyAf9S4W8F4l3VBQ==", | ||||
|       "cpu": [ | ||||
|         "riscv64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "linux" | ||||
|       ] | ||||
|     }, | ||||
|     "node_modules/@rollup/rollup-linux-riscv64-musl": { | ||||
|       "version": "4.40.1", | ||||
|       "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.1.tgz", | ||||
|       "integrity": "sha512-n/vQ4xRZXKuIpqukkMXZt9RWdl+2zgGNx7Uda8NtmLJ06NL8jiHxUawbwC+hdSq1rrw/9CghCpEONor+l1e2gA==", | ||||
|       "cpu": [ | ||||
|         "riscv64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "linux" | ||||
|       ] | ||||
|     }, | ||||
|     "node_modules/@rollup/rollup-linux-s390x-gnu": { | ||||
|       "version": "4.40.1", | ||||
|       "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.1.tgz", | ||||
|       "integrity": "sha512-h8d28xzYb98fMQKUz0w2fMc1XuGzLLjdyxVIbhbil4ELfk5/orZlSTpF/xdI9C8K0I8lCkq+1En2RJsawZekkg==", | ||||
|       "cpu": [ | ||||
|         "s390x" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "linux" | ||||
|       ] | ||||
|     }, | ||||
|     "node_modules/@rollup/rollup-linux-x64-gnu": { | ||||
|       "version": "4.30.1", | ||||
|       "version": "4.40.1", | ||||
|       "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.1.tgz", | ||||
|       "integrity": "sha512-XiK5z70PEFEFqcNj3/zRSz/qX4bp4QIraTy9QjwJAb/Z8GM7kVUsD0Uk8maIPeTyPCP03ChdI+VVmJriKYbRHQ==", | ||||
|       "cpu": [ | ||||
|         "x64" | ||||
|       ], | ||||
| @@ -412,7 +1034,9 @@ | ||||
|       ] | ||||
|     }, | ||||
|     "node_modules/@rollup/rollup-linux-x64-musl": { | ||||
|       "version": "4.30.1", | ||||
|       "version": "4.40.1", | ||||
|       "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.1.tgz", | ||||
|       "integrity": "sha512-2BRORitq5rQ4Da9blVovzNCMaUlyKrzMSvkVR0D4qPuOy/+pMCrh1d7o01RATwVy+6Fa1WBw+da7QPeLWU/1mQ==", | ||||
|       "cpu": [ | ||||
|         "x64" | ||||
|       ], | ||||
| @@ -423,6 +1047,48 @@ | ||||
|         "linux" | ||||
|       ] | ||||
|     }, | ||||
|     "node_modules/@rollup/rollup-win32-arm64-msvc": { | ||||
|       "version": "4.40.1", | ||||
|       "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.1.tgz", | ||||
|       "integrity": "sha512-b2bcNm9Kbde03H+q+Jjw9tSfhYkzrDUf2d5MAd1bOJuVplXvFhWz7tRtWvD8/ORZi7qSCy0idW6tf2HgxSXQSg==", | ||||
|       "cpu": [ | ||||
|         "arm64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "win32" | ||||
|       ] | ||||
|     }, | ||||
|     "node_modules/@rollup/rollup-win32-ia32-msvc": { | ||||
|       "version": "4.40.1", | ||||
|       "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.1.tgz", | ||||
|       "integrity": "sha512-DfcogW8N7Zg7llVEfpqWMZcaErKfsj9VvmfSyRjCyo4BI3wPEfrzTtJkZG6gKP/Z92wFm6rz2aDO7/JfiR/whA==", | ||||
|       "cpu": [ | ||||
|         "ia32" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "win32" | ||||
|       ] | ||||
|     }, | ||||
|     "node_modules/@rollup/rollup-win32-x64-msvc": { | ||||
|       "version": "4.40.1", | ||||
|       "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.1.tgz", | ||||
|       "integrity": "sha512-ECyOuDeH3C1I8jH2MK1RtBJW+YPMvSfT0a5NN0nHfQYnDSJ6tUiZH3gzwVP5/Kfh/+Tt7tpWVF9LXNTnhTJ3kA==", | ||||
|       "cpu": [ | ||||
|         "x64" | ||||
|       ], | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "win32" | ||||
|       ] | ||||
|     }, | ||||
|     "node_modules/@swc/core": { | ||||
|       "version": "1.10.7", | ||||
|       "dev": true, | ||||
| @@ -539,7 +1205,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/estree": { | ||||
|       "version": "1.0.6", | ||||
|       "version": "1.0.7", | ||||
|       "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", | ||||
|       "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", | ||||
|       "license": "MIT" | ||||
|     }, | ||||
|     "node_modules/@types/estree-jsx": { | ||||
| @@ -1099,7 +1767,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/esbuild": { | ||||
|       "version": "0.24.2", | ||||
|       "version": "0.25.3", | ||||
|       "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz", | ||||
|       "integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==", | ||||
|       "dev": true, | ||||
|       "hasInstallScript": true, | ||||
|       "license": "MIT", | ||||
| @@ -1110,31 +1780,31 @@ | ||||
|         "node": ">=18" | ||||
|       }, | ||||
|       "optionalDependencies": { | ||||
|         "@esbuild/aix-ppc64": "0.24.2", | ||||
|         "@esbuild/android-arm": "0.24.2", | ||||
|         "@esbuild/android-arm64": "0.24.2", | ||||
|         "@esbuild/android-x64": "0.24.2", | ||||
|         "@esbuild/darwin-arm64": "0.24.2", | ||||
|         "@esbuild/darwin-x64": "0.24.2", | ||||
|         "@esbuild/freebsd-arm64": "0.24.2", | ||||
|         "@esbuild/freebsd-x64": "0.24.2", | ||||
|         "@esbuild/linux-arm": "0.24.2", | ||||
|         "@esbuild/linux-arm64": "0.24.2", | ||||
|         "@esbuild/linux-ia32": "0.24.2", | ||||
|         "@esbuild/linux-loong64": "0.24.2", | ||||
|         "@esbuild/linux-mips64el": "0.24.2", | ||||
|         "@esbuild/linux-ppc64": "0.24.2", | ||||
|         "@esbuild/linux-riscv64": "0.24.2", | ||||
|         "@esbuild/linux-s390x": "0.24.2", | ||||
|         "@esbuild/linux-x64": "0.24.2", | ||||
|         "@esbuild/netbsd-arm64": "0.24.2", | ||||
|         "@esbuild/netbsd-x64": "0.24.2", | ||||
|         "@esbuild/openbsd-arm64": "0.24.2", | ||||
|         "@esbuild/openbsd-x64": "0.24.2", | ||||
|         "@esbuild/sunos-x64": "0.24.2", | ||||
|         "@esbuild/win32-arm64": "0.24.2", | ||||
|         "@esbuild/win32-ia32": "0.24.2", | ||||
|         "@esbuild/win32-x64": "0.24.2" | ||||
|         "@esbuild/aix-ppc64": "0.25.3", | ||||
|         "@esbuild/android-arm": "0.25.3", | ||||
|         "@esbuild/android-arm64": "0.25.3", | ||||
|         "@esbuild/android-x64": "0.25.3", | ||||
|         "@esbuild/darwin-arm64": "0.25.3", | ||||
|         "@esbuild/darwin-x64": "0.25.3", | ||||
|         "@esbuild/freebsd-arm64": "0.25.3", | ||||
|         "@esbuild/freebsd-x64": "0.25.3", | ||||
|         "@esbuild/linux-arm": "0.25.3", | ||||
|         "@esbuild/linux-arm64": "0.25.3", | ||||
|         "@esbuild/linux-ia32": "0.25.3", | ||||
|         "@esbuild/linux-loong64": "0.25.3", | ||||
|         "@esbuild/linux-mips64el": "0.25.3", | ||||
|         "@esbuild/linux-ppc64": "0.25.3", | ||||
|         "@esbuild/linux-riscv64": "0.25.3", | ||||
|         "@esbuild/linux-s390x": "0.25.3", | ||||
|         "@esbuild/linux-x64": "0.25.3", | ||||
|         "@esbuild/netbsd-arm64": "0.25.3", | ||||
|         "@esbuild/netbsd-x64": "0.25.3", | ||||
|         "@esbuild/openbsd-arm64": "0.25.3", | ||||
|         "@esbuild/openbsd-x64": "0.25.3", | ||||
|         "@esbuild/sunos-x64": "0.25.3", | ||||
|         "@esbuild/win32-arm64": "0.25.3", | ||||
|         "@esbuild/win32-ia32": "0.25.3", | ||||
|         "@esbuild/win32-x64": "0.25.3" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/escape-string-regexp": { | ||||
| @@ -1458,6 +2128,21 @@ | ||||
|         "node": ">= 6" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/fsevents": { | ||||
|       "version": "2.3.3", | ||||
|       "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", | ||||
|       "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", | ||||
|       "dev": true, | ||||
|       "hasInstallScript": true, | ||||
|       "license": "MIT", | ||||
|       "optional": true, | ||||
|       "os": [ | ||||
|         "darwin" | ||||
|       ], | ||||
|       "engines": { | ||||
|         "node": "^8.16.0 || ^10.6.0 || >=11.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/function-bind": { | ||||
|       "version": "1.1.2", | ||||
|       "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", | ||||
| @@ -2788,7 +3473,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/postcss": { | ||||
|       "version": "8.5.1", | ||||
|       "version": "8.5.3", | ||||
|       "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", | ||||
|       "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", | ||||
|       "dev": true, | ||||
|       "funding": [ | ||||
|         { | ||||
| @@ -3256,11 +3943,13 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/rollup": { | ||||
|       "version": "4.30.1", | ||||
|       "version": "4.40.1", | ||||
|       "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.1.tgz", | ||||
|       "integrity": "sha512-C5VvvgCCyfyotVITIAv+4efVytl5F7wt+/I2i9q9GZcEXW9BP52YYOXC58igUi+LFZVHukErIIqQSWwv/M3WRw==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@types/estree": "1.0.6" | ||||
|         "@types/estree": "1.0.7" | ||||
|       }, | ||||
|       "bin": { | ||||
|         "rollup": "dist/bin/rollup" | ||||
| @@ -3270,25 +3959,26 @@ | ||||
|         "npm": ">=8.0.0" | ||||
|       }, | ||||
|       "optionalDependencies": { | ||||
|         "@rollup/rollup-android-arm-eabi": "4.30.1", | ||||
|         "@rollup/rollup-android-arm64": "4.30.1", | ||||
|         "@rollup/rollup-darwin-arm64": "4.30.1", | ||||
|         "@rollup/rollup-darwin-x64": "4.30.1", | ||||
|         "@rollup/rollup-freebsd-arm64": "4.30.1", | ||||
|         "@rollup/rollup-freebsd-x64": "4.30.1", | ||||
|         "@rollup/rollup-linux-arm-gnueabihf": "4.30.1", | ||||
|         "@rollup/rollup-linux-arm-musleabihf": "4.30.1", | ||||
|         "@rollup/rollup-linux-arm64-gnu": "4.30.1", | ||||
|         "@rollup/rollup-linux-arm64-musl": "4.30.1", | ||||
|         "@rollup/rollup-linux-loongarch64-gnu": "4.30.1", | ||||
|         "@rollup/rollup-linux-powerpc64le-gnu": "4.30.1", | ||||
|         "@rollup/rollup-linux-riscv64-gnu": "4.30.1", | ||||
|         "@rollup/rollup-linux-s390x-gnu": "4.30.1", | ||||
|         "@rollup/rollup-linux-x64-gnu": "4.30.1", | ||||
|         "@rollup/rollup-linux-x64-musl": "4.30.1", | ||||
|         "@rollup/rollup-win32-arm64-msvc": "4.30.1", | ||||
|         "@rollup/rollup-win32-ia32-msvc": "4.30.1", | ||||
|         "@rollup/rollup-win32-x64-msvc": "4.30.1", | ||||
|         "@rollup/rollup-android-arm-eabi": "4.40.1", | ||||
|         "@rollup/rollup-android-arm64": "4.40.1", | ||||
|         "@rollup/rollup-darwin-arm64": "4.40.1", | ||||
|         "@rollup/rollup-darwin-x64": "4.40.1", | ||||
|         "@rollup/rollup-freebsd-arm64": "4.40.1", | ||||
|         "@rollup/rollup-freebsd-x64": "4.40.1", | ||||
|         "@rollup/rollup-linux-arm-gnueabihf": "4.40.1", | ||||
|         "@rollup/rollup-linux-arm-musleabihf": "4.40.1", | ||||
|         "@rollup/rollup-linux-arm64-gnu": "4.40.1", | ||||
|         "@rollup/rollup-linux-arm64-musl": "4.40.1", | ||||
|         "@rollup/rollup-linux-loongarch64-gnu": "4.40.1", | ||||
|         "@rollup/rollup-linux-powerpc64le-gnu": "4.40.1", | ||||
|         "@rollup/rollup-linux-riscv64-gnu": "4.40.1", | ||||
|         "@rollup/rollup-linux-riscv64-musl": "4.40.1", | ||||
|         "@rollup/rollup-linux-s390x-gnu": "4.40.1", | ||||
|         "@rollup/rollup-linux-x64-gnu": "4.40.1", | ||||
|         "@rollup/rollup-linux-x64-musl": "4.40.1", | ||||
|         "@rollup/rollup-win32-arm64-msvc": "4.40.1", | ||||
|         "@rollup/rollup-win32-ia32-msvc": "4.40.1", | ||||
|         "@rollup/rollup-win32-x64-msvc": "4.40.1", | ||||
|         "fsevents": "~2.3.2" | ||||
|       } | ||||
|     }, | ||||
| @@ -3451,6 +4141,51 @@ | ||||
|       "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", | ||||
|       "license": "MIT" | ||||
|     }, | ||||
|     "node_modules/tinyglobby": { | ||||
|       "version": "0.2.13", | ||||
|       "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", | ||||
|       "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "fdir": "^6.4.4", | ||||
|         "picomatch": "^4.0.2" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=12.0.0" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sponsors/SuperchupuDev" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/tinyglobby/node_modules/fdir": { | ||||
|       "version": "6.4.4", | ||||
|       "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", | ||||
|       "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "peerDependencies": { | ||||
|         "picomatch": "^3 || ^4" | ||||
|       }, | ||||
|       "peerDependenciesMeta": { | ||||
|         "picomatch": { | ||||
|           "optional": true | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/tinyglobby/node_modules/picomatch": { | ||||
|       "version": "4.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", | ||||
|       "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "engines": { | ||||
|         "node": ">=12" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sponsors/jonschlinkert" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/to-regex-range": { | ||||
|       "version": "5.0.1", | ||||
|       "dev": true, | ||||
| @@ -3988,13 +4723,18 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/vite": { | ||||
|       "version": "6.0.7", | ||||
|       "version": "6.3.4", | ||||
|       "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.4.tgz", | ||||
|       "integrity": "sha512-BiReIiMS2fyFqbqNT/Qqt4CVITDU9M9vE+DKcVAsB+ZV0wvTKd+3hMbkpxz1b+NmEDMegpVbisKiAZOnvO92Sw==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "esbuild": "^0.24.2", | ||||
|         "postcss": "^8.4.49", | ||||
|         "rollup": "^4.23.0" | ||||
|         "esbuild": "^0.25.0", | ||||
|         "fdir": "^6.4.4", | ||||
|         "picomatch": "^4.0.2", | ||||
|         "postcss": "^8.5.3", | ||||
|         "rollup": "^4.34.9", | ||||
|         "tinyglobby": "^0.2.13" | ||||
|       }, | ||||
|       "bin": { | ||||
|         "vite": "bin/vite.js" | ||||
| @@ -4057,6 +4797,34 @@ | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/vite/node_modules/fdir": { | ||||
|       "version": "6.4.4", | ||||
|       "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", | ||||
|       "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "peerDependencies": { | ||||
|         "picomatch": "^3 || ^4" | ||||
|       }, | ||||
|       "peerDependenciesMeta": { | ||||
|         "picomatch": { | ||||
|           "optional": true | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/vite/node_modules/picomatch": { | ||||
|       "version": "4.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", | ||||
|       "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "engines": { | ||||
|         "node": ">=12" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sponsors/jonschlinkert" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/void-elements": { | ||||
|       "version": "3.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", | ||||
|   | ||||
| @@ -43,6 +43,6 @@ | ||||
|     "prettier": "3.5.3", | ||||
|     "typescript": "~5.8.3", | ||||
|     "typescript-eslint": "^8.18.2", | ||||
|     "vite": "^6.0.5" | ||||
|     "vite": "^6.3.4" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										26
									
								
								frontend/src/lib/hooks/use-is-mounted.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								frontend/src/lib/hooks/use-is-mounted.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| import { useCallback, useEffect, useRef } from 'react' | ||||
|  | ||||
| /** | ||||
|  * Custom hook that determines if the component is currently mounted. | ||||
|  * @returns {() => boolean} A function that returns a boolean value indicating whether the component is mounted. | ||||
|  * @public | ||||
|  * @see [Documentation](https://usehooks-ts.com/react-hook/use-is-mounted) | ||||
|  * @example | ||||
|  * ```tsx | ||||
|  * const isComponentMounted = useIsMounted(); | ||||
|  * // Use isComponentMounted() to check if the component is currently mounted before performing certain actions. | ||||
|  * ``` | ||||
|  */ | ||||
| export function useIsMounted(): () => boolean { | ||||
|   const isMounted = useRef(false) | ||||
|  | ||||
|   useEffect(() => { | ||||
|     isMounted.current = true | ||||
|  | ||||
|     return () => { | ||||
|       isMounted.current = false | ||||
|     } | ||||
|   }, []) | ||||
|  | ||||
|   return useCallback(() => isMounted.current, []) | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Enter your TOTP code", | ||||
|     "unauthorizedTitle": "Unauthorized", | ||||
|     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Try again", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "cancelTitle": "Cancel", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "أدخل رمز TOTP الخاص بك", | ||||
|     "unauthorizedTitle": "غير مرخص", | ||||
|     "unauthorizedResourceSubtitle": "المستخدم الذي يحمل اسم المستخدم <Code>{{username}}</Code> غير مصرح له بالوصول إلى المورد <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "المستخدم الذي يحمل اسم المستخدم <Code>{{username}}</Code> غير مصرح له بتسجيل الدخول.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "حاول مجددا", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "إلغاء" | ||||
|     "cancelTitle": "إلغاء", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Enter your TOTP code", | ||||
|     "unauthorizedTitle": "Unauthorized", | ||||
|     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Try again", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "cancelTitle": "Cancel", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Enter your TOTP code", | ||||
|     "unauthorizedTitle": "Unauthorized", | ||||
|     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Try again", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "cancelTitle": "Cancel", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Enter your TOTP code", | ||||
|     "unauthorizedTitle": "Unauthorized", | ||||
|     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Try again", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "cancelTitle": "Cancel", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Geben Sie Ihren TOTP Code ein", | ||||
|     "unauthorizedTitle": "Unautorisiert", | ||||
|     "unauthorizedResourceSubtitle": "Der Benutzer mit Benutzername <Code>{{username}}</Code> ist nicht berechtigt auf die Ressource <Code>{{resource}}</Code> zuzugreifen.", | ||||
|     "unaothorizedLoginSubtitle": "Der Benutzer mit dem Benutzernamen <Code>{{username}}</Code> ist nicht berechtigt, sich einzuloggen.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Erneut versuchen", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "untrustedRedirectTitle": "Nicht vertrauenswürdige Weiterleitung", | ||||
|     "untrustedRedirectSubtitle": "Sie versuchen auf eine Domain umzuleiten, die nicht mit Ihrer konfigurierten Domain übereinstimmt (<Code>{{domain}}</Code>). Sind Sie sicher, dass Sie fortfahren möchten?", | ||||
|     "cancelTitle": "Abbrechen", | ||||
|     "forgotPasswordTitle": "Passwort vergessen?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Εισάγετε τον κωδικό TOTP", | ||||
|     "unauthorizedTitle": "Μη εξουσιοδοτημένο", | ||||
|     "unauthorizedResourceSubtitle": "Ο χρήστης με όνομα χρήστη <Code>{{username}}</Code> δεν έχει άδεια πρόσβασης στον πόρο <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "Ο χρήστης με όνομα χρήστη <Code>{{username}}</Code> δεν είναι εξουσιοδοτημένος να συνδεθεί.", | ||||
|     "unauthorizedLoginSubtitle": "Ο χρήστης με όνομα χρήστη <Code>{{username}}</Code> δεν είναι εξουσιοδοτημένος να συνδεθεί.", | ||||
|     "unauthorizedGroupsSubtitle": "Ο χρήστης με όνομα χρήστη <Code>{{username}}</Code> δεν είναι στις ομάδες που απαιτούνται από τον πόρο <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Προσπαθήστε ξανά", | ||||
|     "untrustedRedirectTitle": "Μη έμπιστη ανακατεύθυνση", | ||||
|     "untrustedRedirectSubtitle": "Προσπαθείτε να ανακατευθύνετε σε έναν τομέα που δεν ταιριάζει με τον ρυθμισμένο τομέα σας (<Code>{{domain}}</Code>). Είστε βέβαιοι ότι θέλετε να συνεχίσετε;", | ||||
|     "cancelTitle": "Ακύρωση" | ||||
|     "cancelTitle": "Ακύρωση", | ||||
|     "forgotPasswordTitle": "Ξεχάσατε το συνθηματικό σας;" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Enter your TOTP code", | ||||
|     "unauthorizedTitle": "Unauthorized", | ||||
|     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Try again", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "cancelTitle": "Cancel", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Enter your TOTP code", | ||||
|     "unauthorizedTitle": "Unauthorized", | ||||
|     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Try again", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "cancelTitle": "Cancel", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Saisissez votre code TOTP", | ||||
|     "unauthorizedTitle": "Non autorisé", | ||||
|     "unauthorizedResourceSubtitle": "L'utilisateur avec le nom d'utilisateur <Code>{{username}}</Code> n'est pas autorisé à accéder à la ressource <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "L'utilisateur avec le nom d'utilisateur <Code>{{username}}</Code> n'est pas autorisé à se connecter.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Réessayer", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "cancelTitle": "Cancel", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Enter your TOTP code", | ||||
|     "unauthorizedTitle": "Unauthorized", | ||||
|     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Try again", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "cancelTitle": "Cancel", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Enter your TOTP code", | ||||
|     "unauthorizedTitle": "Unauthorized", | ||||
|     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Try again", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "cancelTitle": "Cancel", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Enter your TOTP code", | ||||
|     "unauthorizedTitle": "Unauthorized", | ||||
|     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Try again", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "cancelTitle": "Cancel", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Enter your TOTP code", | ||||
|     "unauthorizedTitle": "Unauthorized", | ||||
|     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Try again", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "cancelTitle": "Cancel", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Enter your TOTP code", | ||||
|     "unauthorizedTitle": "Unauthorized", | ||||
|     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Try again", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "cancelTitle": "Cancel", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Voer je TOTP-code in", | ||||
|     "unauthorizedTitle": "Ongeautoriseerd", | ||||
|     "unauthorizedResourceSubtitle": "De gebruiker met gebruikersnaam <Code>{{username}}</Code> heeft geen toegang tot de bron <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "De gebruiker met gebruikersnaam <Code>{{username}}</Code> is niet gemachtigd om in te loggen.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Opnieuw proberen", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "cancelTitle": "Cancel", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Enter your TOTP code", | ||||
|     "unauthorizedTitle": "Unauthorized", | ||||
|     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Try again", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "cancelTitle": "Cancel", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Wprowadź kod TOTP", | ||||
|     "unauthorizedTitle": "Nieautoryzowany", | ||||
|     "unauthorizedResourceSubtitle": "Użytkownik o nazwie <Code>{{username}}</Code> nie jest upoważniony do uzyskania dostępu do zasobu <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "Użytkownik o nazwie <Code>{{username}}</Code> nie jest upoważniony do logowania.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Spróbuj ponownie", | ||||
|     "untrustedRedirectTitle": "Niezaufane przekierowanie", | ||||
|     "untrustedRedirectSubtitle": "Próbujesz przekierować do domeny, która nie pasuje do skonfigurowanej przez Ciebie domeny (<Code>{{domain}}</Code>). Czy na pewno chcesz kontynuować?", | ||||
|     "cancelTitle": "Anuluj" | ||||
|     "cancelTitle": "Anuluj", | ||||
|     "forgotPasswordTitle": "Nie pamiętasz hasła?" | ||||
| } | ||||
| @@ -1,5 +1,5 @@ | ||||
| { | ||||
|     "loginTitle": "Bem-vindo de volta, faça o login com", | ||||
|     "loginTitle": "Bem-vindo de volta, acesse com", | ||||
|     "loginDivider": "Ou continuar com uma senha", | ||||
|     "loginUsername": "Nome de usuário", | ||||
|     "loginPassword": "Senha", | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Insira o seu código TOTP", | ||||
|     "unauthorizedTitle": "Não autorizado", | ||||
|     "unauthorizedResourceSubtitle": "O usuário com nome de usuário <Code>{{username}}</Code> não está autorizado a acessar o recurso <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "O usuário com o nome <Code>{{username}}</Code> não está autorizado a acessar.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Tentar novamente", | ||||
|     "untrustedRedirectTitle": "Redirecionamento não confiável", | ||||
|     "untrustedRedirectSubtitle": "Você está tentando redirecionar para um domínio que não corresponde ao seu domínio configurado (<Code>{{domain}}</Code>). Tem certeza que deseja continuar?", | ||||
|     "cancelTitle": "Cancelar" | ||||
|     "cancelTitle": "Cancelar", | ||||
|     "forgotPasswordTitle": "Esqueceu sua senha?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Enter your TOTP code", | ||||
|     "unauthorizedTitle": "Unauthorized", | ||||
|     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Try again", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "cancelTitle": "Cancel", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Enter your TOTP code", | ||||
|     "unauthorizedTitle": "Unauthorized", | ||||
|     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Try again", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "cancelTitle": "Cancel", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Enter your TOTP code", | ||||
|     "unauthorizedTitle": "Unauthorized", | ||||
|     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Try again", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "cancelTitle": "Cancel", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Enter your TOTP code", | ||||
|     "unauthorizedTitle": "Unauthorized", | ||||
|     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Try again", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "cancelTitle": "Cancel", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Enter your TOTP code", | ||||
|     "unauthorizedTitle": "Unauthorized", | ||||
|     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Try again", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "cancelTitle": "Cancel", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Enter your TOTP code", | ||||
|     "unauthorizedTitle": "Unauthorized", | ||||
|     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Try again", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "İptal" | ||||
|     "cancelTitle": "İptal", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Enter your TOTP code", | ||||
|     "unauthorizedTitle": "Unauthorized", | ||||
|     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Try again", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "cancelTitle": "Cancel", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Enter your TOTP code", | ||||
|     "unauthorizedTitle": "Unauthorized", | ||||
|     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Try again", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "cancelTitle": "Cancel", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "输入您的 TOTP 代码", | ||||
|     "unauthorizedTitle": "未授权", | ||||
|     "unauthorizedResourceSubtitle": "用户 <Code>{{username}}</Code> 无权访问资源 <Code>{{resource}}</Code>。", | ||||
|     "unaothorizedLoginSubtitle": "用户名 <Code>{{username}}</Code> 无登录权限。", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "重试", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "cancelTitle": "Cancel", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -41,9 +41,11 @@ | ||||
|     "totpTitle": "Enter your TOTP code", | ||||
|     "unauthorizedTitle": "Unauthorized", | ||||
|     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", | ||||
|     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||
|     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||
|     "unauthorizedButton": "Try again", | ||||
|     "untrustedRedirectTitle": "Untrusted redirect", | ||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||
|     "cancelTitle": "Cancel" | ||||
|     "cancelTitle": "Cancel", | ||||
|     "forgotPasswordTitle": "Forgot your password?" | ||||
| } | ||||
| @@ -4,7 +4,7 @@ import { Navigate } from "react-router"; | ||||
| import { useUserContext } from "../context/user-context"; | ||||
| import { Layout } from "../components/layouts/layout"; | ||||
| import { ReactNode } from "react"; | ||||
| import { escapeRegex, isQueryValid } from "../utils/utils"; | ||||
| import { escapeRegex, isValidRedirectUri } from "../utils/utils"; | ||||
| import { useAppContext } from "../context/app-context"; | ||||
| import { Trans, useTranslation } from "react-i18next"; | ||||
|  | ||||
| @@ -21,7 +21,7 @@ export const ContinuePage = () => { | ||||
|     return <Navigate to={`/login?redirect_uri=${redirectUri}`} />; | ||||
|   } | ||||
|  | ||||
|   if (!isQueryValid(redirectUri)) { | ||||
|   if (!isValidRedirectUri(redirectUri)) { | ||||
|     return <Navigate to="/" />; | ||||
|   } | ||||
|  | ||||
| @@ -51,7 +51,7 @@ export const ContinuePage = () => { | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   const regex = new RegExp(`^.*${escapeRegex(domain)}$`) | ||||
|   const regex = new RegExp(`^.*${escapeRegex(domain)}$`); | ||||
|  | ||||
|   if (!regex.test(uri.hostname)) { | ||||
|     return ( | ||||
| @@ -60,19 +60,24 @@ export const ContinuePage = () => { | ||||
|           {t("untrustedRedirectTitle")} | ||||
|         </Text> | ||||
|         <Trans | ||||
|             i18nKey="untrustedRedirectSubtitle" | ||||
|             t={t} | ||||
|             components={{ Code: <Code /> }} | ||||
|             values={{ domain: domain }} | ||||
|           /> | ||||
|           i18nKey="untrustedRedirectSubtitle" | ||||
|           t={t} | ||||
|           components={{ Code: <Code /> }} | ||||
|           values={{ domain: domain }} | ||||
|         /> | ||||
|         <Button fullWidth mt="xl" color="red" onClick={redirect}> | ||||
|           {t('continueTitle')} | ||||
|           {t("continueTitle")} | ||||
|         </Button> | ||||
|         <Button fullWidth mt="sm" color="gray" onClick={() => window.location.href = "/"}> | ||||
|           {t('cancelTitle')} | ||||
|         <Button | ||||
|           fullWidth | ||||
|           mt="xs" | ||||
|           color="gray" | ||||
|           onClick={() => (window.location.href = "/")} | ||||
|         > | ||||
|           {t("cancelTitle")} | ||||
|         </Button> | ||||
|       </ContinuePageLayout> | ||||
|     ) | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   if (disableContinue) { | ||||
| @@ -103,8 +108,13 @@ export const ContinuePage = () => { | ||||
|         <Button fullWidth mt="xl" color="yellow" onClick={redirect}> | ||||
|           {t("continueTitle")} | ||||
|         </Button> | ||||
|         <Button fullWidth mt="sm" color="gray" onClick={() => window.location.href = "/"}> | ||||
|           {t('cancelTitle')} | ||||
|         <Button | ||||
|           fullWidth | ||||
|           mt="xs" | ||||
|           color="gray" | ||||
|           onClick={() => (window.location.href = "/")} | ||||
|         > | ||||
|           {t("cancelTitle")} | ||||
|         </Button> | ||||
|       </ContinuePageLayout> | ||||
|     ); | ||||
|   | ||||
| @@ -8,9 +8,11 @@ import { Layout } from "../components/layouts/layout"; | ||||
| import { OAuthButtons } from "../components/auth/oauth-buttons"; | ||||
| import { LoginFormValues } from "../schemas/login-schema"; | ||||
| import { LoginForm } from "../components/auth/login-forn"; | ||||
| import { isQueryValid } from "../utils/utils"; | ||||
| import { useAppContext } from "../context/app-context"; | ||||
| import { useTranslation } from "react-i18next"; | ||||
| import { useEffect, useState } from "react"; | ||||
| import { useIsMounted } from "../lib/hooks/use-is-mounted"; | ||||
| import { isValidRedirectUri } from "../utils/utils"; | ||||
|  | ||||
| export const LoginPage = () => { | ||||
|   const queryString = window.location.search; | ||||
| @@ -18,16 +20,29 @@ export const LoginPage = () => { | ||||
|   const redirectUri = params.get("redirect_uri") ?? ""; | ||||
|  | ||||
|   const { isLoggedIn } = useUserContext(); | ||||
|   const { configuredProviders, title, genericName } = useAppContext(); | ||||
|  | ||||
|   if (isLoggedIn) { | ||||
|     return <Navigate to="/logout" />; | ||||
|   } | ||||
|  | ||||
|   const { | ||||
|     configuredProviders, | ||||
|     title, | ||||
|     genericName, | ||||
|     oauthAutoRedirect: oauthAutoRedirectContext, | ||||
|   } = useAppContext(); | ||||
|  | ||||
|   const { t } = useTranslation(); | ||||
|  | ||||
|   const [oauthAutoRedirect, setOAuthAutoRedirect] = useState( | ||||
|     oauthAutoRedirectContext, | ||||
|   ); | ||||
|  | ||||
|   const oauthProviders = configuredProviders.filter( | ||||
|     (value) => value !== "username", | ||||
|   ); | ||||
|  | ||||
|   if (isLoggedIn) { | ||||
|     return <Navigate to="/logout" />; | ||||
|   } | ||||
|   const isMounted = useIsMounted(); | ||||
|  | ||||
|   const loginMutation = useMutation({ | ||||
|     mutationFn: (login: LoginFormValues) => { | ||||
| @@ -63,7 +78,7 @@ export const LoginPage = () => { | ||||
|       }); | ||||
|  | ||||
|       setTimeout(() => { | ||||
|         if (!isQueryValid(redirectUri)) { | ||||
|         if (!isValidRedirectUri(redirectUri)) { | ||||
|           window.location.replace("/"); | ||||
|           return; | ||||
|         } | ||||
| @@ -85,6 +100,7 @@ export const LoginPage = () => { | ||||
|         message: t("loginOauthFailSubtitle"), | ||||
|         color: "red", | ||||
|       }); | ||||
|       setOAuthAutoRedirect("none"); | ||||
|     }, | ||||
|     onSuccess: (data) => { | ||||
|       notifications.show({ | ||||
| @@ -102,6 +118,33 @@ export const LoginPage = () => { | ||||
|     loginMutation.mutate(values); | ||||
|   }; | ||||
|  | ||||
|   useEffect(() => { | ||||
|     if (isMounted()) { | ||||
|       if ( | ||||
|         oauthProviders.includes(oauthAutoRedirect) && | ||||
|         isValidRedirectUri(redirectUri) | ||||
|       ) { | ||||
|         loginOAuthMutation.mutate(oauthAutoRedirect); | ||||
|       } | ||||
|     } | ||||
|   }, []); | ||||
|  | ||||
|   if ( | ||||
|     oauthProviders.includes(oauthAutoRedirect) && | ||||
|     isValidRedirectUri(redirectUri) | ||||
|   ) { | ||||
|     return ( | ||||
|       <Layout> | ||||
|         <Paper shadow="md" p="xl" mt={30} radius="md" withBorder> | ||||
|           <Text size="xl" fw={700}> | ||||
|             {t("continueRedirectingTitle")} | ||||
|           </Text> | ||||
|           <Text>{t("loginOauthSuccessSubtitle")}</Text> | ||||
|         </Paper> | ||||
|       </Layout> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <Layout> | ||||
|       <Title ta="center">{title}</Title> | ||||
|   | ||||
| @@ -1,9 +1,10 @@ | ||||
| import { Button, Code, Paper, Text } from "@mantine/core"; | ||||
| import { Layout } from "../components/layouts/layout"; | ||||
| import { Navigate } from "react-router"; | ||||
| import { isQueryValid } from "../utils/utils"; | ||||
| import { Trans, useTranslation } from "react-i18next"; | ||||
| import React from "react"; | ||||
| import React, { useEffect } from "react"; | ||||
| import { isValidQuery } from "../utils/utils"; | ||||
| import { useIsMounted } from "../lib/hooks/use-is-mounted"; | ||||
|  | ||||
| export const UnauthorizedPage = () => { | ||||
|   const queryString = window.location.search; | ||||
| @@ -12,13 +13,31 @@ export const UnauthorizedPage = () => { | ||||
|   const groupErr = params.get("groupErr") ?? ""; | ||||
|   const resource = params.get("resource") ?? ""; | ||||
|  | ||||
|   const [isGroupErr, setIsGroupErr] = React.useState(false); | ||||
|  | ||||
|   const useMounted = useIsMounted(); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     if (useMounted()) { | ||||
|       if (isValidQuery(groupErr)) { | ||||
|         if (groupErr === "true") { | ||||
|           setIsGroupErr(true); | ||||
|           return; | ||||
|         } | ||||
|         setIsGroupErr(false); | ||||
|         return; | ||||
|       } | ||||
|       setIsGroupErr(false); | ||||
|     } | ||||
|   }, []); | ||||
|  | ||||
|   const { t } = useTranslation(); | ||||
|  | ||||
|   if (!isQueryValid(username)) { | ||||
|   if (!isValidQuery(username)) { | ||||
|     return <Navigate to="/" />; | ||||
|   } | ||||
|  | ||||
|   if (isQueryValid(resource) && !isQueryValid(groupErr)) { | ||||
|   if (isValidQuery(resource) && !isGroupErr) { | ||||
|     return ( | ||||
|       <UnauthorizedLayout> | ||||
|         <Trans | ||||
| @@ -31,7 +50,7 @@ export const UnauthorizedPage = () => { | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   if (isQueryValid(groupErr) && isQueryValid(resource)) { | ||||
|   if (isGroupErr && isValidQuery(resource)) { | ||||
|     return ( | ||||
|       <UnauthorizedLayout> | ||||
|         <Trans | ||||
|   | ||||
| @@ -7,6 +7,7 @@ export const appContextSchema = z.object({ | ||||
|   genericName: z.string(), | ||||
|   domain: z.string(), | ||||
|   forgotPasswordMessage: z.string(), | ||||
|   oauthAutoRedirect: z.enum(["none", "github", "google", "generic"]), | ||||
| }); | ||||
|  | ||||
| export type AppContextSchemaType = z.infer<typeof appContextSchema>; | ||||
|   | ||||
| @@ -1,3 +1,17 @@ | ||||
| export const capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1); | ||||
| export const isQueryValid = (value: string) => value.trim() !== "" && value !== "null"; | ||||
| export const escapeRegex = (value: string) => value.replace(/[-\/\\^$.*+?()[\]{}|]/g, "\\$&"); | ||||
| export const escapeRegex = (value: string) => value.replace(/[-\/\\^$.*+?()[\]{}|]/g, "\\$&"); | ||||
| export const isValidQuery = (query: string) => query && query.trim() !== ""; | ||||
|  | ||||
| export const isValidRedirectUri = (value: string) => { | ||||
|     if (!isValidQuery(value)) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     try { | ||||
|         new URL(value); | ||||
|     } catch { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
| } | ||||
| @@ -75,6 +75,8 @@ func (h *Handlers) AuthHandler(c *gin.Context) { | ||||
| 	// Get the container labels | ||||
| 	labels, err := h.Docker.GetLabels(appId) | ||||
|  | ||||
| 	log.Debug().Interface("labels", labels).Msg("Got labels") | ||||
|  | ||||
| 	// Check if there was an error | ||||
| 	if err != nil { | ||||
| 		log.Error().Err(err).Msg("Failed to get container labels") | ||||
| @@ -126,6 +128,12 @@ func (h *Handlers) AuthHandler(c *gin.Context) { | ||||
| 	// Get user context | ||||
| 	userContext := h.Hooks.UseUserContext(c) | ||||
|  | ||||
| 	// If we are using basic auth, we need to check if the user has totp and if it does then disable basic auth | ||||
| 	if userContext.Provider == "basic" && userContext.TotpEnabled { | ||||
| 		log.Warn().Str("username", userContext.Username).Msg("User has totp enabled, disabling basic auth") | ||||
| 		userContext.IsLoggedIn = false | ||||
| 	} | ||||
|  | ||||
| 	// Check if user is logged in | ||||
| 	if userContext.IsLoggedIn { | ||||
| 		log.Debug().Msg("Authenticated") | ||||
| @@ -177,54 +185,55 @@ func (h *Handlers) AuthHandler(c *gin.Context) { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		log.Debug().Interface("labels", labels).Msg("Got labels") | ||||
| 		// Check groups if using OAuth | ||||
| 		if userContext.OAuth { | ||||
| 			// Check if user is in required groups | ||||
| 			groupOk := h.Auth.OAuthGroup(c, userContext, labels) | ||||
|  | ||||
| 		// Check if user is in required groups | ||||
| 		groupOk := h.Auth.OAuthGroup(c, userContext, labels) | ||||
| 			log.Debug().Bool("groupOk", groupOk).Msg("Checking if user is in required groups") | ||||
|  | ||||
| 		log.Debug().Bool("groupOk", groupOk).Msg("Checking if user is in required groups") | ||||
| 			// The user is not allowed to access the app | ||||
| 			if !groupOk { | ||||
| 				log.Warn().Str("username", userContext.Username).Str("host", host).Msg("User is not in required groups") | ||||
|  | ||||
| 		// The user is not allowed to access the app | ||||
| 		if !groupOk { | ||||
| 			log.Warn().Str("username", userContext.Username).Str("host", host).Msg("User is not in required groups") | ||||
| 				// Set WWW-Authenticate header | ||||
| 				c.Header("WWW-Authenticate", "Basic realm=\"tinyauth\"") | ||||
|  | ||||
| 			// Set WWW-Authenticate header | ||||
| 			c.Header("WWW-Authenticate", "Basic realm=\"tinyauth\"") | ||||
| 				if proxy.Proxy == "nginx" || !isBrowser { | ||||
| 					c.JSON(401, gin.H{ | ||||
| 						"status":  401, | ||||
| 						"message": "Unauthorized", | ||||
| 					}) | ||||
| 					return | ||||
| 				} | ||||
|  | ||||
| 			if proxy.Proxy == "nginx" || !isBrowser { | ||||
| 				c.JSON(401, gin.H{ | ||||
| 					"status":  401, | ||||
| 					"message": "Unauthorized", | ||||
| 				}) | ||||
| 				// Values | ||||
| 				values := types.UnauthorizedQuery{ | ||||
| 					Resource: strings.Split(host, ".")[0], | ||||
| 					GroupErr: true, | ||||
| 				} | ||||
|  | ||||
| 				// Use either username or email | ||||
| 				if userContext.OAuth { | ||||
| 					values.Username = userContext.Email | ||||
| 				} else { | ||||
| 					values.Username = userContext.Username | ||||
| 				} | ||||
|  | ||||
| 				// Build query | ||||
| 				queries, err := query.Values(values) | ||||
|  | ||||
| 				// Handle error (no need to check for nginx/headers since we are sure we are using caddy/traefik) | ||||
| 				if err != nil { | ||||
| 					log.Error().Err(err).Msg("Failed to build queries") | ||||
| 					c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/error", h.Config.AppURL)) | ||||
| 					return | ||||
| 				} | ||||
|  | ||||
| 				// We are using caddy/traefik so redirect | ||||
| 				c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/unauthorized?%s", h.Config.AppURL, queries.Encode())) | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			// Values | ||||
| 			values := types.UnauthorizedQuery{ | ||||
| 				Resource: strings.Split(host, ".")[0], | ||||
| 				GroupErr: true, | ||||
| 			} | ||||
|  | ||||
| 			// Use either username or email | ||||
| 			if userContext.OAuth { | ||||
| 				values.Username = userContext.Email | ||||
| 			} else { | ||||
| 				values.Username = userContext.Username | ||||
| 			} | ||||
|  | ||||
| 			// Build query | ||||
| 			queries, err := query.Values(values) | ||||
|  | ||||
| 			// Handle error (no need to check for nginx/headers since we are sure we are using caddy/traefik) | ||||
| 			if err != nil { | ||||
| 				log.Error().Err(err).Msg("Failed to build queries") | ||||
| 				c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/error", h.Config.AppURL)) | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			// We are using caddy/traefik so redirect | ||||
| 			c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/unauthorized?%s", h.Config.AppURL, queries.Encode())) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		c.Header("Remote-User", utils.SanitizeHeader(userContext.Username)) | ||||
| @@ -500,6 +509,7 @@ func (h *Handlers) AppHandler(c *gin.Context) { | ||||
| 		GenericName:           h.Config.GenericName, | ||||
| 		Domain:                h.Config.Domain, | ||||
| 		ForgotPasswordMessage: h.Config.ForgotPasswordMessage, | ||||
| 		OAuthAutoRedirect:     h.Config.OAuthAutoRedirect, | ||||
| 	} | ||||
|  | ||||
| 	// Return app context | ||||
|   | ||||
| @@ -35,17 +35,27 @@ func (hooks *Hooks) UseUserContext(c *gin.Context) types.UserContext { | ||||
| 	if basic != nil { | ||||
| 		log.Debug().Msg("Got basic auth") | ||||
|  | ||||
| 		// Check if user exists and password is correct | ||||
| 		// Get user | ||||
| 		user := hooks.Auth.GetUser(basic.Username) | ||||
|  | ||||
| 		if user != nil && hooks.Auth.CheckPassword(*user, basic.Password) { | ||||
| 		// Check we have a user | ||||
| 		if user == nil { | ||||
| 			log.Error().Str("username", basic.Username).Msg("User does not exist") | ||||
|  | ||||
| 			// Return empty context | ||||
| 			return types.UserContext{} | ||||
| 		} | ||||
|  | ||||
| 		// Check if the user has a correct password | ||||
| 		if hooks.Auth.CheckPassword(*user, basic.Password) { | ||||
| 			// Return user context since we are logged in with basic auth | ||||
| 			return types.UserContext{ | ||||
| 				Username:   basic.Username, | ||||
| 				Name:       utils.Capitalize(basic.Username), | ||||
| 				Email:      fmt.Sprintf("%s@%s", strings.ToLower(basic.Username), hooks.Config.Domain), | ||||
| 				IsLoggedIn: true, | ||||
| 				Provider:   "basic", | ||||
| 				Username:    basic.Username, | ||||
| 				Name:        utils.Capitalize(basic.Username), | ||||
| 				Email:       fmt.Sprintf("%s@%s", strings.ToLower(basic.Username), hooks.Config.Domain), | ||||
| 				IsLoggedIn:  true, | ||||
| 				Provider:    "basic", | ||||
| 				TotpEnabled: user.TotpSecret != "", | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
|   | ||||
| @@ -51,6 +51,7 @@ type AppContext struct { | ||||
| 	GenericName           string   `json:"genericName"` | ||||
| 	Domain                string   `json:"domain"` | ||||
| 	ForgotPasswordMessage string   `json:"forgotPasswordMessage"` | ||||
| 	OAuthAutoRedirect     string   `json:"oauthAutoRedirect"` | ||||
| } | ||||
|  | ||||
| // Totp request is the request for the totp endpoint | ||||
|   | ||||
| @@ -26,6 +26,7 @@ type Config struct { | ||||
| 	GenericName             string `mapstructure:"generic-name"` | ||||
| 	DisableContinue         bool   `mapstructure:"disable-continue"` | ||||
| 	OAuthWhitelist          string `mapstructure:"oauth-whitelist"` | ||||
| 	OAuthAutoRedirect       string `mapstructure:"oauth-auto-redirect" validate:"oneof=none github google generic"` | ||||
| 	SessionExpiry           int    `mapstructure:"session-expiry"` | ||||
| 	LogLevel                int8   `mapstructure:"log-level" validate:"min=-1,max=5"` | ||||
| 	Title                   string `mapstructure:"app-title"` | ||||
| @@ -44,6 +45,7 @@ type HandlersConfig struct { | ||||
| 	GenericName           string | ||||
| 	Title                 string | ||||
| 	ForgotPasswordMessage string | ||||
| 	OAuthAutoRedirect     string | ||||
| } | ||||
|  | ||||
| // OAuthConfig is the configuration for the providers | ||||
|   | ||||
| @@ -51,6 +51,7 @@ type UserContext struct { | ||||
| 	Provider    string | ||||
| 	TotpPending bool | ||||
| 	OAuthGroups string | ||||
| 	TotpEnabled bool | ||||
| } | ||||
|  | ||||
| // LoginAttempt tracks information about login attempts for rate limiting | ||||
|   | ||||
		Reference in New Issue
	
	Block a user