mirror of
				https://github.com/steveiliop56/tinyauth.git
				synced 2025-10-31 14:15:50 +00:00 
			
		
		
		
	Compare commits
	
		
			19 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 5f7f88421e | ||
|   | bc941cb248 | ||
|   | 00d15de44f | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | a4f17de0d1 | ||
|   | 6867667de6 | ||
|   | 079886b54c | ||
|   | 19eb8f3064 | ||
|   | 1a13936693 | ||
|   | af26d705cd | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 2d4ceda12f | ||
|   | 4a87af4463 | ||
|   | 88d918d608 | ||
|   | 5854d973ea | ||
|   | f25ab72747 | ||
|   | 2233557990 | ||
|   | d3bec635f8 | ||
|   | 6519644fc1 | ||
|   | 736f65b7b2 | ||
|   | 63d39b5500 | 
| @@ -133,7 +133,10 @@ var rootCmd = &cobra.Command{ | ||||
| 				SearchFilter: config.LdapSearchFilter, | ||||
| 			} | ||||
| 			ldapService, err = ldap.NewLDAP(ldapConfig) | ||||
| 			HandleError(err, "Failed to create LDAP service") | ||||
| 			if err != nil { | ||||
| 				log.Error().Err(err).Msg("Failed to initialize LDAP service, disabling LDAP authentication") | ||||
| 				ldapService = nil | ||||
| 			} | ||||
| 		} else { | ||||
| 			log.Info().Msg("LDAP not configured, using local users or OAuth") | ||||
| 		} | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
|         "@radix-ui/react-separator": "^1.1.7", | ||||
|         "@radix-ui/react-slot": "^1.2.3", | ||||
|         "@tailwindcss/vite": "^4.1.11", | ||||
|         "@tanstack/react-query": "^5.82.0", | ||||
|         "@tanstack/react-query": "^5.83.0", | ||||
|         "axios": "^1.10.0", | ||||
|         "class-variance-authority": "^0.7.1", | ||||
|         "clsx": "^2.1.1", | ||||
| @@ -26,28 +26,28 @@ | ||||
|         "react-hook-form": "^7.60.0", | ||||
|         "react-i18next": "^15.6.0", | ||||
|         "react-markdown": "^10.1.0", | ||||
|         "react-router": "^7.6.3", | ||||
|         "react-router": "^7.7.0", | ||||
|         "sonner": "^2.0.6", | ||||
|         "tailwind-merge": "^3.3.1", | ||||
|         "tailwindcss": "^4.1.11", | ||||
|         "zod": "^4.0.2", | ||||
|         "zod": "^4.0.5", | ||||
|       }, | ||||
|       "devDependencies": { | ||||
|         "@eslint/js": "^9.30.1", | ||||
|         "@eslint/js": "^9.31.0", | ||||
|         "@tanstack/eslint-plugin-query": "^5.81.2", | ||||
|         "@types/node": "^24.0.13", | ||||
|         "@types/node": "^24.0.14", | ||||
|         "@types/react": "^19.1.8", | ||||
|         "@types/react-dom": "^19.1.6", | ||||
|         "@vitejs/plugin-react": "^4.6.0", | ||||
|         "eslint": "^9.30.1", | ||||
|         "eslint": "^9.31.0", | ||||
|         "eslint-plugin-react-hooks": "^5.2.0", | ||||
|         "eslint-plugin-react-refresh": "^0.4.19", | ||||
|         "globals": "^16.3.0", | ||||
|         "prettier": "3.6.2", | ||||
|         "tw-animate-css": "^1.3.5", | ||||
|         "typescript": "~5.8.3", | ||||
|         "typescript-eslint": "^8.36.0", | ||||
|         "vite": "^7.0.4", | ||||
|         "typescript-eslint": "^8.37.0", | ||||
|         "vite": "^7.0.5", | ||||
|       }, | ||||
|     }, | ||||
|   }, | ||||
| @@ -150,11 +150,11 @@ | ||||
|  | ||||
|     "@eslint/config-helpers": ["@eslint/config-helpers@0.3.0", "", {}, "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw=="], | ||||
|  | ||||
|     "@eslint/core": ["@eslint/core@0.14.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg=="], | ||||
|     "@eslint/core": ["@eslint/core@0.15.1", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA=="], | ||||
|  | ||||
|     "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="], | ||||
|  | ||||
|     "@eslint/js": ["@eslint/js@9.30.1", "", {}, "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg=="], | ||||
|     "@eslint/js": ["@eslint/js@9.31.0", "", {}, "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw=="], | ||||
|  | ||||
|     "@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="], | ||||
|  | ||||
| @@ -328,9 +328,9 @@ | ||||
|  | ||||
|     "@tanstack/eslint-plugin-query": ["@tanstack/eslint-plugin-query@5.81.2", "", { "dependencies": { "@typescript-eslint/utils": "^8.18.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0" } }, "sha512-h4k6P6fm5VhKP5NkK+0TTVpGGyKQdx6tk7NYYG7J7PkSu7ClpLgBihw7yzK8N3n5zPaF3IMyErxfoNiXWH/3/A=="], | ||||
|  | ||||
|     "@tanstack/query-core": ["@tanstack/query-core@5.82.0", "", {}, "sha512-JrjoVuaajBQtnoWSg8iaPHaT4mW73lK2t+exxHNOSMqy0+13eKLqJgTKXKImLejQIfdAHQ6Un0njEhOvUtOd5w=="], | ||||
|     "@tanstack/query-core": ["@tanstack/query-core@5.83.0", "", {}, "sha512-0M8dA+amXUkyz5cVUm/B+zSk3xkQAcuXuz5/Q/LveT4ots2rBpPTZOzd7yJa2Utsf8D2Upl5KyjhHRY+9lB/XA=="], | ||||
|  | ||||
|     "@tanstack/react-query": ["@tanstack/react-query@5.82.0", "", { "dependencies": { "@tanstack/query-core": "5.82.0" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-mnk8/ofKEthFeMdhV1dV8YXRf+9HqvXAcciXkoo755d/ocfWq7N/Y9jGOzS3h7ZW9dDGwSIhs3/HANWUBsyqYg=="], | ||||
|     "@tanstack/react-query": ["@tanstack/react-query@5.83.0", "", { "dependencies": { "@tanstack/query-core": "5.83.0" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-/XGYhZ3foc5H0VM2jLSD/NyBRIOK4q9kfeml4+0x2DlL6xVuAcVEW+hTlTapAmejObg0i3eNqhkr2dT+eciwoQ=="], | ||||
|  | ||||
|     "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], | ||||
|  | ||||
| @@ -354,7 +354,7 @@ | ||||
|  | ||||
|     "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], | ||||
|  | ||||
|     "@types/node": ["@types/node@24.0.13", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-Qm9OYVOFHFYg3wJoTSrz80hoec5Lia/dPp84do3X7dZvLikQvM1YpmvTBEdIr/e+U8HTkFjLHLnl78K/qjf+jQ=="], | ||||
|     "@types/node": ["@types/node@24.0.14", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw=="], | ||||
|  | ||||
|     "@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="], | ||||
|  | ||||
| @@ -364,25 +364,25 @@ | ||||
|  | ||||
|     "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], | ||||
|  | ||||
|     "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.36.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.36.0", "@typescript-eslint/type-utils": "8.36.0", "@typescript-eslint/utils": "8.36.0", "@typescript-eslint/visitor-keys": "8.36.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.36.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg=="], | ||||
|     "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.37.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.37.0", "@typescript-eslint/type-utils": "8.37.0", "@typescript-eslint/utils": "8.37.0", "@typescript-eslint/visitor-keys": "8.37.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.37.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA=="], | ||||
|  | ||||
|     "@typescript-eslint/parser": ["@typescript-eslint/parser@8.36.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.36.0", "@typescript-eslint/types": "8.36.0", "@typescript-eslint/typescript-estree": "8.36.0", "@typescript-eslint/visitor-keys": "8.36.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q=="], | ||||
|     "@typescript-eslint/parser": ["@typescript-eslint/parser@8.37.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.37.0", "@typescript-eslint/types": "8.37.0", "@typescript-eslint/typescript-estree": "8.37.0", "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA=="], | ||||
|  | ||||
|     "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.34.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.34.1", "@typescript-eslint/types": "^8.34.1", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-nuHlOmFZfuRwLJKDGQOVc0xnQrAmuq1Mj/ISou5044y1ajGNp2BNliIqp7F2LPQ5sForz8lempMFCovfeS1XoA=="], | ||||
|     "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.37.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.37.0", "@typescript-eslint/types": "^8.37.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA=="], | ||||
|  | ||||
|     "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.34.1", "", { "dependencies": { "@typescript-eslint/types": "8.34.1", "@typescript-eslint/visitor-keys": "8.34.1" } }, "sha512-beu6o6QY4hJAgL1E8RaXNC071G4Kso2MGmJskCFQhRhg8VOH/FDbC8soP8NHN7e/Hdphwp8G8cE6OBzC8o41ZA=="], | ||||
|  | ||||
|     "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.34.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-K4Sjdo4/xF9NEeA2khOb7Y5nY6NSXBnod87uniVYW9kHP+hNlDV8trUSFeynA2uxWam4gIWgWoygPrv9VMWrYg=="], | ||||
|     "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.37.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg=="], | ||||
|  | ||||
|     "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.36.0", "", { "dependencies": { "@typescript-eslint/typescript-estree": "8.36.0", "@typescript-eslint/utils": "8.36.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg=="], | ||||
|     "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.37.0", "", { "dependencies": { "@typescript-eslint/types": "8.37.0", "@typescript-eslint/typescript-estree": "8.37.0", "@typescript-eslint/utils": "8.37.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow=="], | ||||
|  | ||||
|     "@typescript-eslint/types": ["@typescript-eslint/types@8.34.1", "", {}, "sha512-rjLVbmE7HR18kDsjNIZQHxmv9RZwlgzavryL5Lnj2ujIRTeXlKtILHgRNmQ3j4daw7zd+mQgy+uyt6Zo6I0IGA=="], | ||||
|  | ||||
|     "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.34.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.34.1", "@typescript-eslint/tsconfig-utils": "8.34.1", "@typescript-eslint/types": "8.34.1", "@typescript-eslint/visitor-keys": "8.34.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-rjCNqqYPuMUF5ODD+hWBNmOitjBWghkGKJg6hiCHzUvXRy6rK22Jd3rwbP2Xi+R7oYVvIKhokHVhH41BxPV5mA=="], | ||||
|     "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.37.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.37.0", "@typescript-eslint/tsconfig-utils": "8.37.0", "@typescript-eslint/types": "8.37.0", "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg=="], | ||||
|  | ||||
|     "@typescript-eslint/utils": ["@typescript-eslint/utils@8.34.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.34.1", "@typescript-eslint/types": "8.34.1", "@typescript-eslint/typescript-estree": "8.34.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-mqOwUdZ3KjtGk7xJJnLbHxTuWVn3GO2WZZuM+Slhkun4+qthLdXx32C8xIXbO1kfCECb3jIs3eoxK3eryk7aoQ=="], | ||||
|  | ||||
|     "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.36.0", "", { "dependencies": { "@typescript-eslint/types": "8.36.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA=="], | ||||
|     "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.37.0", "", { "dependencies": { "@typescript-eslint/types": "8.37.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w=="], | ||||
|  | ||||
|     "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], | ||||
|  | ||||
| @@ -494,7 +494,7 @@ | ||||
|  | ||||
|     "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], | ||||
|  | ||||
|     "eslint": ["eslint@9.30.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.0", "@eslint/core": "^0.14.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.30.1", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ=="], | ||||
|     "eslint": ["eslint@9.31.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.0", "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.31.0", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ=="], | ||||
|  | ||||
|     "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@5.2.0", "", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg=="], | ||||
|  | ||||
| @@ -804,7 +804,7 @@ | ||||
|  | ||||
|     "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], | ||||
|  | ||||
|     "react-router": ["react-router@7.6.3", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-zf45LZp5skDC6I3jDLXQUu0u26jtuP4lEGbc7BbdyxenBN1vJSTA18czM2D+h5qyMBuMrD+9uB+mU37HIoKGRA=="], | ||||
|     "react-router": ["react-router@7.7.0", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-3FUYSwlvB/5wRJVTL/aavqHmfUKe0+Xm9MllkYgGo9eDwNdkvwlJGjpPxono1kCycLt6AnDTgjmXvK3/B4QGuw=="], | ||||
|  | ||||
|     "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], | ||||
|  | ||||
| @@ -872,7 +872,7 @@ | ||||
|  | ||||
|     "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], | ||||
|  | ||||
|     "typescript-eslint": ["typescript-eslint@8.36.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.36.0", "@typescript-eslint/parser": "8.36.0", "@typescript-eslint/utils": "8.36.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA=="], | ||||
|     "typescript-eslint": ["typescript-eslint@8.37.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.37.0", "@typescript-eslint/parser": "8.37.0", "@typescript-eslint/typescript-estree": "8.37.0", "@typescript-eslint/utils": "8.37.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA=="], | ||||
|  | ||||
|     "undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="], | ||||
|  | ||||
| @@ -900,7 +900,7 @@ | ||||
|  | ||||
|     "vfile-message": ["vfile-message@4.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw=="], | ||||
|  | ||||
|     "vite": ["vite@7.0.4", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.6", "picomatch": "^4.0.2", "postcss": "^8.5.6", "rollup": "^4.40.0", "tinyglobby": "^0.2.14" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA=="], | ||||
|     "vite": ["vite@7.0.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.6", "picomatch": "^4.0.2", "postcss": "^8.5.6", "rollup": "^4.40.0", "tinyglobby": "^0.2.14" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw=="], | ||||
|  | ||||
|     "void-elements": ["void-elements@3.1.0", "", {}, "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w=="], | ||||
|  | ||||
| @@ -912,7 +912,7 @@ | ||||
|  | ||||
|     "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], | ||||
|  | ||||
|     "zod": ["zod@4.0.2", "", {}, "sha512-X2niJNY54MGam4L6Kj0AxeedeDIi/E5QFW0On2faSX5J4/pfLk1tW+cRMIMoojnCavn/u5W/kX17e1CSGnKMxA=="], | ||||
|     "zod": ["zod@4.0.5", "", {}, "sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA=="], | ||||
|  | ||||
|     "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], | ||||
|  | ||||
| @@ -932,6 +932,8 @@ | ||||
|  | ||||
|     "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], | ||||
|  | ||||
|     "@eslint/plugin-kit/@eslint/core": ["@eslint/core@0.14.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg=="], | ||||
|  | ||||
|     "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="], | ||||
|  | ||||
|     "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="], | ||||
| @@ -958,31 +960,33 @@ | ||||
|  | ||||
|     "@types/babel__traverse/@babel/types": ["@babel/types@7.27.1", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q=="], | ||||
|  | ||||
|     "@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.36.0", "", { "dependencies": { "@typescript-eslint/types": "8.36.0", "@typescript-eslint/visitor-keys": "8.36.0" } }, "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA=="], | ||||
|     "@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.37.0", "", { "dependencies": { "@typescript-eslint/types": "8.37.0", "@typescript-eslint/visitor-keys": "8.37.0" } }, "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA=="], | ||||
|  | ||||
|     "@typescript-eslint/eslint-plugin/@typescript-eslint/utils": ["@typescript-eslint/utils@8.36.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.36.0", "@typescript-eslint/types": "8.36.0", "@typescript-eslint/typescript-estree": "8.36.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g=="], | ||||
|     "@typescript-eslint/eslint-plugin/@typescript-eslint/utils": ["@typescript-eslint/utils@8.37.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.37.0", "@typescript-eslint/types": "8.37.0", "@typescript-eslint/typescript-estree": "8.37.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A=="], | ||||
|  | ||||
|     "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.4", "", {}, "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A=="], | ||||
|  | ||||
|     "@typescript-eslint/parser/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.36.0", "", { "dependencies": { "@typescript-eslint/types": "8.36.0", "@typescript-eslint/visitor-keys": "8.36.0" } }, "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA=="], | ||||
|     "@typescript-eslint/parser/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.37.0", "", { "dependencies": { "@typescript-eslint/types": "8.37.0", "@typescript-eslint/visitor-keys": "8.37.0" } }, "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA=="], | ||||
|  | ||||
|     "@typescript-eslint/parser/@typescript-eslint/types": ["@typescript-eslint/types@8.36.0", "", {}, "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ=="], | ||||
|     "@typescript-eslint/parser/@typescript-eslint/types": ["@typescript-eslint/types@8.37.0", "", {}, "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ=="], | ||||
|  | ||||
|     "@typescript-eslint/parser/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.36.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.36.0", "@typescript-eslint/tsconfig-utils": "8.36.0", "@typescript-eslint/types": "8.36.0", "@typescript-eslint/visitor-keys": "8.36.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg=="], | ||||
|     "@typescript-eslint/project-service/@typescript-eslint/types": ["@typescript-eslint/types@8.37.0", "", {}, "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ=="], | ||||
|  | ||||
|     "@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.34.1", "", { "dependencies": { "@typescript-eslint/types": "8.34.1", "eslint-visitor-keys": "^4.2.1" } }, "sha512-xoh5rJ+tgsRKoXnkBPFRLZ7rjKM0AfVbC68UZ/ECXoDbfggb9RbEySN359acY1vS3qZ0jVTVWzbtfapwm5ztxw=="], | ||||
|  | ||||
|     "@typescript-eslint/type-utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.36.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.36.0", "@typescript-eslint/tsconfig-utils": "8.36.0", "@typescript-eslint/types": "8.36.0", "@typescript-eslint/visitor-keys": "8.36.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg=="], | ||||
|     "@typescript-eslint/type-utils/@typescript-eslint/types": ["@typescript-eslint/types@8.37.0", "", {}, "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ=="], | ||||
|  | ||||
|     "@typescript-eslint/type-utils/@typescript-eslint/utils": ["@typescript-eslint/utils@8.36.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.36.0", "@typescript-eslint/types": "8.36.0", "@typescript-eslint/typescript-estree": "8.36.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g=="], | ||||
|     "@typescript-eslint/type-utils/@typescript-eslint/utils": ["@typescript-eslint/utils@8.37.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.37.0", "@typescript-eslint/types": "8.37.0", "@typescript-eslint/typescript-estree": "8.37.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A=="], | ||||
|  | ||||
|     "@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.34.1", "", { "dependencies": { "@typescript-eslint/types": "8.34.1", "eslint-visitor-keys": "^4.2.1" } }, "sha512-xoh5rJ+tgsRKoXnkBPFRLZ7rjKM0AfVbC68UZ/ECXoDbfggb9RbEySN359acY1vS3qZ0jVTVWzbtfapwm5ztxw=="], | ||||
|     "@typescript-eslint/typescript-estree/@typescript-eslint/types": ["@typescript-eslint/types@8.37.0", "", {}, "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ=="], | ||||
|  | ||||
|     "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], | ||||
|  | ||||
|     "@typescript-eslint/typescript-estree/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], | ||||
|  | ||||
|     "@typescript-eslint/visitor-keys/@typescript-eslint/types": ["@typescript-eslint/types@8.36.0", "", {}, "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ=="], | ||||
|     "@typescript-eslint/utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.34.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.34.1", "@typescript-eslint/tsconfig-utils": "8.34.1", "@typescript-eslint/types": "8.34.1", "@typescript-eslint/visitor-keys": "8.34.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-rjCNqqYPuMUF5ODD+hWBNmOitjBWghkGKJg6hiCHzUvXRy6rK22Jd3rwbP2Xi+R7oYVvIKhokHVhH41BxPV5mA=="], | ||||
|  | ||||
|     "@typescript-eslint/visitor-keys/@typescript-eslint/types": ["@typescript-eslint/types@8.37.0", "", {}, "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ=="], | ||||
|  | ||||
|     "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], | ||||
|  | ||||
| @@ -996,7 +1000,7 @@ | ||||
|  | ||||
|     "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], | ||||
|  | ||||
|     "typescript-eslint/@typescript-eslint/utils": ["@typescript-eslint/utils@8.36.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.36.0", "@typescript-eslint/types": "8.36.0", "@typescript-eslint/typescript-estree": "8.36.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g=="], | ||||
|     "typescript-eslint/@typescript-eslint/utils": ["@typescript-eslint/utils@8.37.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.37.0", "@typescript-eslint/types": "8.37.0", "@typescript-eslint/typescript-estree": "8.37.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A=="], | ||||
|  | ||||
|     "@babel/helper-module-imports/@babel/traverse/@babel/generator": ["@babel/generator@7.27.1", "", { "dependencies": { "@babel/parser": "^7.27.1", "@babel/types": "^7.27.1", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" } }, "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w=="], | ||||
|  | ||||
| @@ -1018,66 +1022,30 @@ | ||||
|  | ||||
|     "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], | ||||
|  | ||||
|     "@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager/@typescript-eslint/types": ["@typescript-eslint/types@8.36.0", "", {}, "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ=="], | ||||
|     "@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager/@typescript-eslint/types": ["@typescript-eslint/types@8.37.0", "", {}, "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ=="], | ||||
|  | ||||
|     "@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/types": ["@typescript-eslint/types@8.36.0", "", {}, "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ=="], | ||||
|     "@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/types": ["@typescript-eslint/types@8.37.0", "", {}, "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ=="], | ||||
|  | ||||
|     "@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.36.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.36.0", "@typescript-eslint/tsconfig-utils": "8.36.0", "@typescript-eslint/types": "8.36.0", "@typescript-eslint/visitor-keys": "8.36.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg=="], | ||||
|  | ||||
|     "@typescript-eslint/parser/@typescript-eslint/typescript-estree/@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.36.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.36.0", "@typescript-eslint/types": "^8.36.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g=="], | ||||
|  | ||||
|     "@typescript-eslint/parser/@typescript-eslint/typescript-estree/@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.36.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA=="], | ||||
|  | ||||
|     "@typescript-eslint/parser/@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], | ||||
|  | ||||
|     "@typescript-eslint/parser/@typescript-eslint/typescript-estree/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], | ||||
|  | ||||
|     "@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.36.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.36.0", "@typescript-eslint/types": "^8.36.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g=="], | ||||
|  | ||||
|     "@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.36.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA=="], | ||||
|  | ||||
|     "@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/@typescript-eslint/types": ["@typescript-eslint/types@8.36.0", "", {}, "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ=="], | ||||
|  | ||||
|     "@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], | ||||
|  | ||||
|     "@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], | ||||
|  | ||||
|     "@typescript-eslint/type-utils/@typescript-eslint/utils/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.36.0", "", { "dependencies": { "@typescript-eslint/types": "8.36.0", "@typescript-eslint/visitor-keys": "8.36.0" } }, "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA=="], | ||||
|  | ||||
|     "@typescript-eslint/type-utils/@typescript-eslint/utils/@typescript-eslint/types": ["@typescript-eslint/types@8.36.0", "", {}, "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ=="], | ||||
|     "@typescript-eslint/type-utils/@typescript-eslint/utils/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.37.0", "", { "dependencies": { "@typescript-eslint/types": "8.37.0", "@typescript-eslint/visitor-keys": "8.37.0" } }, "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA=="], | ||||
|  | ||||
|     "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], | ||||
|  | ||||
|     "typescript-eslint/@typescript-eslint/utils/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.36.0", "", { "dependencies": { "@typescript-eslint/types": "8.36.0", "@typescript-eslint/visitor-keys": "8.36.0" } }, "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA=="], | ||||
|     "@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.34.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.34.1", "@typescript-eslint/types": "^8.34.1", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-nuHlOmFZfuRwLJKDGQOVc0xnQrAmuq1Mj/ISou5044y1ajGNp2BNliIqp7F2LPQ5sForz8lempMFCovfeS1XoA=="], | ||||
|  | ||||
|     "typescript-eslint/@typescript-eslint/utils/@typescript-eslint/types": ["@typescript-eslint/types@8.36.0", "", {}, "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ=="], | ||||
|     "@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.34.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-K4Sjdo4/xF9NEeA2khOb7Y5nY6NSXBnod87uniVYW9kHP+hNlDV8trUSFeynA2uxWam4gIWgWoygPrv9VMWrYg=="], | ||||
|  | ||||
|     "typescript-eslint/@typescript-eslint/utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.36.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.36.0", "@typescript-eslint/tsconfig-utils": "8.36.0", "@typescript-eslint/types": "8.36.0", "@typescript-eslint/visitor-keys": "8.36.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg=="], | ||||
|     "@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.34.1", "", { "dependencies": { "@typescript-eslint/types": "8.34.1", "eslint-visitor-keys": "^4.2.1" } }, "sha512-xoh5rJ+tgsRKoXnkBPFRLZ7rjKM0AfVbC68UZ/ECXoDbfggb9RbEySN359acY1vS3qZ0jVTVWzbtfapwm5ztxw=="], | ||||
|  | ||||
|     "@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], | ||||
|  | ||||
|     "@typescript-eslint/utils/@typescript-eslint/typescript-estree/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], | ||||
|  | ||||
|     "typescript-eslint/@typescript-eslint/utils/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.37.0", "", { "dependencies": { "@typescript-eslint/types": "8.37.0", "@typescript-eslint/visitor-keys": "8.37.0" } }, "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA=="], | ||||
|  | ||||
|     "typescript-eslint/@typescript-eslint/utils/@typescript-eslint/types": ["@typescript-eslint/types@8.37.0", "", {}, "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ=="], | ||||
|  | ||||
|     "@tailwindcss/oxide-wasm32-wasi/@emnapi/core/@emnapi/wasi-threads/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], | ||||
|  | ||||
|     "@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.36.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.36.0", "@typescript-eslint/types": "^8.36.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g=="], | ||||
|  | ||||
|     "@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.36.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA=="], | ||||
|  | ||||
|     "@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], | ||||
|  | ||||
|     "@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], | ||||
|  | ||||
|     "@typescript-eslint/parser/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], | ||||
|  | ||||
|     "@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], | ||||
|  | ||||
|     "typescript-eslint/@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.36.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.36.0", "@typescript-eslint/types": "^8.36.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g=="], | ||||
|  | ||||
|     "typescript-eslint/@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.36.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA=="], | ||||
|  | ||||
|     "typescript-eslint/@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], | ||||
|  | ||||
|     "typescript-eslint/@typescript-eslint/utils/@typescript-eslint/typescript-estree/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], | ||||
|  | ||||
|     "@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], | ||||
|  | ||||
|     "typescript-eslint/@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], | ||||
|     "@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -16,7 +16,7 @@ | ||||
|     "@radix-ui/react-separator": "^1.1.7", | ||||
|     "@radix-ui/react-slot": "^1.2.3", | ||||
|     "@tailwindcss/vite": "^4.1.11", | ||||
|     "@tanstack/react-query": "^5.82.0", | ||||
|     "@tanstack/react-query": "^5.83.0", | ||||
|     "axios": "^1.10.0", | ||||
|     "class-variance-authority": "^0.7.1", | ||||
|     "clsx": "^2.1.1", | ||||
| @@ -32,27 +32,27 @@ | ||||
|     "react-hook-form": "^7.60.0", | ||||
|     "react-i18next": "^15.6.0", | ||||
|     "react-markdown": "^10.1.0", | ||||
|     "react-router": "^7.6.3", | ||||
|     "react-router": "^7.7.0", | ||||
|     "sonner": "^2.0.6", | ||||
|     "tailwind-merge": "^3.3.1", | ||||
|     "tailwindcss": "^4.1.11", | ||||
|     "zod": "^4.0.2" | ||||
|     "zod": "^4.0.5" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@eslint/js": "^9.30.1", | ||||
|     "@eslint/js": "^9.31.0", | ||||
|     "@tanstack/eslint-plugin-query": "^5.81.2", | ||||
|     "@types/node": "^24.0.13", | ||||
|     "@types/node": "^24.0.14", | ||||
|     "@types/react": "^19.1.8", | ||||
|     "@types/react-dom": "^19.1.6", | ||||
|     "@vitejs/plugin-react": "^4.6.0", | ||||
|     "eslint": "^9.30.1", | ||||
|     "eslint": "^9.31.0", | ||||
|     "eslint-plugin-react-hooks": "^5.2.0", | ||||
|     "eslint-plugin-react-refresh": "^0.4.19", | ||||
|     "globals": "^16.3.0", | ||||
|     "prettier": "3.6.2", | ||||
|     "tw-animate-css": "^1.3.5", | ||||
|     "typescript": "~5.8.3", | ||||
|     "typescript-eslint": "^8.36.0", | ||||
|     "vite": "^7.0.4" | ||||
|     "typescript-eslint": "^8.37.0", | ||||
|     "vite": "^7.0.5" | ||||
|   } | ||||
| } | ||||
| @@ -12,6 +12,7 @@ import { | ||||
| } from "../ui/form"; | ||||
| import { Button } from "../ui/button"; | ||||
| import { loginSchema, LoginSchema } from "@/schemas/login-schema"; | ||||
| import z from "zod"; | ||||
|  | ||||
| interface Props { | ||||
|   onSubmit: (data: LoginSchema) => void; | ||||
| @@ -22,6 +23,11 @@ export const LoginForm = (props: Props) => { | ||||
|   const { onSubmit, loading } = props; | ||||
|   const { t } = useTranslation(); | ||||
|  | ||||
|   z.config({ | ||||
|     customError: (iss) => | ||||
|       iss.input === undefined ? t("fieldRequired") : t("invalidInput"), | ||||
|   }); | ||||
|  | ||||
|   const form = useForm<LoginSchema>({ | ||||
|     resolver: zodResolver(loginSchema), | ||||
|   }); | ||||
| @@ -39,6 +45,7 @@ export const LoginForm = (props: Props) => { | ||||
|                 <Input | ||||
|                   placeholder={t("loginUsername")} | ||||
|                   disabled={loading} | ||||
|                   autoComplete="username" | ||||
|                   {...field} | ||||
|                 /> | ||||
|               </FormControl> | ||||
| @@ -58,6 +65,7 @@ export const LoginForm = (props: Props) => { | ||||
|                     placeholder={t("loginPassword")} | ||||
|                     type="password" | ||||
|                     disabled={loading} | ||||
|                     autoComplete="current-password" | ||||
|                     {...field} | ||||
|                   /> | ||||
|                 </FormControl> | ||||
|   | ||||
| @@ -8,6 +8,8 @@ import { | ||||
| import { zodResolver } from "@hookform/resolvers/zod"; | ||||
| import { useForm } from "react-hook-form"; | ||||
| import { totpSchema, TotpSchema } from "@/schemas/totp-schema"; | ||||
| import { useTranslation } from "react-i18next"; | ||||
| import z from "zod"; | ||||
|  | ||||
| interface Props { | ||||
|   formId: string; | ||||
| @@ -17,6 +19,12 @@ interface Props { | ||||
|  | ||||
| export const TotpForm = (props: Props) => { | ||||
|   const { formId, onSubmit, loading } = props; | ||||
|   const { t } = useTranslation(); | ||||
|  | ||||
|   z.config({ | ||||
|     customError: (iss) => | ||||
|       iss.input === undefined ? t("fieldRequired") : t("invalidInput"), | ||||
|   }); | ||||
|  | ||||
|   const form = useForm<TotpSchema>({ | ||||
|     resolver: zodResolver(totpSchema), | ||||
| @@ -31,7 +39,12 @@ export const TotpForm = (props: Props) => { | ||||
|           render={({ field }) => ( | ||||
|             <FormItem> | ||||
|               <FormControl> | ||||
|                 <InputOTP maxLength={6} disabled={loading} {...field}> | ||||
|                 <InputOTP | ||||
|                   maxLength={6} | ||||
|                   disabled={loading} | ||||
|                   {...field} | ||||
|                   autoComplete="one-time-code" | ||||
|                 > | ||||
|                   <InputOTPGroup> | ||||
|                     <InputOTPSlot index={0} /> | ||||
|                     <InputOTPSlot index={1} /> | ||||
|   | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", | ||||
|     "errorTitle": "An error occurred", | ||||
|     "errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", | ||||
|     "errorTitle": "حدث خطأ", | ||||
|     "errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", | ||||
|     "errorTitle": "An error occurred", | ||||
|     "errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", | ||||
|     "errorTitle": "An error occurred", | ||||
|     "errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Kunne ikke indlæse godkendelsesudbydere. Tjek venligst din konfiguration.", | ||||
|     "errorTitle": "Der opstod en fejl", | ||||
|     "errorSubtitle": "Der opstod en fejl under forsøget på at udføre denne handling. Tjek venligst konsollen for mere information.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Fehler beim Laden der Authentifizierungsanbieter. Bitte überprüfen Sie Ihre Konfiguration.", | ||||
|     "errorTitle": "Ein Fehler ist aufgetreten", | ||||
|     "errorSubtitle": "Beim Versuch, diese Aktion auszuführen, ist ein Fehler aufgetreten. Bitte überprüfen Sie die Konsole für weitere Informationen.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Αποτυχία φόρτωσης παρόχων πιστοποίησης. Παρακαλώ ελέγξτε τις ρυθμίσεις σας.", | ||||
|     "errorTitle": "Παρουσιάστηκε ένα σφάλμα", | ||||
|     "errorSubtitle": "Παρουσιάστηκε σφάλμα κατά την προσπάθεια εκτέλεσης αυτής της ενέργειας. Ελέγξτε την κονσόλα για περισσότερες πληροφορίες.", | ||||
|     "forgotPasswordMessage": "Μπορείτε να επαναφέρετε τον κωδικό πρόσβασής σας αλλάζοντας τη μεταβλητή περιβάλλοντος `USERS`." | ||||
|     "forgotPasswordMessage": "Μπορείτε να επαναφέρετε τον κωδικό πρόσβασής σας αλλάζοντας τη μεταβλητή περιβάλλοντος `USERS`.", | ||||
|     "fieldRequired": "Αυτό το πεδίο είναι υποχρεωτικό", | ||||
|     "invalidInput": "Μη έγκυρη καταχώρηση" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", | ||||
|     "errorTitle": "An error occurred", | ||||
|     "errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", | ||||
|     "errorTitle": "An error occurred", | ||||
|     "errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Error al cargar los proveedores de autenticación. Por favor revise su configuración.", | ||||
|     "errorTitle": "Ha ocurrido un error", | ||||
|     "errorSubtitle": "Ocurrió un error mientras se trataba de realizar esta acción. Por favor, revise la consola para más información.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", | ||||
|     "errorTitle": "An error occurred", | ||||
|     "errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Échec du chargement des fournisseurs d'authentification. Veuillez vérifier votre configuration.", | ||||
|     "errorTitle": "Une erreur est survenue", | ||||
|     "errorSubtitle": "Une erreur est survenue lors de l'exécution de cette action. Veuillez consulter la console pour plus d'informations.", | ||||
|     "forgotPasswordMessage": "Vous pouvez réinitialiser votre mot de passe en modifiant la variable d'environnement `USERS`." | ||||
|     "forgotPasswordMessage": "Vous pouvez réinitialiser votre mot de passe en modifiant la variable d'environnement `USERS`.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", | ||||
|     "errorTitle": "An error occurred", | ||||
|     "errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", | ||||
|     "errorTitle": "An error occurred", | ||||
|     "errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", | ||||
|     "errorTitle": "An error occurred", | ||||
|     "errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", | ||||
|     "errorTitle": "An error occurred", | ||||
|     "errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", | ||||
|     "errorTitle": "An error occurred", | ||||
|     "errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", | ||||
|     "errorTitle": "An error occurred", | ||||
|     "errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", | ||||
|     "errorTitle": "An error occurred", | ||||
|     "errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Nie udało się załadować dostawców uwierzytelniania. Sprawdź swoją konfigurację.", | ||||
|     "errorTitle": "Wystąpił błąd", | ||||
|     "errorSubtitle": "Wystąpił błąd podczas próby wykonania tej czynności. Sprawdź konsolę, aby uzyskać więcej informacji.", | ||||
|     "forgotPasswordMessage": "Możesz zresetować hasło, zmieniając zmienną środowiskową `USERS`." | ||||
|     "forgotPasswordMessage": "Możesz zresetować hasło, zmieniając zmienną środowiskową `USERS`.", | ||||
|     "fieldRequired": "To pole jest wymagane", | ||||
|     "invalidInput": "Nieprawidłowe dane wejściowe" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", | ||||
|     "errorTitle": "An error occurred", | ||||
|     "errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", | ||||
|     "errorTitle": "An error occurred", | ||||
|     "errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", | ||||
|     "errorTitle": "An error occurred", | ||||
|     "errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Не удалось загрузить провайдеров аутентификации. Пожалуйста, проверьте конфигурацию.", | ||||
|     "errorTitle": "Произошла ошибка", | ||||
|     "errorSubtitle": "Произошла ошибка при попытке выполнить это действие. Проверьте консоль для дополнительной информации.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -1,55 +1,57 @@ | ||||
| { | ||||
|     "loginTitle": "Welcome back, login with", | ||||
|     "loginTitleSimple": "Welcome back, please login", | ||||
|     "loginDivider": "Or", | ||||
|     "loginUsername": "Username", | ||||
|     "loginPassword": "Password", | ||||
|     "loginSubmit": "Login", | ||||
|     "loginFailTitle": "Failed to log in", | ||||
|     "loginFailSubtitle": "Please check your username and password", | ||||
|     "loginFailRateLimit": "You failed to login too many times. Please try again later", | ||||
|     "loginSuccessTitle": "Logged in", | ||||
|     "loginSuccessSubtitle": "Welcome back!", | ||||
|     "loginOauthFailTitle": "An error occurred", | ||||
|     "loginOauthFailSubtitle": "Failed to get OAuth URL", | ||||
|     "loginOauthSuccessTitle": "Redirecting", | ||||
|     "loginOauthSuccessSubtitle": "Redirecting to your OAuth provider", | ||||
|     "continueRedirectingTitle": "Redirecting...", | ||||
|     "continueRedirectingSubtitle": "You should be redirected to the app soon", | ||||
|     "continueInvalidRedirectTitle": "Invalid redirect", | ||||
|     "continueInvalidRedirectSubtitle": "The redirect URL is invalid", | ||||
|     "continueInsecureRedirectTitle": "Insecure redirect", | ||||
|     "continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?", | ||||
|     "continueTitle": "Continue", | ||||
|     "continueSubtitle": "Click the button to continue to your app.", | ||||
|     "logoutFailTitle": "Failed to log out", | ||||
|     "logoutFailSubtitle": "Please try again", | ||||
|     "logoutSuccessTitle": "Logged out", | ||||
|     "logoutSuccessSubtitle": "You have been logged out", | ||||
|     "logoutTitle": "Logout", | ||||
|     "logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.", | ||||
|     "logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.", | ||||
|     "notFoundTitle": "Page not found", | ||||
|     "notFoundSubtitle": "The page you are looking for does not exist.", | ||||
|     "notFoundButton": "Go home", | ||||
|     "totpFailTitle": "Failed to verify code", | ||||
|     "totpFailSubtitle": "Please check your code and try again", | ||||
|     "totpSuccessTitle": "Verified", | ||||
|     "totpSuccessSubtitle": "Redirecting to your app", | ||||
|     "totpTitle": "Enter your TOTP code", | ||||
|     "totpSubtitle": "Please enter the code from your authenticator app.", | ||||
|     "unauthorizedTitle": "Unauthorized", | ||||
|     "unauthorizedResourceSubtitle": "The user with username <code>{{username}}</code> is not authorized to access the resource <code>{{resource}}</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>.", | ||||
|     "unauthorizedIpSubtitle": "Your IP address <code>{{ip}}</code> is not authorized to access 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", | ||||
|     "forgotPasswordTitle": "Forgot your password?", | ||||
|     "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", | ||||
|     "errorTitle": "An error occurred", | ||||
|     "errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "loginTitle": "Добродошли назад, пријавите се са", | ||||
|     "loginTitleSimple": "Добродошли назад, молим вас пријавите се", | ||||
|     "loginDivider": "Или", | ||||
|     "loginUsername": "Корисничко име", | ||||
|     "loginPassword": "Лозинка", | ||||
|     "loginSubmit": "Пријава", | ||||
|     "loginFailTitle": "Неуспешна пријава", | ||||
|     "loginFailSubtitle": "Молим вас проверите ваше корисничко име и лозинку", | ||||
|     "loginFailRateLimit": "Нисте успели да се пријавите превише пута. Молим вас покушајте касније", | ||||
|     "loginSuccessTitle": "Пријављени", | ||||
|     "loginSuccessSubtitle": "Добродошли назад!", | ||||
|     "loginOauthFailTitle": "Појавила се грешка", | ||||
|     "loginOauthFailSubtitle": "Неуспело преузимање OAuth адресе", | ||||
|     "loginOauthSuccessTitle": "Преусмеравање", | ||||
|     "loginOauthSuccessSubtitle": "Преусмеравање на вашег OAuth провајдера", | ||||
|     "continueRedirectingTitle": "Преусмеравање...", | ||||
|     "continueRedirectingSubtitle": "Требали би сте ускоро да будете преусмерени на апликацију", | ||||
|     "continueInvalidRedirectTitle": "Неисправно преусмеравање", | ||||
|     "continueInvalidRedirectSubtitle": "Адреса за преусмеравање није исправна", | ||||
|     "continueInsecureRedirectTitle": "Небезбедно преусмеравање", | ||||
|     "continueInsecureRedirectSubtitle": "Покушавате да преусмерите са <code>https</code> на <code>http</code> што није безбедно. Да ли желите да наставите?", | ||||
|     "continueTitle": "Настави", | ||||
|     "continueSubtitle": "Кликните на дугме да би сте наставили на нашу апликацију.", | ||||
|     "logoutFailTitle": "Неуспешно одјављивање", | ||||
|     "logoutFailSubtitle": "Молим вас покушајте поново", | ||||
|     "logoutSuccessTitle": "Одјављени", | ||||
|     "logoutSuccessSubtitle": "Одјављени сте", | ||||
|     "logoutTitle": "Одјава", | ||||
|     "logoutUsernameSubtitle": "Тренутно сте пријављени као <code>{{username}}</code>. Кликните на дугме испод да се одјавите.", | ||||
|     "logoutOauthSubtitle": "Тренутно сте пријављени као <code>{{username}}</code> користећи {{provider}} OAuth провајдера. Кликните на дугме испод да се одјавите.", | ||||
|     "notFoundTitle": "Страница није пронађена", | ||||
|     "notFoundSubtitle": "Страница коју тражите не постоји.", | ||||
|     "notFoundButton": "На почетак", | ||||
|     "totpFailTitle": "Неуспело потврђивање кода", | ||||
|     "totpFailSubtitle": "Молим вас проверите ваш код и покушајте поново", | ||||
|     "totpSuccessTitle": "Потврђен", | ||||
|     "totpSuccessSubtitle": "Преусмеравање на вашу апликацију", | ||||
|     "totpTitle": "Унесите ваш TOTP код", | ||||
|     "totpSubtitle": "Молим вас унесите код из ваше апликације за аутентификацију.", | ||||
|     "unauthorizedTitle": "Неауторизован", | ||||
|     "unauthorizedResourceSubtitle": "Корисник са корисничким именом <code>{{username}}</code> није ауторизован да приступи ресурсу <code>{{resource}}</code>.", | ||||
|     "unauthorizedLoginSubtitle": "Корисник са корисничким именом <code>{{username}}</code> није ауторизован за пријављивање.", | ||||
|     "unauthorizedGroupsSubtitle": "Корисник са корисничким именом <code>{{username}}</code> није у групама које захтева ресурс <code>{{resource}}</code>.", | ||||
|     "unauthorizedIpSubtitle": "Ваша IP адреса <code>{{ip}}</code> није ауторизована да приступи ресурсу <code>{{resource}}</code>.", | ||||
|     "unauthorizedButton": "Покушајте поново", | ||||
|     "untrustedRedirectTitle": "Преусмерење без поверења", | ||||
|     "untrustedRedirectSubtitle": "Покушавате да преусмерите на домен који се не поклапа са подешеним доменом (<code>{{domain}}</code>). Да ли желите да наставите?", | ||||
|     "cancelTitle": "Поништи", | ||||
|     "forgotPasswordTitle": "Заборавили сте лозинку?", | ||||
|     "failedToFetchProvidersTitle": "Није успело учитавање провајдера аутентификације. Молим вас проверите ваша подешавања.", | ||||
|     "errorTitle": "Појавила се грешка", | ||||
|     "errorSubtitle": "Појавила се грешка при покушају извршавања ове радње. Молим вас проверите конзолу за додатне информације.", | ||||
|     "forgotPasswordMessage": "Можете поништити вашу лозинку променом `USERS` променљиве окружења.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", | ||||
|     "errorTitle": "An error occurred", | ||||
|     "errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", | ||||
|     "errorTitle": "An error occurred", | ||||
|     "errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", | ||||
|     "errorTitle": "An error occurred", | ||||
|     "errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", | ||||
|     "errorTitle": "An error occurred", | ||||
|     "errorSubtitle": "An error occurred while trying to perform this action. Please check the console for more information.", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "加载身份验证提供程序失败,请检查您的配置。", | ||||
|     "errorTitle": "发生了错误", | ||||
|     "errorSubtitle": "执行此操作时发生错误,请检查控制台以获取更多信息。", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
| @@ -14,7 +14,7 @@ | ||||
|     "loginOauthFailSubtitle": "無法取得 OAuth 網址", | ||||
|     "loginOauthSuccessTitle": "重新導向中", | ||||
|     "loginOauthSuccessSubtitle": "正在將您重新導向至 OAuth 供應商", | ||||
|     "continueRedirectingTitle": "重新導向中...", | ||||
|     "continueRedirectingTitle": "重新導向中……", | ||||
|     "continueRedirectingSubtitle": "您即將被重新導向至應用程式", | ||||
|     "continueInvalidRedirectTitle": "無效的重新導向", | ||||
|     "continueInvalidRedirectSubtitle": "重新導向的網址無效", | ||||
| @@ -51,5 +51,7 @@ | ||||
|     "failedToFetchProvidersTitle": "載入驗證供應商失敗。請檢查您的設定。", | ||||
|     "errorTitle": "發生錯誤", | ||||
|     "errorSubtitle": "執行此操作時發生錯誤。請檢查主控台以獲取更多資訊。", | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable." | ||||
|     "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", | ||||
|     "fieldRequired": "This field is required", | ||||
|     "invalidInput": "Invalid input" | ||||
| } | ||||
							
								
								
									
										1
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.mod
									
									
									
									
									
								
							| @@ -17,6 +17,7 @@ require ( | ||||
|  | ||||
| require ( | ||||
| 	github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect | ||||
| 	github.com/cenkalti/backoff/v5 v5.0.2 // indirect | ||||
| 	github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect | ||||
| 	github.com/charmbracelet/x/cellbuf v0.0.13 // indirect | ||||
| 	github.com/containerd/errdefs v1.0.0 // indirect | ||||
|   | ||||
							
								
								
									
										2
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.sum
									
									
									
									
									
								
							| @@ -26,6 +26,8 @@ github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY= | ||||
| github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc= | ||||
| github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= | ||||
| github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= | ||||
| github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= | ||||
| github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= | ||||
| github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs= | ||||
| github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg= | ||||
| github.com/charmbracelet/bubbletea v1.3.4 h1:kCg7B+jSCFPLYRA52SDZjr51kG/fMUEoPoZrkaDHyoI= | ||||
|   | ||||
| @@ -50,7 +50,7 @@ func (auth *Auth) GetSession(c *gin.Context) (*sessions.Session, error) { | ||||
|  | ||||
| 	// If there was an error getting the session, it might be invalid so let's clear it and retry | ||||
| 	if err != nil { | ||||
| 		log.Warn().Err(err).Msg("Invalid session, clearing cookie and retrying") | ||||
| 		log.Error().Err(err).Msg("Invalid session, clearing cookie and retrying") | ||||
| 		c.SetCookie(auth.Config.SessionCookieName, "", -1, "/", fmt.Sprintf(".%s", auth.Config.Domain), auth.Config.CookieSecure, true) | ||||
| 		session, err = auth.Store.Get(c.Request, auth.Config.SessionCookieName) | ||||
| 		if err != nil { | ||||
| @@ -88,7 +88,9 @@ func (auth *Auth) SearchUser(username string) types.UserSearch { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return types.UserSearch{} | ||||
| 	return types.UserSearch{ | ||||
| 		Type: "unknown", | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (auth *Auth) VerifyUser(search types.UserSearch, password string) bool { | ||||
| @@ -370,7 +372,7 @@ func (auth *Auth) AuthEnabled(uri string, labels types.Labels) (bool, error) { | ||||
|  | ||||
| 	// If there is an error, invalid regex, auth enabled | ||||
| 	if err != nil { | ||||
| 		log.Warn().Err(err).Msg("Invalid regex") | ||||
| 		log.Error().Err(err).Msg("Invalid regex") | ||||
| 		return true, err | ||||
| 	} | ||||
|  | ||||
| @@ -399,7 +401,7 @@ func (auth *Auth) CheckIP(labels types.Labels, ip string) bool { | ||||
| 	for _, blocked := range labels.IP.Block { | ||||
| 		res, err := utils.FilterIP(blocked, ip) | ||||
| 		if err != nil { | ||||
| 			log.Warn().Err(err).Str("item", blocked).Msg("Invalid IP/CIDR in block list") | ||||
| 			log.Error().Err(err).Str("item", blocked).Msg("Invalid IP/CIDR in block list") | ||||
| 			continue | ||||
| 		} | ||||
| 		if res { | ||||
| @@ -412,7 +414,7 @@ func (auth *Auth) CheckIP(labels types.Labels, ip string) bool { | ||||
| 	for _, allowed := range labels.IP.Allow { | ||||
| 		res, err := utils.FilterIP(allowed, ip) | ||||
| 		if err != nil { | ||||
| 			log.Warn().Err(err).Str("item", allowed).Msg("Invalid IP/CIDR in allow list") | ||||
| 			log.Error().Err(err).Str("item", allowed).Msg("Invalid IP/CIDR in allow list") | ||||
| 			continue | ||||
| 		} | ||||
| 		if res { | ||||
| @@ -436,7 +438,7 @@ func (auth *Auth) BypassedIP(labels types.Labels, ip string) bool { | ||||
| 	for _, bypassed := range labels.IP.Bypass { | ||||
| 		res, err := utils.FilterIP(bypassed, ip) | ||||
| 		if err != nil { | ||||
| 			log.Warn().Err(err).Str("item", bypassed).Msg("Invalid IP/CIDR in bypass list") | ||||
| 			log.Error().Err(err).Str("item", bypassed).Msg("Invalid IP/CIDR in bypass list") | ||||
| 			continue | ||||
| 		} | ||||
| 		if res { | ||||
|   | ||||
| @@ -5,7 +5,7 @@ type Claims struct { | ||||
| 	Name              string `json:"name"` | ||||
| 	Email             string `json:"email"` | ||||
| 	PreferredUsername string `json:"preferred_username"` | ||||
| 	Groups            []string `json:"groups"` | ||||
| 	Groups            any    `json:"groups"` | ||||
| } | ||||
|  | ||||
| // Version information | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| package server_test | ||||
| package handlers_test | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| @@ -138,7 +138,7 @@ func (h *Handlers) OAuthCallbackHandler(c *gin.Context) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	log.Debug().Msg("Got user") | ||||
| 	log.Debug().Interface("user", user).Msg("Got user") | ||||
|  | ||||
| 	// Check that email is not empty | ||||
| 	if user.Email == "" { | ||||
| @@ -189,7 +189,7 @@ func (h *Handlers) OAuthCallbackHandler(c *gin.Context) { | ||||
| 		Name:        name, | ||||
| 		Email:       user.Email, | ||||
| 		Provider:    providerName.Provider, | ||||
| 		OAuthGroups: strings.Join(user.Groups, ","), | ||||
| 		OAuthGroups: utils.CoalesceToString(user.Groups), | ||||
| 	}) | ||||
|  | ||||
| 	// Check if we have a redirect URI | ||||
|   | ||||
| @@ -40,10 +40,7 @@ func (h *Handlers) ProxyHandler(c *gin.Context) { | ||||
| 	proto := c.Request.Header.Get("X-Forwarded-Proto") | ||||
| 	host := c.Request.Header.Get("X-Forwarded-Host") | ||||
|  | ||||
| 	// Remove the port from the host if it exists | ||||
| 	hostPortless := strings.Split(host, ":")[0] // *lol* | ||||
|  | ||||
| 	// Get the id | ||||
| 	id := strings.Split(hostPortless, ".")[0] | ||||
|  | ||||
| 	labels, err := h.Docker.GetLabels(id, hostPortless) | ||||
| @@ -66,10 +63,10 @@ func (h *Handlers) ProxyHandler(c *gin.Context) { | ||||
|  | ||||
| 	ip := c.ClientIP() | ||||
|  | ||||
| 	// Check if the IP is in bypass list | ||||
| 	if h.Auth.BypassedIP(labels, ip) { | ||||
| 		headersParsed := utils.ParseHeaders(labels.Headers) | ||||
| 		c.Header("Authorization", c.Request.Header.Get("Authorization")) | ||||
|  | ||||
| 		headersParsed := utils.ParseHeaders(labels.Headers) | ||||
| 		for key, value := range headersParsed { | ||||
| 			log.Debug().Str("key", key).Msg("Setting header") | ||||
| 			c.Header(key, value) | ||||
| @@ -87,7 +84,6 @@ func (h *Handlers) ProxyHandler(c *gin.Context) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Check if the IP is allowed/blocked | ||||
| 	if !h.Auth.CheckIP(labels, ip) { | ||||
| 		if proxy.Proxy == "nginx" || !isBrowser { | ||||
| 			c.JSON(403, gin.H{ | ||||
| @@ -113,7 +109,6 @@ func (h *Handlers) ProxyHandler(c *gin.Context) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Check if auth is enabled | ||||
| 	authEnabled, err := h.Auth.AuthEnabled(uri, labels) | ||||
| 	if err != nil { | ||||
| 		log.Error().Err(err).Msg("Failed to check if app is allowed") | ||||
| @@ -129,8 +124,9 @@ func (h *Handlers) ProxyHandler(c *gin.Context) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// If auth is not enabled, return 200 | ||||
| 	if !authEnabled { | ||||
| 		c.Header("Authorization", c.Request.Header.Get("Authorization")) | ||||
|  | ||||
| 		headersParsed := utils.ParseHeaders(labels.Headers) | ||||
| 		for key, value := range headersParsed { | ||||
| 			log.Debug().Str("key", key).Msg("Setting header") | ||||
| @@ -150,7 +146,6 @@ func (h *Handlers) ProxyHandler(c *gin.Context) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// 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 | ||||
| @@ -159,7 +154,6 @@ func (h *Handlers) ProxyHandler(c *gin.Context) { | ||||
| 		userContext.IsLoggedIn = false | ||||
| 	} | ||||
|  | ||||
| 	// Check if user is logged in | ||||
| 	if userContext.IsLoggedIn { | ||||
| 		log.Debug().Msg("Authenticated") | ||||
|  | ||||
| @@ -200,7 +194,6 @@ func (h *Handlers) ProxyHandler(c *gin.Context) { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		// Check groups if using OAuth | ||||
| 		if userContext.OAuth { | ||||
| 			groupOk := h.Auth.OAuthGroup(c, userContext, labels) | ||||
|  | ||||
| @@ -239,19 +232,18 @@ func (h *Handlers) ProxyHandler(c *gin.Context) { | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		c.Header("Authorization", c.Request.Header.Get("Authorization")) | ||||
| 		c.Header("Remote-User", utils.SanitizeHeader(userContext.Username)) | ||||
| 		c.Header("Remote-Name", utils.SanitizeHeader(userContext.Name)) | ||||
| 		c.Header("Remote-Email", utils.SanitizeHeader(userContext.Email)) | ||||
| 		c.Header("Remote-Groups", utils.SanitizeHeader(userContext.OAuthGroups)) | ||||
|  | ||||
| 		// Set the rest of the headers | ||||
| 		parsedHeaders := utils.ParseHeaders(labels.Headers) | ||||
| 		for key, value := range parsedHeaders { | ||||
| 			log.Debug().Str("key", key).Msg("Setting header") | ||||
| 			c.Header(key, value) | ||||
| 		} | ||||
|  | ||||
| 		// Set basic auth headers if configured | ||||
| 		if labels.Basic.Username != "" && utils.GetSecret(labels.Basic.Password.Plain, labels.Basic.Password.File) != "" { | ||||
| 			log.Debug().Str("username", labels.Basic.Username).Msg("Setting basic auth headers") | ||||
| 			c.Header("Authorization", fmt.Sprintf("Basic %s", utils.GetBasicAuth(labels.Basic.Username, utils.GetSecret(labels.Basic.Password.Plain, labels.Basic.Password.File)))) | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 	"tinyauth/internal/auth" | ||||
| 	"tinyauth/internal/oauth" | ||||
| 	"tinyauth/internal/providers" | ||||
| 	"tinyauth/internal/types" | ||||
| 	"tinyauth/internal/utils" | ||||
| @@ -27,28 +28,92 @@ func NewHooks(config types.HooksConfig, auth *auth.Auth, providers *providers.Pr | ||||
| } | ||||
|  | ||||
| func (hooks *Hooks) UseUserContext(c *gin.Context) types.UserContext { | ||||
| 	// Get session cookie and basic auth | ||||
| 	cookie, err := hooks.Auth.GetSessionCookie(c) | ||||
| 	var provider *oauth.OAuth | ||||
|  | ||||
| 	if err != nil { | ||||
| 		log.Error().Err(err).Msg("Failed to get session cookie") | ||||
| 		goto basic | ||||
| 	} | ||||
|  | ||||
| 	if cookie.TotpPending { | ||||
| 		log.Debug().Msg("Totp pending") | ||||
| 		return types.UserContext{ | ||||
| 			Username:    cookie.Username, | ||||
| 			Name:        cookie.Name, | ||||
| 			Email:       cookie.Email, | ||||
| 			Provider:    cookie.Provider, | ||||
| 			TotpPending: true, | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if cookie.Provider == "username" { | ||||
| 		log.Debug().Msg("Provider is username") | ||||
|  | ||||
| 		userSearch := hooks.Auth.SearchUser(cookie.Username) | ||||
|  | ||||
| 		if userSearch.Type == "unknown" { | ||||
| 			log.Warn().Str("username", cookie.Username).Msg("User does not exist") | ||||
| 			goto basic | ||||
| 		} | ||||
|  | ||||
| 		log.Debug().Str("type", userSearch.Type).Msg("User exists") | ||||
|  | ||||
| 		return types.UserContext{ | ||||
| 			Username:   cookie.Username, | ||||
| 			Name:       cookie.Name, | ||||
| 			Email:      cookie.Email, | ||||
| 			IsLoggedIn: true, | ||||
| 			Provider:   "username", | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	log.Debug().Msg("Provider is not username") | ||||
|  | ||||
| 	provider = hooks.Providers.GetProvider(cookie.Provider) | ||||
|  | ||||
| 	if provider != nil { | ||||
| 		log.Debug().Msg("Provider exists") | ||||
|  | ||||
| 		if !hooks.Auth.EmailWhitelisted(cookie.Email) { | ||||
| 			log.Warn().Str("email", cookie.Email).Msg("Email is not whitelisted") | ||||
| 			hooks.Auth.DeleteSessionCookie(c) | ||||
| 			goto basic | ||||
| 		} | ||||
|  | ||||
| 		log.Debug().Msg("Email is whitelisted") | ||||
|  | ||||
| 		return types.UserContext{ | ||||
| 			Username:    cookie.Username, | ||||
| 			Name:        cookie.Name, | ||||
| 			Email:       cookie.Email, | ||||
| 			IsLoggedIn:  true, | ||||
| 			OAuth:       true, | ||||
| 			Provider:    cookie.Provider, | ||||
| 			OAuthGroups: cookie.OAuthGroups, | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| basic: | ||||
| 	log.Debug().Msg("Trying basic auth") | ||||
|  | ||||
| 	basic := hooks.Auth.GetBasicAuth(c) | ||||
|  | ||||
| 	// Check if basic auth is set | ||||
| 	if basic != nil { | ||||
| 		log.Debug().Msg("Got basic auth") | ||||
|  | ||||
| 		userSearch := hooks.Auth.SearchUser(basic.Username) | ||||
|  | ||||
| 		if userSearch.Type == "" { | ||||
| 			log.Error().Str("username", basic.Username).Msg("User does not exist") | ||||
| 		if userSearch.Type == "unkown" { | ||||
| 			log.Error().Str("username", basic.Username).Msg("Basic auth user does not exist") | ||||
| 			return types.UserContext{} | ||||
| 		} | ||||
|  | ||||
| 		// Verify the user | ||||
| 		if !hooks.Auth.VerifyUser(userSearch, basic.Password) { | ||||
| 			log.Error().Str("username", basic.Username).Msg("Password incorrect") | ||||
| 			log.Error().Str("username", basic.Username).Msg("Basic auth user password incorrect") | ||||
| 			return types.UserContext{} | ||||
| 		} | ||||
|  | ||||
| 		// Get the user type | ||||
| 		if userSearch.Type == "ldap" { | ||||
| 			log.Debug().Msg("User is LDAP") | ||||
|  | ||||
| @@ -75,73 +140,5 @@ func (hooks *Hooks) UseUserContext(c *gin.Context) types.UserContext { | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	// Check cookie error after basic auth | ||||
| 	if err != nil { | ||||
| 		log.Error().Err(err).Msg("Failed to get session cookie") | ||||
| 		return types.UserContext{} | ||||
| 	} | ||||
|  | ||||
| 	if cookie.TotpPending { | ||||
| 		log.Debug().Msg("Totp pending") | ||||
| 		return types.UserContext{ | ||||
| 			Username:    cookie.Username, | ||||
| 			Name:        cookie.Name, | ||||
| 			Email:       cookie.Email, | ||||
| 			Provider:    cookie.Provider, | ||||
| 			TotpPending: true, | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Check if session cookie is username/password auth | ||||
| 	if cookie.Provider == "username" { | ||||
| 		log.Debug().Msg("Provider is username") | ||||
|  | ||||
| 		userSearch := hooks.Auth.SearchUser(cookie.Username) | ||||
|  | ||||
| 		if userSearch.Type == "" { | ||||
| 			log.Error().Str("username", cookie.Username).Msg("User does not exist") | ||||
| 			return types.UserContext{} | ||||
| 		} | ||||
|  | ||||
| 		log.Debug().Str("type", userSearch.Type).Msg("User exists") | ||||
|  | ||||
| 		return types.UserContext{ | ||||
| 			Username:   cookie.Username, | ||||
| 			Name:       cookie.Name, | ||||
| 			Email:      cookie.Email, | ||||
| 			IsLoggedIn: true, | ||||
| 			Provider:   "username", | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	log.Debug().Msg("Provider is not username") | ||||
|  | ||||
| 	// The provider is not username so we need to check if it is an oauth provider | ||||
| 	provider := hooks.Providers.GetProvider(cookie.Provider) | ||||
|  | ||||
| 	// If we have a provider with this name | ||||
| 	if provider != nil { | ||||
| 		log.Debug().Msg("Provider exists") | ||||
|  | ||||
| 		// If the email is not whitelisted we delete the cookie and return an empty context | ||||
| 		if !hooks.Auth.EmailWhitelisted(cookie.Email) { | ||||
| 			log.Error().Str("email", cookie.Email).Msg("Email is not whitelisted") | ||||
| 			hooks.Auth.DeleteSessionCookie(c) | ||||
| 			return types.UserContext{} | ||||
| 		} | ||||
|  | ||||
| 		log.Debug().Msg("Email is whitelisted") | ||||
|  | ||||
| 		return types.UserContext{ | ||||
| 			Username:    cookie.Username, | ||||
| 			Name:        cookie.Name, | ||||
| 			Email:       cookie.Email, | ||||
| 			IsLoggedIn:  true, | ||||
| 			OAuth:       true, | ||||
| 			Provider:    cookie.Provider, | ||||
| 			OAuthGroups: cookie.OAuthGroups, | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return types.UserContext{} | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,13 @@ | ||||
| package ldap | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"crypto/tls" | ||||
| 	"fmt" | ||||
| 	"time" | ||||
| 	"tinyauth/internal/types" | ||||
|  | ||||
| 	"github.com/cenkalti/backoff/v5" | ||||
| 	ldapgo "github.com/go-ldap/ldap/v3" | ||||
| 	"github.com/rs/zerolog/log" | ||||
| ) | ||||
| @@ -30,6 +32,11 @@ func NewLDAP(config types.LdapConfig) (*LDAP, error) { | ||||
| 			err := ldap.heartbeat() | ||||
| 			if err != nil { | ||||
| 				log.Error().Err(err).Msg("LDAP connection heartbeat failed") | ||||
| 				if reconnectErr := ldap.reconnect(); reconnectErr != nil { | ||||
| 					log.Error().Err(reconnectErr).Msg("Failed to reconnect to LDAP server") | ||||
| 					continue | ||||
| 				} | ||||
| 				log.Info().Msg("Successfully reconnected to LDAP server") | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
| @@ -38,6 +45,7 @@ func NewLDAP(config types.LdapConfig) (*LDAP, error) { | ||||
| } | ||||
|  | ||||
| func (l *LDAP) connect() (*ldapgo.Conn, error) { | ||||
| 	log.Debug().Msg("Connecting to LDAP server") | ||||
| 	conn, err := ldapgo.DialURL(l.Config.Address, ldapgo.DialWithTLSConfig(&tls.Config{ | ||||
| 		InsecureSkipVerify: l.Config.Insecure, | ||||
| 		MinVersion:         tls.VersionTLS12, | ||||
| @@ -46,6 +54,7 @@ func (l *LDAP) connect() (*ldapgo.Conn, error) { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	log.Debug().Msg("Binding to LDAP server") | ||||
| 	err = conn.Bind(l.Config.BindDN, l.Config.BindPassword) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| @@ -109,3 +118,30 @@ func (l *LDAP) heartbeat() error { | ||||
| 	// No error means the connection is alive | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (l *LDAP) reconnect() error { | ||||
| 	log.Info().Msg("Reconnecting to LDAP server") | ||||
|  | ||||
| 	exp := backoff.NewExponentialBackOff() | ||||
| 	exp.InitialInterval = 500 * time.Millisecond | ||||
| 	exp.RandomizationFactor = 0.1 | ||||
| 	exp.Multiplier = 1.5 | ||||
| 	exp.Reset() | ||||
|  | ||||
| 	operation := func() (*ldapgo.Conn, error) { | ||||
| 		l.Conn.Close() | ||||
| 		conn, err := l.connect() | ||||
| 		if err != nil { | ||||
| 			return nil, nil | ||||
| 		} | ||||
| 		return conn, nil | ||||
| 	} | ||||
|  | ||||
| 	_, err := backoff.Retry(context.TODO(), operation, backoff.WithBackOff(exp), backoff.WithMaxTries(3)) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
| @@ -21,6 +21,23 @@ type Server struct { | ||||
| 	Router   *gin.Engine | ||||
| } | ||||
|  | ||||
| var ( | ||||
| 	loggerSkipPathsPrefix = []string{ | ||||
| 		"GET /api/healthcheck", | ||||
| 		"HEAD /api/healthcheck", | ||||
| 		"GET /favicon.ico", | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| func logPath(path string) bool { | ||||
| 	for _, prefix := range loggerSkipPathsPrefix { | ||||
| 		if strings.HasPrefix(path, prefix) { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func NewServer(config types.ServerConfig, handlers *handlers.Handlers) (*Server, error) { | ||||
| 	gin.SetMode(gin.ReleaseMode) | ||||
|  | ||||
| @@ -68,6 +85,7 @@ func NewServer(config types.ServerConfig, handlers *handlers.Handlers) (*Server, | ||||
|  | ||||
| 	// App routes | ||||
| 	router.GET("/api/healthcheck", handlers.HealthcheckHandler) | ||||
| 	router.HEAD("/api/healthcheck", handlers.HealthcheckHandler) | ||||
|  | ||||
| 	return &Server{ | ||||
| 		Config:   config, | ||||
| @@ -84,22 +102,19 @@ func (s *Server) Start() error { | ||||
| // zerolog is a middleware for gin that logs requests using zerolog | ||||
| func zerolog() gin.HandlerFunc { | ||||
| 	return func(c *gin.Context) { | ||||
| 		// Get initial time | ||||
| 		tStart := time.Now() | ||||
|  | ||||
| 		// Process request | ||||
| 		c.Next() | ||||
|  | ||||
| 		// Get status code, address, method and path | ||||
| 		code := c.Writer.Status() | ||||
| 		address := c.Request.RemoteAddr | ||||
| 		method := c.Request.Method | ||||
| 		path := c.Request.URL.Path | ||||
|  | ||||
| 		// Get latency | ||||
| 		latency := time.Since(tStart).String() | ||||
|  | ||||
| 		// Log request | ||||
| 		// logPath check if the path should be logged normally or with debug | ||||
| 		if logPath(method + " " + path) { | ||||
| 			switch { | ||||
| 			case code >= 200 && code < 300: | ||||
| 				log.Info().Str("method", method).Str("path", path).Str("address", address).Int("status", code).Str("latency", latency).Msg("Request") | ||||
| @@ -108,5 +123,8 @@ func zerolog() gin.HandlerFunc { | ||||
| 			case code >= 400: | ||||
| 				log.Error().Str("method", method).Str("path", path).Str("address", address).Int("status", code).Str("latency", latency).Msg("Request") | ||||
| 			} | ||||
| 		} else { | ||||
| 			log.Debug().Str("method", method).Str("path", path).Str("address", address).Int("status", code).Str("latency", latency).Msg("Request") | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -327,3 +327,24 @@ func DeriveKey(secret string, info string) (string, error) { | ||||
| 	encodedKey := base64.StdEncoding.EncodeToString(key) | ||||
| 	return encodedKey, nil | ||||
| } | ||||
|  | ||||
| func CoalesceToString(value any) string { | ||||
| 	switch v := value.(type) { | ||||
| 	case []any: | ||||
| 		log.Debug().Msg("Coalescing []any to string") | ||||
| 		strs := make([]string, 0, len(v)) | ||||
| 		for _, item := range v { | ||||
| 			if str, ok := item.(string); ok { | ||||
| 				strs = append(strs, str) | ||||
| 				continue | ||||
| 			} | ||||
| 			log.Warn().Interface("item", item).Msg("Item in []any is not a string, skipping") | ||||
| 		} | ||||
| 		return strings.Join(strs, ",") | ||||
| 	case string: | ||||
| 		return v | ||||
| 	default: | ||||
| 		log.Warn().Interface("value", value).Interface("type", v).Msg("Unsupported type, returning empty string") | ||||
| 		return "" | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -511,3 +511,38 @@ func TestDeriveKey(t *testing.T) { | ||||
| 		t.Fatalf("Expected %v, got %v", expected, result) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestCoalesceToString(t *testing.T) { | ||||
| 	t.Log("Testing coalesce to string with a string") | ||||
|  | ||||
| 	value := any("test") | ||||
| 	expected := "test" | ||||
|  | ||||
| 	result := utils.CoalesceToString(value) | ||||
|  | ||||
| 	if result != expected { | ||||
| 		t.Fatalf("Expected %v, got %v", expected, result) | ||||
| 	} | ||||
|  | ||||
| 	t.Log("Testing coalesce to string with a slice of strings") | ||||
|  | ||||
| 	value = []any{any("test1"), any("test2"), any(123)} | ||||
| 	expected = "test1,test2" | ||||
|  | ||||
| 	result = utils.CoalesceToString(value) | ||||
|  | ||||
| 	if result != expected { | ||||
| 		t.Fatalf("Expected %v, got %v", expected, result) | ||||
| 	} | ||||
|  | ||||
| 	t.Log("Testing coalesce to string with an unsupported type") | ||||
|  | ||||
| 	value = 12345 | ||||
| 	expected = "" | ||||
|  | ||||
| 	result = utils.CoalesceToString(value) | ||||
|  | ||||
| 	if result != expected { | ||||
| 		t.Fatalf("Expected %v, got %v", expected, result) | ||||
| 	} | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user