diff --git a/backend/Pipfile b/backend/Pipfile
index 566c752..7510ff1 100644
--- a/backend/Pipfile
+++ b/backend/Pipfile
@@ -6,12 +6,16 @@ name = "pypi"
[packages]
django = "*"
djangorestframework = "*"
-pycodestyle = "*"
django-cors-headers = "*"
psycopg2-binary = "*"
dotenv = "*"
pytest = "*"
pytest-django = "*"
+djangorestframework-simplejwt = "*"
+pycodestyle = "*"
+requests = "*"
+pyjwt = "*"
+drf-spectacular = "*"
[dev-packages]
diff --git a/backend/Pipfile.lock b/backend/Pipfile.lock
index 4a34ced..b075d50 100644
--- a/backend/Pipfile.lock
+++ b/backend/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "e150d0e5553cb65551da7428eb7dd6810099cf0188118c2031d785f562315b37"
+ "sha256": "c8a08d8710d5b37141e66db971439bf41996f2ea1330f2d5716f8be2a841a796"
},
"pipfile-spec": 6,
"requires": {
@@ -24,6 +24,107 @@
"markers": "python_version >= '3.9'",
"version": "==3.9.1"
},
+ "attrs": {
+ "hashes": [
+ "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3",
+ "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"
+ ],
+ "markers": "python_version >= '3.8'",
+ "version": "==25.3.0"
+ },
+ "certifi": {
+ "hashes": [
+ "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407",
+ "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==2025.8.3"
+ },
+ "charset-normalizer": {
+ "hashes": [
+ "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91",
+ "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0",
+ "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154",
+ "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601",
+ "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884",
+ "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07",
+ "sha256:0f2be7e0cf7754b9a30eb01f4295cc3d4358a479843b31f328afd210e2c7598c",
+ "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64",
+ "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe",
+ "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f",
+ "sha256:16a8770207946ac75703458e2c743631c79c59c5890c80011d536248f8eaa432",
+ "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc",
+ "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa",
+ "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9",
+ "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae",
+ "sha256:1ef99f0456d3d46a50945c98de1774da86f8e992ab5c77865ea8b8195341fc19",
+ "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d",
+ "sha256:23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e",
+ "sha256:252098c8c7a873e17dd696ed98bbe91dbacd571da4b87df3736768efa7a792e4",
+ "sha256:257f26fed7d7ff59921b78244f3cd93ed2af1800ff048c33f624c87475819dd7",
+ "sha256:2c322db9c8c89009a990ef07c3bcc9f011a3269bc06782f916cd3d9eed7c9312",
+ "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92",
+ "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31",
+ "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c",
+ "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f",
+ "sha256:34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99",
+ "sha256:3653fad4fe3ed447a596ae8638b437f827234f01a8cd801842e43f3d0a6b281b",
+ "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15",
+ "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392",
+ "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f",
+ "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8",
+ "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491",
+ "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0",
+ "sha256:511729f456829ef86ac41ca78c63a5cb55240ed23b4b737faca0eb1abb1c41bc",
+ "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0",
+ "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f",
+ "sha256:5b413b0b1bfd94dbf4023ad6945889f374cd24e3f62de58d6bb102c4d9ae534a",
+ "sha256:5d8d01eac18c423815ed4f4a2ec3b439d654e55ee4ad610e153cf02faf67ea40",
+ "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927",
+ "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849",
+ "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce",
+ "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14",
+ "sha256:70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05",
+ "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c",
+ "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c",
+ "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a",
+ "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc",
+ "sha256:88ab34806dea0671532d3f82d82b85e8fc23d7b2dd12fa837978dad9bb392a34",
+ "sha256:8999f965f922ae054125286faf9f11bc6932184b93011d138925a1773830bbe9",
+ "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096",
+ "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14",
+ "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30",
+ "sha256:a2d08ac246bb48479170408d6c19f6385fa743e7157d716e144cad849b2dd94b",
+ "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b",
+ "sha256:b5e3b2d152e74e100a9e9573837aba24aab611d39428ded46f4e4022ea7d1942",
+ "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db",
+ "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5",
+ "sha256:c60e092517a73c632ec38e290eba714e9627abe9d301c8c8a12ec32c314a2a4b",
+ "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce",
+ "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669",
+ "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0",
+ "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018",
+ "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93",
+ "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe",
+ "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049",
+ "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a",
+ "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef",
+ "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2",
+ "sha256:d22dbedd33326a4a5190dd4fe9e9e693ef12160c77382d9e87919bce54f3d4ca",
+ "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16",
+ "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f",
+ "sha256:d95bfb53c211b57198bb91c46dd5a2d8018b3af446583aab40074bf7988401cb",
+ "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1",
+ "sha256:ec557499516fc90fd374bf2e32349a2887a876fbf162c160e3c01b6849eaf557",
+ "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37",
+ "sha256:fb731e5deb0c7ef82d698b0f4c5bb724633ee2a489401594c5c88b02e6cb15f7",
+ "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72",
+ "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c",
+ "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==3.4.3"
+ },
"django": {
"hashes": [
"sha256:0745b25681b129a77aae3d4f6549b62d3913d74407831abaa0d9021a03954bae",
@@ -51,6 +152,15 @@
"markers": "python_version >= '3.9'",
"version": "==3.16.1"
},
+ "djangorestframework-simplejwt": {
+ "hashes": [
+ "sha256:2c30f3707053d384e9f315d11c2daccfcb548d4faa453111ca19a542b732e469",
+ "sha256:e72c5572f51d7803021288e2057afcbd03f17fe11d484096f40a460abc76e87f"
+ ],
+ "index": "pypi",
+ "markers": "python_version >= '3.9'",
+ "version": "==5.5.1"
+ },
"dotenv": {
"hashes": [
"sha256:29cf74a087b31dafdb5a446b6d7e11cbce8ed2741540e2339c69fbef92c94ce9"
@@ -58,6 +168,31 @@
"index": "pypi",
"version": "==0.9.9"
},
+ "drf-spectacular": {
+ "hashes": [
+ "sha256:2c778a47a40ab2f5078a7c42e82baba07397bb35b074ae4680721b2805943061",
+ "sha256:856e7edf1056e49a4245e87a61e8da4baff46c83dbc25be1da2df77f354c7cb4"
+ ],
+ "index": "pypi",
+ "markers": "python_version >= '3.7'",
+ "version": "==0.28.0"
+ },
+ "idna": {
+ "hashes": [
+ "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9",
+ "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==3.10"
+ },
+ "inflection": {
+ "hashes": [
+ "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417",
+ "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==0.5.1"
+ },
"iniconfig": {
"hashes": [
"sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7",
@@ -66,6 +201,22 @@
"markers": "python_version >= '3.8'",
"version": "==2.1.0"
},
+ "jsonschema": {
+ "hashes": [
+ "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63",
+ "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85"
+ ],
+ "markers": "python_version >= '3.9'",
+ "version": "==4.25.1"
+ },
+ "jsonschema-specifications": {
+ "hashes": [
+ "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af",
+ "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608"
+ ],
+ "markers": "python_version >= '3.9'",
+ "version": "==2025.4.1"
+ },
"packaging": {
"hashes": [
"sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484",
@@ -174,6 +325,15 @@
"markers": "python_version >= '3.8'",
"version": "==2.19.2"
},
+ "pyjwt": {
+ "hashes": [
+ "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953",
+ "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb"
+ ],
+ "index": "pypi",
+ "markers": "python_version >= '3.9'",
+ "version": "==2.10.1"
+ },
"pytest": {
"hashes": [
"sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7",
@@ -200,6 +360,243 @@
"markers": "python_version >= '3.9'",
"version": "==1.1.1"
},
+ "pyyaml": {
+ "hashes": [
+ "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff",
+ "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48",
+ "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086",
+ "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e",
+ "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133",
+ "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5",
+ "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484",
+ "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee",
+ "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5",
+ "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68",
+ "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a",
+ "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf",
+ "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99",
+ "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8",
+ "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85",
+ "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19",
+ "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc",
+ "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a",
+ "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1",
+ "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317",
+ "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c",
+ "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631",
+ "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d",
+ "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652",
+ "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5",
+ "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e",
+ "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b",
+ "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8",
+ "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476",
+ "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706",
+ "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563",
+ "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237",
+ "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b",
+ "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083",
+ "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180",
+ "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425",
+ "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e",
+ "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f",
+ "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725",
+ "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183",
+ "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab",
+ "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774",
+ "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725",
+ "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e",
+ "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5",
+ "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d",
+ "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290",
+ "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44",
+ "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed",
+ "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4",
+ "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba",
+ "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12",
+ "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"
+ ],
+ "markers": "python_version >= '3.8'",
+ "version": "==6.0.2"
+ },
+ "referencing": {
+ "hashes": [
+ "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa",
+ "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"
+ ],
+ "markers": "python_version >= '3.9'",
+ "version": "==0.36.2"
+ },
+ "requests": {
+ "hashes": [
+ "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6",
+ "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"
+ ],
+ "index": "pypi",
+ "markers": "python_version >= '3.9'",
+ "version": "==2.32.5"
+ },
+ "rpds-py": {
+ "hashes": [
+ "sha256:008b839781d6c9bf3b6a8984d1d8e56f0ec46dc56df61fd669c49b58ae800400",
+ "sha256:037a2361db72ee98d829bc2c5b7cc55598ae0a5e0ec1823a56ea99374cfd73c1",
+ "sha256:079bc583a26db831a985c5257797b2b5d3affb0386e7ff886256762f82113b5e",
+ "sha256:08f1e20bccf73b08d12d804d6e1c22ca5530e71659e6673bce31a6bb71c1e73f",
+ "sha256:0b08d152555acf1f455154d498ca855618c1378ec810646fcd7c76416ac6dc60",
+ "sha256:0d807710df3b5faa66c731afa162ea29717ab3be17bdc15f90f2d9f183da4059",
+ "sha256:0dc5dceeaefcc96dc192e3a80bbe1d6c410c469e97bdd47494a7d930987f18b2",
+ "sha256:12ed005216a51b1d6e2b02a7bd31885fe317e45897de81d86dcce7d74618ffff",
+ "sha256:134fae0e36022edad8290a6661edf40c023562964efea0cc0ec7f5d392d2aaef",
+ "sha256:13e608ac9f50a0ed4faec0e90ece76ae33b34c0e8656e3dceb9a7db994c692cd",
+ "sha256:1441811a96eadca93c517d08df75de45e5ffe68aa3089924f963c782c4b898cf",
+ "sha256:15d3b4d83582d10c601f481eca29c3f138d44c92187d197aff663a269197c02d",
+ "sha256:16323f674c089b0360674a4abd28d5042947d54ba620f72514d69be4ff64845e",
+ "sha256:168b025f8fd8d8d10957405f3fdcef3dc20f5982d398f90851f4abc58c566c52",
+ "sha256:1b207d881a9aef7ba753d69c123a35d96ca7cb808056998f6b9e8747321f03b8",
+ "sha256:1fea2b1a922c47c51fd07d656324531adc787e415c8b116530a1d29c0516c62d",
+ "sha256:23f6b69d1c26c4704fec01311963a41d7de3ee0570a84ebde4d544e5a1859ffc",
+ "sha256:2643400120f55c8a96f7c9d858f7be0c88d383cd4653ae2cf0d0c88f668073e5",
+ "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8",
+ "sha256:2bde09cbcf2248b73c7c323be49b280180ff39fadcfe04e7b6f54a678d02a7cf",
+ "sha256:2c426b99a068601b5f4623573df7a7c3d72e87533a2dd2253353a03e7502566c",
+ "sha256:2efe4eb1d01b7f5f1939f4ef30ecea6c6b3521eec451fb93191bf84b2a522418",
+ "sha256:2f57af9b4d0793e53266ee4325535a31ba48e2f875da81a9177c9926dfa60746",
+ "sha256:2fd50659a069c15eef8aa3d64bbef0d69fd27bb4a50c9ab4f17f83a16cbf8905",
+ "sha256:3020724ade63fe320a972e2ffd93b5623227e684315adce194941167fee02688",
+ "sha256:3182af66048c00a075010bc7f4860f33913528a4b6fc09094a6e7598e462fe39",
+ "sha256:31d3ebadefcd73b73928ed0b2fd696f7fefda8629229f81929ac9c1854d0cffb",
+ "sha256:33aa65b97826a0e885ef6e278fbd934e98cdcfed80b63946025f01e2f5b29502",
+ "sha256:387ce8c44ae94e0ec50532d9cb0edce17311024c9794eb196b90e1058aadeb66",
+ "sha256:3adc388fc3afb6540aec081fa59e6e0d3908722771aa1e37ffe22b220a436f0b",
+ "sha256:3c64d07e95606ec402a0a1c511fe003873fa6af630bda59bac77fac8b4318ebc",
+ "sha256:3ce0cac322b0d69b63c9cdb895ee1b65805ec9ffad37639f291dd79467bee675",
+ "sha256:3d905d16f77eb6ab2e324e09bfa277b4c8e5e6b8a78a3e7ff8f3cdf773b4c013",
+ "sha256:3deab27804d65cd8289eb814c2c0e807c4b9d9916c9225e363cb0cf875eb67c1",
+ "sha256:3e039aabf6d5f83c745d5f9a0a381d031e9ed871967c0a5c38d201aca41f3ba1",
+ "sha256:41e532bbdcb57c92ba3be62c42e9f096431b4cf478da9bc3bc6ce5c38ab7ba7a",
+ "sha256:42a89282d711711d0a62d6f57d81aa43a1368686c45bc1c46b7f079d55692734",
+ "sha256:466bfe65bd932da36ff279ddd92de56b042f2266d752719beb97b08526268ec5",
+ "sha256:4708c5c0ceb2d034f9991623631d3d23cb16e65c83736ea020cdbe28d57c0a0e",
+ "sha256:47162fdab9407ec3f160805ac3e154df042e577dd53341745fc7fb3f625e6d92",
+ "sha256:4848ca84d6ded9b58e474dfdbad4b8bfb450344c0551ddc8d958bf4b36aa837c",
+ "sha256:4b507d19f817ebaca79574b16eb2ae412e5c0835542c93fe9983f1e432aca195",
+ "sha256:4e44099bd522cba71a2c6b97f68e19f40e7d85399de899d66cdb67b32d7cb786",
+ "sha256:4ed2e16abbc982a169d30d1a420274a709949e2cbdef119fe2ec9d870b42f274",
+ "sha256:4f75e4bd8ab8db624e02c8e2fc4063021b58becdbe6df793a8111d9343aec1e3",
+ "sha256:4fc9b7fe29478824361ead6e14e4f5aed570d477e06088826537e202d25fe859",
+ "sha256:50c946f048209e6362e22576baea09193809f87687a95a8db24e5fbdb307b93a",
+ "sha256:5281ed1cc1d49882f9997981c88df1a22e140ab41df19071222f7e5fc4e72125",
+ "sha256:530064db9146b247351f2a0250b8f00b289accea4596a033e94be2389977de71",
+ "sha256:55266dafa22e672f5a4f65019015f90336ed31c6383bd53f5e7826d21a0e0b83",
+ "sha256:5b640501be9288c77738b5492b3fd3abc4ba95c50c2e41273c8a1459f08298d3",
+ "sha256:62ac3d4e3e07b58ee0ddecd71d6ce3b1637de2d373501412df395a0ec5f9beb5",
+ "sha256:62f85b665cedab1a503747617393573995dac4600ff51869d69ad2f39eb5e817",
+ "sha256:639fd5efec029f99b79ae47e5d7e00ad8a773da899b6309f6786ecaf22948c48",
+ "sha256:6567d2bb951e21232c2f660c24cf3470bb96de56cdcb3f071a83feeaff8a2772",
+ "sha256:67ce7620704745881a3d4b0ada80ab4d99df390838839921f99e63c474f82cf2",
+ "sha256:689fb5200a749db0415b092972e8eba85847c23885c8543a8b0f5c009b1a5948",
+ "sha256:68afeec26d42ab3b47e541b272166a0b4400313946871cba3ed3a4fc0cab1cef",
+ "sha256:6e5e54da1e74b91dbc7996b56640f79b195d5925c2b78efaa8c5d53e1d88edde",
+ "sha256:6f4461bf931108c9fa226ffb0e257c1b18dc2d44cd72b125bec50ee0ab1248a9",
+ "sha256:6f5b7bd8e219ed50299e58551a410b64daafb5017d54bbe822e003856f06a802",
+ "sha256:70d0738ef8fee13c003b100c2fbd667ec4f133468109b3472d249231108283a3",
+ "sha256:71108900c9c3c8590697244b9519017a400d9ba26a36c48381b3f64743a44aab",
+ "sha256:74e5b2f7bb6fa38b1b10546d27acbacf2a022a8b5543efb06cfebc72a59c85be",
+ "sha256:78af06ddc7fe5cc0e967085a9115accee665fb912c22a3f54bad70cc65b05fe6",
+ "sha256:7b002cab05d6339716b03a4a3a2ce26737f6231d7b523f339fa061d53368c9d8",
+ "sha256:7b90b0496570bd6b0321724a330d8b545827c4df2034b6ddfc5f5275f55da2ad",
+ "sha256:7ba22cb9693df986033b91ae1d7a979bc399237d45fccf875b76f62bb9e52ddf",
+ "sha256:7ba32c16b064267b22f1850a34051121d423b6f7338a12b9459550eb2096e7ec",
+ "sha256:7e32721e5d4922deaaf963469d795d5bde6093207c52fec719bd22e5d1bedbc4",
+ "sha256:7ee6521b9baf06085f62ba9c7a3e5becffbc32480d2f1b351559c001c38ce4c1",
+ "sha256:80c60cfb5310677bd67cb1e85a1e8eb52e12529545441b43e6f14d90b878775a",
+ "sha256:8177002868d1426305bb5de1e138161c2ec9eb2d939be38291d7c431c4712df8",
+ "sha256:819064fa048ba01b6dadc5116f3ac48610435ac9a0058bbde98e569f9e785c39",
+ "sha256:84f7d509870098de0e864cad0102711c1e24e9b1a50ee713b65928adb22269e4",
+ "sha256:879b0e14a2da6a1102a3fc8af580fc1ead37e6d6692a781bd8c83da37429b5ab",
+ "sha256:8a3f29aba6e2d7d90528d3c792555a93497fe6538aa65eb675b44505be747808",
+ "sha256:8a63b640a7845f2bdd232eb0d0a4a2dd939bcdd6c57e6bb134526487f3160ec5",
+ "sha256:8b61097f7488de4be8244c89915da8ed212832ccf1e7c7753a25a394bf9b1f10",
+ "sha256:8ee50c3e41739886606388ba3ab3ee2aae9f35fb23f833091833255a31740797",
+ "sha256:8fabb8fd848a5f75a2324e4a84501ee3a5e3c78d8603f83475441866e60b94a3",
+ "sha256:9024de74731df54546fab0bfbcdb49fae19159ecaecfc8f37c18d2c7e2c0bd61",
+ "sha256:92022bbbad0d4426e616815b16bc4127f83c9a74940e1ccf3cfe0b387aba0228",
+ "sha256:93a2ed40de81bcff59aabebb626562d48332f3d028ca2036f1d23cbb52750be4",
+ "sha256:94c44ee01fd21c9058f124d2d4f0c9dc7634bec93cd4b38eefc385dabe71acbf",
+ "sha256:9a1f4814b65eacac94a00fc9a526e3fdafd78e439469644032032d0d63de4881",
+ "sha256:9d992ac10eb86d9b6f369647b6a3f412fc0075cfd5d799530e84d335e440a002",
+ "sha256:9e71f5a087ead99563c11fdaceee83ee982fd39cf67601f4fd66cb386336ee52",
+ "sha256:a205fdfe55c90c2cd8e540ca9ceba65cbe6629b443bc05db1f590a3db8189ff9",
+ "sha256:a46fdec0083a26415f11d5f236b79fa1291c32aaa4a17684d82f7017a1f818b1",
+ "sha256:a50431bf02583e21bf273c71b89d710e7a710ad5e39c725b14e685610555926f",
+ "sha256:a512c8263249a9d68cac08b05dd59d2b3f2061d99b322813cbcc14c3c7421998",
+ "sha256:a55b9132bb1ade6c734ddd2759c8dc132aa63687d259e725221f106b83a0e485",
+ "sha256:a6e57b0abfe7cc513450fcf529eb486b6e4d3f8aee83e92eb5f1ef848218d456",
+ "sha256:a75f305c9b013289121ec0f1181931975df78738cdf650093e6b86d74aa7d8dd",
+ "sha256:a9e960fc78fecd1100539f14132425e1d5fe44ecb9239f8f27f079962021523e",
+ "sha256:aa8933159edc50be265ed22b401125c9eebff3171f570258854dbce3ecd55475",
+ "sha256:aaf94f812c95b5e60ebaf8bfb1898a7d7cb9c1af5744d4a67fa47796e0465d4e",
+ "sha256:abfa1171a9952d2e0002aba2ad3780820b00cc3d9c98c6630f2e93271501f66c",
+ "sha256:acb9aafccaae278f449d9c713b64a9e68662e7799dbd5859e2c6b3c67b56d334",
+ "sha256:ae2775c1973e3c30316892737b91f9283f9908e3cc7625b9331271eaaed7dc90",
+ "sha256:ae92443798a40a92dc5f0b01d8a7c93adde0c4dc965310a29ae7c64d72b9fad2",
+ "sha256:b2e7f8f169d775dd9092a1743768d771f1d1300453ddfe6325ae3ab5332b4657",
+ "sha256:b4938466c6b257b2f5c4ff98acd8128ec36b5059e5c8f8372d79316b1c36bb15",
+ "sha256:b6dfb0e058adb12d8b1d1b25f686e94ffa65d9995a5157afe99743bf7369d62b",
+ "sha256:b7fb801aa7f845ddf601c49630deeeccde7ce10065561d92729bfe81bd21fb33",
+ "sha256:ba81d2b56b6d4911ce735aad0a1d4495e808b8ee4dc58715998741a26874e7c2",
+ "sha256:bbf94c58e8e0cd6b6f38d8de67acae41b3a515c26169366ab58bdca4a6883bb8",
+ "sha256:be898f271f851f68b318872ce6ebebbc62f303b654e43bf72683dbdc25b7c881",
+ "sha256:bf876e79763eecf3e7356f157540d6a093cef395b65514f17a356f62af6cc136",
+ "sha256:c1476d6f29eb81aa4151c9a31219b03f1f798dc43d8af1250a870735516a1212",
+ "sha256:c2a8fed130ce946d5c585eddc7c8eeef0051f58ac80a8ee43bd17835c144c2cc",
+ "sha256:c46c9dd2403b66a2a3b9720ec4b74d4ab49d4fabf9f03dfdce2d42af913fe8d0",
+ "sha256:c4b676c4ae3921649a15d28ed10025548e9b561ded473aa413af749503c6737e",
+ "sha256:c796c0c1cc68cb08b0284db4229f5af76168172670c74908fdbd4b7d7f515819",
+ "sha256:c918c65ec2e42c2a78d19f18c553d77319119bf43aa9e2edf7fb78d624355527",
+ "sha256:cb56c6210ef77caa58e16e8c17d35c63fe3f5b60fd9ba9d424470c3400bcf9ed",
+ "sha256:cdfe4bb2f9fe7458b7453ad3c33e726d6d1c7c0a72960bcc23800d77384e42df",
+ "sha256:cf9931f14223de59551ab9d38ed18d92f14f055a5f78c1d8ad6493f735021bbb",
+ "sha256:d252f2d8ca0195faa707f8eb9368955760880b2b42a8ee16d382bf5dd807f89a",
+ "sha256:d5fa0ee122dc09e23607a28e6d7b150da16c662e66409bbe85230e4c85bb528a",
+ "sha256:d76f9cc8665acdc0c9177043746775aa7babbf479b5520b78ae4002d889f5c21",
+ "sha256:d78827d7ac08627ea2c8e02c9e5b41180ea5ea1f747e9db0915e3adf36b62dcf",
+ "sha256:d7ff07d696a7a38152ebdb8212ca9e5baab56656749f3d6004b34ab726b550b8",
+ "sha256:d9199717881f13c32c4046a15f024971a3b78ad4ea029e8da6b86e5aa9cf4594",
+ "sha256:dc23e6820e3b40847e2f4a7726462ba0cf53089512abe9ee16318c366494c17a",
+ "sha256:dce51c828941973a5684d458214d3a36fcd28da3e1875d659388f4f9f12cc33e",
+ "sha256:dd2135527aa40f061350c3f8f89da2644de26cd73e4de458e79606384f4f68e7",
+ "sha256:dd6cd0485b7d347304067153a6dc1d73f7d4fd995a396ef32a24d24b8ac63ac8",
+ "sha256:df8b74962e35c9249425d90144e721eed198e6555a0e22a563d29fe4486b51f6",
+ "sha256:dfbfac137d2a3d0725758cd141f878bf4329ba25e34979797c89474a89a8a3a3",
+ "sha256:e202e6d4188e53c6661af813b46c37ca2c45e497fc558bacc1a7630ec2695aec",
+ "sha256:e2f6fd8a1cea5bbe599b6e78a6e5ee08db434fc8ffea51ff201c8765679698b3",
+ "sha256:e48af21883ded2b3e9eb48cb7880ad8598b31ab752ff3be6457001d78f416723",
+ "sha256:e4b9fcfbc021633863a37e92571d6f91851fa656f0180246e84cbd8b3f6b329b",
+ "sha256:e5c20f33fd10485b80f65e800bbe5f6785af510b9f4056c5a3c612ebc83ba6cb",
+ "sha256:eb11a4f1b2b63337cfd3b4d110af778a59aae51c81d195768e353d8b52f88081",
+ "sha256:ed090ccd235f6fa8bb5861684567f0a83e04f52dfc2e5c05f2e4b1309fcf85e7",
+ "sha256:ed10dc32829e7d222b7d3b93136d25a406ba9788f6a7ebf6809092da1f4d279d",
+ "sha256:eda8719d598f2f7f3e0f885cba8646644b55a187762bec091fa14a2b819746a9",
+ "sha256:ee4308f409a40e50593c7e3bb8cbe0b4d4c66d1674a316324f0c2f5383b486f9",
+ "sha256:ee5422d7fb21f6a00c1901bf6559c49fee13a5159d0288320737bbf6585bd3e4",
+ "sha256:f149826d742b406579466283769a8ea448eed82a789af0ed17b0cd5770433444",
+ "sha256:f2729615f9d430af0ae6b36cf042cb55c0936408d543fb691e1a9e36648fd35a",
+ "sha256:f39f58a27cc6e59f432b568ed8429c7e1641324fbe38131de852cd77b2d534b0",
+ "sha256:f41f814b8eaa48768d1bb551591f6ba45f87ac76899453e8ccd41dba1289b04b",
+ "sha256:f9025faafc62ed0b75a53e541895ca272815bec18abe2249ff6501c8f2e12b83",
+ "sha256:faf8d146f3d476abfee026c4ae3bdd9ca14236ae4e4c310cbd1cf75ba33d24a3",
+ "sha256:fb08b65b93e0c6dd70aac7f7890a9c0938d5ec71d5cb32d45cf844fb8ae47636",
+ "sha256:fb7c72262deae25366e3b6c0c0ba46007967aea15d1eea746e44ddba8ec58dcc",
+ "sha256:fb89bec23fddc489e5d78b550a7b773557c9ab58b7946154a10a6f7a214a48b2",
+ "sha256:fe0dd05afb46597b9a2e11c351e5e4283c741237e7f617ffb3252780cca9336a",
+ "sha256:fecc80cb2a90e28af8a9b366edacf33d7a91cbfe4c2c4544ea1246e949cfebeb",
+ "sha256:fed467af29776f6556250c9ed85ea5a4dd121ab56a5f8b206e3e7a4c551e48ec",
+ "sha256:ffce0481cc6e95e5b3f0a47ee17ffbd234399e6d532f394c8dce320c3b089c21"
+ ],
+ "markers": "python_version >= '3.9'",
+ "version": "==0.27.1"
+ },
"sqlparse": {
"hashes": [
"sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272",
@@ -207,6 +604,30 @@
],
"markers": "python_version >= '3.8'",
"version": "==0.5.3"
+ },
+ "typing-extensions": {
+ "hashes": [
+ "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466",
+ "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"
+ ],
+ "markers": "python_version >= '3.9'",
+ "version": "==4.15.0"
+ },
+ "uritemplate": {
+ "hashes": [
+ "sha256:480c2ed180878955863323eea31b0ede668795de182617fef9c6ca09e6ec9d0e",
+ "sha256:962201ba1c4edcab02e60f9a0d3821e82dfc5d2d6662a21abd533879bdb8a686"
+ ],
+ "markers": "python_version >= '3.9'",
+ "version": "==4.2.0"
+ },
+ "urllib3": {
+ "hashes": [
+ "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760",
+ "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"
+ ],
+ "markers": "python_version >= '3.9'",
+ "version": "==2.5.0"
}
},
"develop": {}
diff --git a/backend/api/auth/__init__.py b/backend/api/auth/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/backend/api/auth/serializers.py b/backend/api/auth/serializers.py
new file mode 100644
index 0000000..4eda53e
--- /dev/null
+++ b/backend/api/auth/serializers.py
@@ -0,0 +1,114 @@
+from typing import Any, Optional
+from rest_framework import serializers
+from django.conf import settings
+from api.types import User
+
+class UserResponseSerializer(serializers.Serializer):
+ id = serializers.IntegerField(read_only=True)
+ email = serializers.EmailField(read_only=True)
+ account_type = serializers.CharField(
+ source='userprofile.account_type',
+ read_only=True
+ )
+ name = serializers.CharField(source='first_name', read_only=True)
+ surname = serializers.CharField(source='last_name', read_only=True)
+ imageURL = serializers.SerializerMethodField()
+ uuid = serializers.SerializerMethodField()
+
+ class Meta:
+ ref_name = "UserResponse" # для OpenAPI
+
+ def get_uuid(self, obj: User) -> Optional[str]:
+ """Получает короткий UUID (первые 6 символов) из профиля пользователя"""
+ return obj.userprofile.short_uuid if hasattr(obj, 'userprofile') else None
+
+ def get_imageURL(self, obj: User) -> Optional[str]:
+ """Получает полный URL для изображения профиля пользователя"""
+ try:
+ if not hasattr(obj, 'userprofile') or not obj.userprofile.imageURL:
+ return None
+
+ relative_url = obj.userprofile.imageURL.lstrip('/')
+ base_url = settings.BASE_URL.rstrip('/')
+ return f"{base_url}/{relative_url}"
+ except Exception:
+ return None
+
+ def to_representation(self, instance: User) -> dict[str, Any]:
+ """Переопределяется для добавления проверки типа для вывода"""
+ data = super().to_representation(instance)
+ return {
+ 'id': data['id'], # int
+ 'email': data['email'], # str
+ 'account_type': data['account_type'], # AccountTypeLiteral
+ 'name': data['name'], # str
+ 'surname': data['surname'], # str
+ 'imageURL': data['imageURL'], # Optional[str]
+ 'uuid': data['uuid'], # Optional[str]
+ }
+
+
+class LoginRequestSerializer(serializers.Serializer):
+ """Сериализатор для запроса авторизации"""
+ login = serializers.CharField(
+ help_text="Логин пользователя",
+ required=True
+ )
+ password = serializers.CharField(
+ help_text="Пароль пользователя",
+ write_only=True,
+ required=True,
+ style={'input_type': 'password'}
+ )
+
+
+class LoginResponseSerializer(serializers.Serializer):
+ """Сериализатор для ответа при успешной авторизации"""
+ message = serializers.CharField(
+ help_text="Сообщение о успешной авторизации",
+ read_only=True
+ )
+ access = serializers.CharField(
+ help_text="JWT access token для авторизации запросов",
+ read_only=True
+ )
+ refresh = serializers.CharField(
+ help_text="JWT refresh token для обновления access token",
+ read_only=True
+ )
+ user = UserResponseSerializer(
+ help_text="Данные авторизованного пользователя",
+ read_only=True
+ )
+
+
+class LogoutResponseSerializer(serializers.Serializer):
+ """Сериализатор для ответа при выходе из системы"""
+ message = serializers.CharField(
+ help_text="Сообщение о успешном выходе",
+ read_only=True
+ )
+
+
+class RefreshTokenRequestSerializer(serializers.Serializer):
+ """Сериализатор для запроса обновления токена"""
+ refresh = serializers.CharField(
+ help_text="Refresh token для обновления",
+ required=True
+ )
+
+
+class RefreshTokenResponseSerializer(serializers.Serializer):
+ """Сериализатор для ответа с обновленными токенами"""
+ access = serializers.CharField(
+ help_text="Новый JWT access token",
+ read_only=True
+ )
+ refresh = serializers.CharField(
+ help_text="Новый JWT refresh token",
+ read_only=True
+ )
+ expires_at = serializers.FloatField(
+ help_text="Timestamp времени истечения access token",
+ read_only=True
+ )
\ No newline at end of file
diff --git a/backend/api/auth/urls.py b/backend/api/auth/urls.py
new file mode 100644
index 0000000..0f80943
--- /dev/null
+++ b/backend/api/auth/urls.py
@@ -0,0 +1,29 @@
+from django.urls import path, include
+from .views import LoginViewSet, LogoutView, RefreshTokenView
+from rest_framework.routers import DefaultRouter
+from drf_spectacular.views import (
+ SpectacularAPIView,
+ SpectacularSwaggerView,
+ SpectacularRedocView,
+)
+
+router = DefaultRouter()
+router.register(r'', LoginViewSet, basename='auth')
+
+urlpatterns = [
+ path('', include(router.urls)),
+ path('logout/', LogoutView.as_view(), name='auth-logout'),
+ path('refresh/', RefreshTokenView.as_view(), name='token-refresh'),
+ path('schema/', SpectacularAPIView.as_view(), name='schema'),
+ path(
+ 'docs/',
+ SpectacularSwaggerView.as_view(url_name='schema'),
+ name='swagger-ui',
+ ),
+ # ReDoc UI - альтернативный вариант отображения доков:
+ path(
+ 'redoc/',
+ SpectacularRedocView.as_view(url_name='schema'),
+ name='redoc',
+ ),
+]
\ No newline at end of file
diff --git a/backend/api/auth/views.py b/backend/api/auth/views.py
new file mode 100644
index 0000000..d58e6d2
--- /dev/null
+++ b/backend/api/auth/views.py
@@ -0,0 +1,259 @@
+from rest_framework import status
+from datetime import datetime
+import traceback
+from django.conf import settings
+from rest_framework.decorators import action
+from rest_framework.response import Response
+from rest_framework_simplejwt.tokens import RefreshToken
+from rest_framework.views import APIView
+from drf_spectacular.utils import extend_schema, OpenApiResponse, OpenApiExample
+from drf_spectacular.types import OpenApiTypes
+
+from django.contrib.auth.models import User
+
+from .serializers import (
+ UserResponseSerializer,
+ LoginRequestSerializer,
+ LoginResponseSerializer,
+ LogoutResponseSerializer,
+ RefreshTokenRequestSerializer,
+ RefreshTokenResponseSerializer
+)
+
+from api.utils.cookies import AuthBaseViewSet
+from api.types import User
+
+
+@extend_schema(tags=['Логин'])
+class LoginViewSet(AuthBaseViewSet):
+ """ViewSet для авторизации пользователей"""
+ serializer_class = LoginRequestSerializer
+
+ @extend_schema(
+ summary="Авторизация пользователя",
+ description="Эндпоинт для авторизации пользователя по логину и паролю",
+ responses={
+ 200: OpenApiResponse(
+ response=LoginResponseSerializer,
+ description="Успешная авторизация",
+ examples=[
+ OpenApiExample(
+ 'Успешный ответ',
+ value={
+ "message": "Успешная авторизация",
+ "access": "eyJ0eXAiOiJKV1QiLCJhbGc...",
+ "refresh": "eyJ0eXAiOiJKV1QiLCJhbGc...",
+ "user": {
+ "id": 1,
+ "email": "user@example.com",
+ "account_type": "engieneer",
+ "name": "Иван",
+ "surname": "Иванов",
+ "imageURL": "https://example.com/avatar.jpg",
+ "uuid": "abc123"
+ }
+ }
+ )
+ ]
+ ),
+ 400: OpenApiResponse(
+ description="Неверные параметры запроса",
+ response=OpenApiTypes.OBJECT,
+ examples=[
+ OpenApiExample(
+ 'Отсутствуют обязательные поля',
+ value={"error": "Логин и пароль обязательны"}
+ )
+ ]
+ ),
+ 403: OpenApiResponse(
+ description="Неверный пароль",
+ response=OpenApiTypes.OBJECT,
+ examples=[
+ OpenApiExample(
+ 'Неверный пароль',
+ value={"error": "Неверный пароль"}
+ )
+ ]
+ ),
+ 404: OpenApiResponse(
+ description="Пользователь не найден",
+ response=OpenApiTypes.OBJECT,
+ examples=[
+ OpenApiExample(
+ 'Пользователь не найден',
+ value={"error": "Пользователь не найден"}
+ )
+ ]
+ ),
+ 500: OpenApiResponse(
+ description="Внутренняя ошибка сервера",
+ response=OpenApiTypes.OBJECT,
+ examples=[
+ OpenApiExample(
+ 'Ошибка сервера',
+ value={"error": "Ошибка авторизации"}
+ )
+ ]
+ )
+ }
+ )
+ @action(detail=False, methods=['post'], url_path="login")
+ def login_client(self, request):
+ try:
+ login = request.data.get("login")
+ password = request.data.get("password")
+
+ if not login or not password:
+ return Response(
+ {"error": "Логин и пароль обязательны"},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
+ try:
+ user = User.objects.get(login=login)
+ except User.DoesNotExist:
+ return Response(
+ {"error": "Пользователь не найден"},
+ status=status.HTTP_404_NOT_FOUND
+ )
+ if not user.check_password(password):
+ return Response(
+ {"error": "Неверный пароль"},
+ status=status.HTTP_403_FORBIDDEN
+ )
+
+ refresh = RefreshToken.for_user(user)
+
+ user_data = UserResponseSerializer(user).data
+
+ response = Response({
+ "message": "Успешная авторизация",
+ "access": str(refresh.access_token),
+ "refresh": str(refresh),
+ "user": user_data
+ }, status=status.HTTP_200_OK)
+
+ # сеттим куки
+ return self._set_auth_cookies(response, refresh)
+
+ except Exception as e:
+ return Response(
+ {"error": "Ошибка авторизации"},
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR
+ )
+
+@extend_schema(tags=['Логин'])
+class RefreshTokenView(APIView):
+ """View для обновления JWT токенов"""
+ serializer_class = RefreshTokenRequestSerializer
+
+ @extend_schema(
+ summary="Обновление токенов",
+ description="Эндпоинт для обновления JWT токенов. Принимает refresh token и возвращает новую пару токенов.",
+ request=RefreshTokenRequestSerializer,
+ responses={
+ 200: OpenApiResponse(
+ response=RefreshTokenResponseSerializer,
+ description="Токены успешно обновлены",
+ examples=[
+ OpenApiExample(
+ 'Успешное обновление',
+ value={
+ "access": "eyJ0eXAiOiJKV1QiLCJhbGc...",
+ "refresh": "eyJ0eXAiOiJKV1QiLCJhbGc...",
+ "expires_at": 1679831642.0
+ }
+ )
+ ]
+ ),
+ 400: OpenApiResponse(
+ description="Ошибка обновления токена",
+ response=OpenApiTypes.OBJECT,
+ examples=[
+ OpenApiExample(
+ 'Отсутствует refresh token',
+ value={"error": "Refresh token is required"}
+ ),
+ OpenApiExample(
+ 'Невалидный refresh token',
+ value={"error": "Invalid refresh token: Token is invalid or expired"}
+ )
+ ]
+ )
+ }
+ )
+ def post(self, request):
+ try:
+ refresh_token = request.data.get('refresh')
+
+ if not refresh_token:
+ return Response(
+ {'error': 'Требуется refresh token'},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
+ try:
+ token = RefreshToken(refresh_token)
+
+ # точное время истечения токена
+ expires_at = datetime.now() + settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME']
+
+ response_data = {
+ 'access': str(token.access_token),
+ 'refresh': str(token),
+ 'expires_at': datetime.timestamp(expires_at)
+ }
+
+ return Response(response_data)
+
+ except Exception as e:
+ # логируем ошибки
+
+ print(f"Token refresh error: {str(e)}")
+ print(traceback.format_exc())
+
+ return Response(
+ {'error': f'Невалидный refresh token: {str(e)}'},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
+ except Exception as e:
+ return Response(
+ {'error': f'Ошибка обновления токена: {str(e)}'},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
+@extend_schema(tags=['Логаут'])
+class LogoutView(APIView):
+ """View для выхода из системы"""
+ serializer_class = LogoutResponseSerializer
+
+ @extend_schema(
+ summary="Выход из системы",
+ description="Эндпоинт для выхода из системы. Очищает JWT токены и сессионные куки.",
+ responses={
+ 200: OpenApiResponse(
+ response=LogoutResponseSerializer,
+ description="Успешный выход из системы",
+ examples=[
+ OpenApiExample(
+ 'Успешный выход',
+ value={"message": "Успешный выход из системы"}
+ )
+ ]
+ )
+ }
+ )
+ def post(self, request):
+ """Выход из системы с очисткой всех токенов и куки"""
+ response = Response(
+ {'message': 'Успешный выход из системы'},
+ status=status.HTTP_200_OK
+ )
+
+ response.delete_cookie('access_token')
+ response.delete_cookie('refresh_token')
+ response.delete_cookie('sessionid')
+
+ return response
\ No newline at end of file
diff --git a/backend/api/models.py b/backend/api/models.py
index 71a8362..60ac79d 100644
--- a/backend/api/models.py
+++ b/backend/api/models.py
@@ -1,3 +1,56 @@
+from django.contrib.auth.models import User
from django.db import models
+from django.db.models.fields.related import OneToOneField
+from typing import Optional
+import uuid
-# Create your models here.
+from sitemanagement.constants.account_types import account_types, AccountType, AccountTypeLiteral
+
+class UserProfile(models.Model):
+ """
+ Профиль пользователя с дополнительной информацией
+ """
+
+ def get_account_type_display(self) -> str:
+ """Автоматически добавляется Django для полей с choices"""
+ ...
+
+ user: OneToOneField[User] = models.OneToOneField(
+ User,
+ on_delete=models.CASCADE,
+ related_name='userprofile'
+ )
+ uuid: models.UUIDField = models.UUIDField(
+ default=uuid.uuid4,
+ editable=False,
+ unique=True,
+ null=True
+ )
+ account_type: models.CharField = models.CharField(
+ max_length=10,
+ verbose_name="Тип аккаунта",
+ choices=account_types,
+ db_index=True
+ )
+ imageURL: models.CharField = models.CharField(
+ max_length=255,
+ null=True,
+ blank=True,
+ verbose_name="URL изображения профиля"
+ )
+
+ class Meta:
+ verbose_name = "Профиль пользователя"
+ verbose_name_plural = "Профили пользователей"
+
+ def __str__(self) -> str:
+ return f"{self.user.first_name} ({self.get_account_type_display()})"
+
+ @property
+ def short_uuid(self) -> Optional[str]:
+ """Возвращает первые 6 символов UUID или None, если UUID не установлен"""
+ return str(self.uuid)[:6] if self.uuid else None
+
+ def get_account_type(self) -> AccountTypeLiteral:
+ """Возвращает тип аккаунта пользователя"""
+ return AccountType(self.account_type).value
\ No newline at end of file
diff --git a/backend/api/types.py b/backend/api/types.py
new file mode 100644
index 0000000..0b3c811
--- /dev/null
+++ b/backend/api/types.py
@@ -0,0 +1,9 @@
+from typing import TYPE_CHECKING
+from django.contrib.auth.models import User as DjangoUser
+
+if TYPE_CHECKING:
+ from .models import UserProfile
+
+class User(DjangoUser):
+ """Тип для Django User с кастомной моделью UserProfile"""
+ userprofile: 'UserProfile'
diff --git a/backend/api/urls.py b/backend/api/urls.py
new file mode 100644
index 0000000..c194f05
--- /dev/null
+++ b/backend/api/urls.py
@@ -0,0 +1,5 @@
+from django.urls import path, include
+
+urlpatterns = [
+ path('v1/auth/', include('api.auth.urls'))
+]
\ No newline at end of file
diff --git a/backend/api/utils/cookies.py b/backend/api/utils/cookies.py
new file mode 100644
index 0000000..2eab99f
--- /dev/null
+++ b/backend/api/utils/cookies.py
@@ -0,0 +1,65 @@
+from rest_framework.viewsets import ViewSet
+from rest_framework.decorators import action
+from rest_framework.response import Response
+from rest_framework import status
+from datetime import datetime
+from django.conf import settings
+from rest_framework_simplejwt.tokens import RefreshToken
+
+class AuthBaseViewSet(ViewSet):
+ """Базовый класс для аутентификации с общими методами"""
+
+ def _set_auth_cookies(self, response, refresh):
+ """Устанавливает куки для токенов аутентификации"""
+ response.set_cookie(
+ 'access_token',
+ str(refresh.access_token),
+ httponly=True,
+ secure=True,
+ samesite='Lax',
+ max_age=300
+ )
+ response.set_cookie(
+ 'refresh_token',
+ str(refresh),
+ httponly=True,
+ secure=True,
+ samesite='Lax',
+ max_age=86400
+ )
+ return response
+
+ @action(detail=False, methods=['post'], url_path="refresh")
+ def refresh_token(self, request):
+ try:
+ refresh_token = request.data.get('refresh')
+
+ if not refresh_token:
+ return Response(
+ {'error': 'Refresh token is required'},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
+ try:
+ token = RefreshToken(refresh_token)
+ response_data = {
+ 'access': str(token.access_token),
+ 'refresh': str(token),
+ 'expires_at': datetime.timestamp(
+ datetime.now() + settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME']
+ )
+ }
+
+ return Response(response_data)
+
+ except Exception as e:
+ return Response(
+ {'error': f'Invalid refresh token: {str(e)}'},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
+ except Exception as e:
+ return Response(
+ {'error': f'Token refresh failed: {str(e)}'},
+ status=status.HTTP_400_BAD_REQUEST
+ )
diff --git a/backend/api/views.py b/backend/api/views.py
deleted file mode 100644
index 91ea44a..0000000
--- a/backend/api/views.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from django.shortcuts import render
-
-# Create your views here.
diff --git a/backend/base/asgi.py b/backend/base/asgi.py
index 71a4cef..266428d 100644
--- a/backend/base/asgi.py
+++ b/backend/base/asgi.py
@@ -1,12 +1,3 @@
-"""
-ASGI config for base project.
-
-It exposes the ASGI callable as a module-level variable named ``application``.
-
-For more information on this file, see
-https://docs.djangoproject.com/en/5.2/howto/deployment/asgi/
-"""
-
import os
from django.core.asgi import get_asgi_application
diff --git a/backend/base/settings.py b/backend/base/settings.py
index 8618a55..bd40943 100644
--- a/backend/base/settings.py
+++ b/backend/base/settings.py
@@ -1,6 +1,7 @@
import os
from pathlib import Path
from dotenv import load_dotenv
+from datetime import timedelta
BASE_DIR = Path(__file__).resolve().parent.parent
load_dotenv(dotenv_path=BASE_DIR / './.env')
@@ -33,6 +34,27 @@ CORS_ALLOW_HEADERS = [
'cookie'
]
+REST_FRAMEWORK = {
+ 'DEFAULT_AUTHENTICATION_CLASSES': [
+ 'rest_framework_simplejwt.authentication.JWTAuthentication',
+ 'rest_framework.authentication.SessionAuthentication',
+ ],
+ 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
+}
+
+
+SIMPLE_JWT = {
+ 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=15),
+ 'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
+ 'ROTATE_REFRESH_TOKENS': True,
+ 'BLACKLIST_AFTER_ROTATION': True,
+ 'UPDATE_LAST_LOGIN': True,
+ 'ALGORITHM': 'HS256',
+ 'SIGNING_KEY': SECRET_KEY,
+ 'VERIFYING_KEY': None,
+ 'AUTH_HEADER_TYPES': ('Bearer',),
+ 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
+}
INSTALLED_APPS = [
'django.contrib.admin',
@@ -45,9 +67,36 @@ INSTALLED_APPS = [
'api.apps.ApiConfig',
'sitemanagement.apps.SitemanagementConfig',
'rest_framework',
+ 'drf_spectacular',
]
+#!OpenAPI
+SPECTACULAR_SETTINGS = {
+ 'TITLE': 'AERBIM API',
+ 'DESCRIPTION': 'AERBIM - документация по API',
+ 'VERSION': '1.0.0',
+ 'SERVE_INCLUDE_SCHEMA': False,
+ 'SCHEMA_PATH_PREFIX': '/api/v[0-9]',
+ 'COMPONENT_SPLIT_REQUEST': False, # не создавать автоматически *Request схемы
+
+ 'COMPONENT_NO_READ_ONLY_REQUIRED': True, # не требовать read_only поля
+ 'COMPONENT_SPLIT_PATCHES': False, # не создавать отдельные схемы для PATCH
+
+ # настройки безопасности
+ 'SECURITY': [{'Bearer': []}],
+ 'SWAGGER_UI_SETTINGS': {
+ 'persistAuthorization': True,
+ },
+
+ # сортировка тегов и операций
+ 'TAGS': [
+ {'name': 'Логаут', 'description': 'Метод для работы с логаутом'},
+ {'name': 'Логин', 'description': 'Методы для работы с логином'},
+ ],
+}
+
MIDDLEWARE = [
+ 'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
@@ -104,10 +153,11 @@ AUTH_PASSWORD_VALIDATORS = [
},
]
-LANGUAGE_CODE = 'en-us'
-TIME_ZONE = 'UTC'
-USE_I18N = True
+LANGUAGE_CODE = 'ru-ru'
+TIME_ZONE = 'Europe/Minsk'
USE_TZ = True
+USE_I18N = True
+USE_L10N = True
STATIC_URL = 'static/'
diff --git a/backend/base/urls.py b/backend/base/urls.py
index c7a45b4..2d42021 100644
--- a/backend/base/urls.py
+++ b/backend/base/urls.py
@@ -1,22 +1,12 @@
-"""
-URL configuration for base project.
-
-The `urlpatterns` list routes URLs to views. For more information please see:
- https://docs.djangoproject.com/en/5.2/topics/http/urls/
-Examples:
-Function views
- 1. Add an import: from my_app import views
- 2. Add a URL to urlpatterns: path('', views.home, name='home')
-Class-based views
- 1. Add an import: from other_app.views import Home
- 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
-Including another URLconf
- 1. Import the include() function: from django.urls import include, path
- 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
-"""
from django.contrib import admin
-from django.urls import path
+from django.urls import path, include
+from django.conf import settings
+from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
+ path('api/', include('api.urls')),
]
+
+if settings.DEBUG:
+ urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
\ No newline at end of file
diff --git a/backend/base/wsgi.py b/backend/base/wsgi.py
index 70ae90a..f846bd0 100644
--- a/backend/base/wsgi.py
+++ b/backend/base/wsgi.py
@@ -1,12 +1,3 @@
-"""
-WSGI config for base project.
-
-It exposes the WSGI callable as a module-level variable named ``application``.
-
-For more information on this file, see
-https://docs.djangoproject.com/en/5.2/howto/deployment/wsgi/
-"""
-
import os
from django.core.wsgi import get_wsgi_application
diff --git a/backend/requirements.txt b/backend/requirements.txt
index 2758db7..fb476c5 100644
--- a/backend/requirements.txt
+++ b/backend/requirements.txt
@@ -1,15 +1,23 @@
asgiref==3.9.1
+certifi==2025.8.3
+charset-normalizer==3.4.3
Django==5.2.5
django-cors-headers==4.7.0
djangorestframework==3.16.1
+djangorestframework_simplejwt==5.5.1
dotenv==0.9.9
+idna==3.10
iniconfig==2.1.0
packaging==25.0
pluggy==1.6.0
psycopg2-binary==2.9.10
pycodestyle==2.14.0
Pygments==2.19.2
+PyJWT==2.10.1
pytest==8.4.1
pytest-django==4.11.1
python-dotenv==1.1.1
+requests==2.32.5
sqlparse==0.5.3
+urllib3==2.5.0
+drf-spectacular==0.27.1
diff --git a/backend/sitemanagement/constants/account_types.py b/backend/sitemanagement/constants/account_types.py
new file mode 100644
index 0000000..239bd10
--- /dev/null
+++ b/backend/sitemanagement/constants/account_types.py
@@ -0,0 +1,19 @@
+from typing import Literal, Tuple, List
+from enum import Enum
+
+class AccountType(str, Enum):
+ ENGINEER = "engineer"
+ OPERATOR = "operator"
+ ADMIN = "admin"
+
+ @classmethod
+ def choices(cls) -> List[Tuple[str, str]]:
+ return [
+ (cls.ENGINEER.value, "Инженер"),
+ (cls.OPERATOR.value, "Оператор"),
+ (cls.ADMIN.value, "Администратор"),
+ ]
+
+AccountTypeLiteral = Literal["engineer", "operator", "admin"]
+
+account_types = AccountType.choices()
\ No newline at end of file
diff --git a/frontend/.prettierignore b/frontend/.prettierignore
new file mode 100644
index 0000000..200c99d
--- /dev/null
+++ b/frontend/.prettierignore
@@ -0,0 +1,9 @@
+node_modules
+.next
+dist
+out
+coverage
+public
+.vscode
+*.log
+*.lock
\ No newline at end of file
diff --git a/frontend/.prettierrc b/frontend/.prettierrc
new file mode 100644
index 0000000..4ac25a5
--- /dev/null
+++ b/frontend/.prettierrc
@@ -0,0 +1,9 @@
+{
+ "semi": false,
+ "singleQuote": true,
+ "trailingComma": "es5",
+ "printWidth": 100,
+ "tabWidth": 2,
+ "arrowParens": "avoid",
+ "plugins": ["prettier-plugin-tailwindcss"]
+}
diff --git a/frontend/app/(auth)/login/page.tsx b/frontend/app/(auth)/login/page.tsx
new file mode 100644
index 0000000..1621d08
--- /dev/null
+++ b/frontend/app/(auth)/login/page.tsx
@@ -0,0 +1,7 @@
+import React from 'react'
+
+const LoginPage = () => {
+ return
LoginPage
+}
+
+export default LoginPage
diff --git a/frontend/app/(protected)/dashboard/page.tsx b/frontend/app/(protected)/dashboard/page.tsx
new file mode 100644
index 0000000..983ebb8
--- /dev/null
+++ b/frontend/app/(protected)/dashboard/page.tsx
@@ -0,0 +1,9 @@
+import React from 'react'
+
+const page = () => {
+ return (
+ page
+ )
+}
+
+export default page
\ No newline at end of file
diff --git a/frontend/app/(protected)/model/page.tsx b/frontend/app/(protected)/model/page.tsx
new file mode 100644
index 0000000..17a6231
--- /dev/null
+++ b/frontend/app/(protected)/model/page.tsx
@@ -0,0 +1,60 @@
+'use client'
+
+import React, { useState } from 'react'
+import ModelViewer from '@/components/ModelViewer'
+
+export default function Home() {
+ const [modelInfo, setModelInfo] = useState<{
+ meshes: unknown[]
+ boundingBox: {
+ min: { x: number; y: number; z: number }
+ max: { x: number; y: number; z: number }
+ }
+ } | null>(null)
+ const [error, setError] = useState(null)
+
+ const handleModelLoaded = (data: {
+ meshes: unknown[]
+ boundingBox: {
+ min: { x: number; y: number; z: number }
+ max: { x: number; y: number; z: number }
+ }
+ }) => {
+ setModelInfo(data)
+ setError(null)
+ console.log('Model loaded successfully:', data)
+ }
+
+ const handleError = (errorMessage: string) => {
+ setError(errorMessage)
+ setModelInfo(null)
+ }
+
+ return (
+
+
+
+ {error && (
+
+ Error: {error}
+
+ )}
+
+ {modelInfo && (
+
+
EXPO Building Model
+
+
+
🖱️ Left click + drag: Rotate
+
🖱️ Right click + drag: Pan
+
🖱️ Scroll: Zoom in/out
+
+
+ )}
+
+ )
+}
diff --git a/frontend/app/(protected)/objects/page.tsx b/frontend/app/(protected)/objects/page.tsx
new file mode 100644
index 0000000..983ebb8
--- /dev/null
+++ b/frontend/app/(protected)/objects/page.tsx
@@ -0,0 +1,9 @@
+import React from 'react'
+
+const page = () => {
+ return (
+ page
+ )
+}
+
+export default page
\ No newline at end of file
diff --git a/frontend/app/default.ts b/frontend/app/default.ts
new file mode 100644
index 0000000..8bc948e
--- /dev/null
+++ b/frontend/app/default.ts
@@ -0,0 +1 @@
+export { redirect as default } from 'next/navigation'
diff --git a/frontend/app/hooks/useClientFetch.ts b/frontend/app/hooks/useClientFetch.ts
new file mode 100644
index 0000000..ebca539
--- /dev/null
+++ b/frontend/app/hooks/useClientFetch.ts
@@ -0,0 +1,131 @@
+import { useQuery, useMutation, UseQueryOptions, UseMutationOptions } from '@tanstack/react-query'
+import axios, { AxiosError, AxiosRequestConfig } from 'axios'
+
+type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
+
+interface FetchOptions {
+ method?: HttpMethod
+ config?: AxiosRequestConfig
+ queryOptions?: Omit, 'queryKey' | 'queryFn'>
+ mutationOptions?: Omit, 'mutationFn'>
+}
+
+interface QueryResult {
+ data: TData | undefined
+ isLoading: boolean
+ error: TError | null
+ refetch: () => Promise
+}
+
+interface MutationResult {
+ mutate: (variables: TVariables) => void
+ isLoading: boolean
+ error: TError | null
+ data: TData | undefined
+}
+
+export function useClientFetch(
+ url: string,
+ options: FetchOptions = {}
+): TVariables extends void
+ ? QueryResult
+ : MutationResult {
+ const { method = 'GET', config = {}, queryOptions = {}, mutationOptions = {} } = options
+
+ const API_URL = process.env.NEXT_PUBLIC_API_URL
+ const fullUrl = url.startsWith('http') ? url : `${API_URL}${url}`
+
+ // всегда вызываем оба хука
+ const query = useQuery({
+ queryKey: [url, config.params],
+ queryFn: async () => {
+ const response = await axios.get(fullUrl, config)
+ return response.data
+ },
+ ...queryOptions,
+ // отключаем автоматическое выполнение для мутаций
+ enabled: method === 'GET' && queryOptions.enabled !== false,
+ })
+
+ const mutation = useMutation({
+ mutationFn: async (variables: TVariables) => {
+ const response = await axios({
+ method: method.toLowerCase(),
+ url: fullUrl,
+ data: variables,
+ ...config,
+ })
+ return response.data
+ },
+ ...mutationOptions,
+ })
+
+ // возвращаем соответствующий результат в зависимости от метода
+ if (method === 'GET') {
+ return {
+ data: query.data,
+ isLoading: query.isLoading,
+ error: query.error,
+ refetch: query.refetch,
+ } as TVariables extends void ? QueryResult : never
+ }
+
+ return {
+ mutate: mutation.mutate,
+ isLoading: mutation.isPending,
+ error: mutation.error,
+ data: mutation.data,
+ } as TVariables extends void ? never : MutationResult
+}
+
+// примеры использования:
+
+/*
+// GET запрос
+interface UserData {
+ id: number
+ name: string
+ email: string
+}
+
+const { data, isLoading, error } = useClientFetch('/users/me')
+
+// POST запрос с типизированным payload
+interface LoginPayload {
+ email: string
+ password: string
+}
+
+interface LoginResponse {
+ token: string
+ user: UserData
+}
+
+const { mutate, isLoading } = useClientFetch('/auth/login', {
+ method: 'POST',
+ mutationOptions: {
+ onSuccess: (data) => {
+ // data типизирован как LoginResponse
+ },
+ onError: (error) => {
+ // error типизирован как AxiosError
+ }
+ }
+})
+
+// использование:
+mutate({ email: 'user@example.com', password: '123456' })
+
+// PATCH запрос
+interface UpdateUserPayload {
+ name?: string
+ email?: string
+}
+
+const { mutate } = useClientFetch('/users/me', {
+ method: 'PATCH'
+})
+
+// использование:
+mutate({ name: 'New Name' })
+*/
diff --git a/frontend/app/hooks/useForm.ts b/frontend/app/hooks/useForm.ts
new file mode 100644
index 0000000..5a0d752
--- /dev/null
+++ b/frontend/app/hooks/useForm.ts
@@ -0,0 +1,150 @@
+'use client'
+
+import { useState, ChangeEvent, FormEvent } from 'react'
+import toast from 'react-hot-toast'
+import { ValidationRules, ValidationErrors } from '../types'
+
+type FormValue = string | number | boolean | string[] | number[] | null
+
+type CustomChangeEvent = {
+ target: {
+ id: string
+ value: FormValue
+ type?: string
+ checked?: boolean
+ }
+}
+
+export function useForm>(
+ initialValues: T,
+ validationRules?: { [K in keyof T]?: ValidationRules },
+ onSubmit?: (values: T) => void
+) {
+ const [values, setValues] = useState(initialValues)
+ const [errors, setErrors] = useState({})
+ const [isVisible, setIsVisible] = useState(false)
+
+ const handleChange = (
+ e: ChangeEvent | CustomChangeEvent
+ ) => {
+ const { id, value, type } = e.target
+ const isCheckbox = type === 'checkbox' && 'checked' in e.target
+
+ setValues(prev => ({
+ ...prev,
+ [id]: isCheckbox ? (e.target as HTMLInputElement).checked : value,
+ }))
+
+ // скидываем ошибки
+ if (errors[id]) {
+ setErrors(prev => {
+ const newErrors = { ...prev }
+ delete newErrors[id]
+ return newErrors
+ })
+ }
+ }
+
+ const resetField = (fieldName: keyof T, value: FormValue = '') => {
+ setValues(prev => ({
+ ...prev,
+ [fieldName]: value,
+ }))
+
+ // очищаем ошибки для этого поля, если они есть
+ if (errors[fieldName as string]) {
+ setErrors(prev => {
+ const newErrors = { ...prev }
+ delete newErrors[fieldName as string]
+ return newErrors
+ })
+ }
+ }
+
+ const fieldNames: { [key: string]: string } = {
+ login: 'Логин',
+ password: 'Пароль',
+ accountType: 'Роль',
+ }
+
+ const validate = () => {
+ if (!validationRules) return true
+
+ const newErrors: ValidationErrors = {}
+
+ Object.keys(validationRules).forEach(key => {
+ const value = values[key]
+ const rules = validationRules[key as keyof T]
+
+ if (rules?.required && !value) {
+ newErrors[key] = 'Это поле обязательно'
+ toast.error(`Поле "${fieldNames[key] || key}" обязательно для заполнения`, {
+ duration: 2000,
+ position: 'top-right',
+ style: {
+ background: '#FEE2E2',
+ color: '#991B1B',
+ padding: '16px',
+ borderRadius: '8px',
+ },
+ })
+ }
+
+ if (rules?.minLength && typeof value === 'string' && value.length < rules.minLength) {
+ newErrors[key] = `Минимальная длина ${rules.minLength} символов`
+ toast.error(
+ `Минимальная длина поля "${fieldNames[key] || key}" - ${rules.minLength} символов`,
+ {
+ duration: 2000,
+ position: 'top-right',
+ style: {
+ background: '#FEE2E2',
+ color: '#991B1B',
+ padding: '16px',
+ borderRadius: '8px',
+ },
+ }
+ )
+ }
+
+ if (rules?.pattern && typeof value === 'string' && !rules.pattern.test(value)) {
+ newErrors[key] = 'Неверный формат'
+ toast.error(`Поле "${fieldNames[key] || key}" заполнено некорректно`, {
+ duration: 2000,
+ position: 'top-right',
+ style: {
+ background: '#FEE2E2',
+ color: '#991B1B',
+ padding: '16px',
+ borderRadius: '8px',
+ },
+ })
+ }
+ })
+
+ setErrors(newErrors)
+ return Object.keys(newErrors).length === 0
+ }
+
+ const handleSubmit = (e: FormEvent) => {
+ e.preventDefault()
+ if (validate() && onSubmit) {
+ onSubmit(values)
+ }
+ }
+
+ const togglePasswordVisibility = () => {
+ setIsVisible(!isVisible)
+ }
+
+ return {
+ values,
+ errors,
+ isVisible,
+ setValues,
+ handleChange,
+ handleSubmit,
+ togglePasswordVisibility,
+ resetField,
+ }
+}
diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx
index 00f5bb1..0a35558 100644
--- a/frontend/app/page.tsx
+++ b/frontend/app/page.tsx
@@ -1,60 +1,5 @@
-'use client'
-
-import React, { useState } from 'react'
-import ModelViewer from '../components/ModelViewer'
+import { redirect } from 'next/navigation'
export default function Home() {
- const [modelInfo, setModelInfo] = useState<{
- meshes: unknown[]
- boundingBox: {
- min: { x: number; y: number; z: number }
- max: { x: number; y: number; z: number }
- }
- } | null>(null)
- const [error, setError] = useState(null)
-
- const handleModelLoaded = (data: {
- meshes: unknown[]
- boundingBox: {
- min: { x: number; y: number; z: number }
- max: { x: number; y: number; z: number }
- }
- }) => {
- setModelInfo(data)
- setError(null)
- console.log('Model loaded successfully:', data)
- }
-
- const handleError = (errorMessage: string) => {
- setError(errorMessage)
- setModelInfo(null)
- }
-
- return (
-
-
-
- {error && (
-
- Error: {error}
-
- )}
-
- {modelInfo && (
-
-
EXPO Building Model
-
-
-
🖱️ Left click + drag: Rotate
-
🖱️ Right click + drag: Pan
-
🖱️ Scroll: Zoom in/out
-
-
- )}
-
- )
+ redirect('/objects')
}
diff --git a/frontend/app/store/userStore.ts b/frontend/app/store/userStore.ts
new file mode 100644
index 0000000..bbaec70
--- /dev/null
+++ b/frontend/app/store/userStore.ts
@@ -0,0 +1,47 @@
+import { create } from 'zustand'
+import { persist } from 'zustand/middleware'
+import { User, UserState } from '../types'
+
+interface UserStore extends UserState {
+ // состояние
+ isAuthenticated: boolean
+ user: User | null
+
+ // действия
+ setUser: (user: User | null) => void
+ setAuthenticated: (isAuthenticated: boolean) => void
+ logout: () => void
+
+ // асинхронные действия
+ //! что пользователь может делать асинхронно?
+}
+
+const useUserStore = create()(
+ persist(
+ set => ({
+ // начальное состояние
+ isAuthenticated: false,
+ user: null,
+ favorites: [],
+
+ // синхронные действия
+ setUser: user => set({ user }),
+ setAuthenticated: isAuthenticated => set({ isAuthenticated }),
+ logout: () =>
+ set({
+ isAuthenticated: false,
+ user: null,
+ }),
+ }),
+
+ //! асинхронщина?
+ { name: 'user-store' }
+ )
+)
+
+export default useUserStore
+
+// пример использования
+// const { user, isAuthenticated } = useUserStore() -- получаем данные из стора
+// const { setUser, setAuthenticated } = useUserStore() -- устанавливаем данные в стор
+// const { logout } = useUserStore() -- выходим из пользовательского аккаунта
diff --git a/frontend/app/types/index.ts b/frontend/app/types/index.ts
new file mode 100644
index 0000000..a5cf347
--- /dev/null
+++ b/frontend/app/types/index.ts
@@ -0,0 +1,34 @@
+export interface ValidationRules {
+ required?: boolean
+ minLength?: number
+ pattern?: RegExp
+}
+
+export interface ValidationErrors {
+ [key: string]: string
+}
+
+export type ToastProps = {
+ type: 'error' | 'success' | 'loading'
+ message: string
+ action?: {
+ text: string
+ onClick: () => void
+ }
+ duration?: number
+}
+
+export interface User {
+ id?: number | undefined
+ name: string
+ surname: string
+ image?: string
+ email: string
+ account_type?: string
+ login: string
+}
+
+export interface UserState {
+ isAuthenticated: boolean
+ user: User | null
+}
diff --git a/frontend/components/ui/Selector.tsx b/frontend/components/ui/Selector.tsx
new file mode 100644
index 0000000..a0d3e22
--- /dev/null
+++ b/frontend/components/ui/Selector.tsx
@@ -0,0 +1,9 @@
+import React from 'react'
+
+const Selector = () => {
+ return (
+ Selector
+ )
+}
+
+export default Selector
\ No newline at end of file
diff --git a/frontend/components/ui/ShowToast.tsx b/frontend/components/ui/ShowToast.tsx
new file mode 100644
index 0000000..7d16387
--- /dev/null
+++ b/frontend/components/ui/ShowToast.tsx
@@ -0,0 +1,59 @@
+import toast from 'react-hot-toast'
+import { ToastProps } from '@/app/types'
+
+const toastStyles = {
+ success: {
+ background: 'bg-green-50',
+ text: 'text-green-800',
+ border: 'border-green-200',
+ },
+ error: {
+ background: 'bg-red-50',
+ text: 'text-red-800',
+ border: 'border-red-200',
+ },
+ loading: {
+ background: 'bg-blue-50',
+ text: 'text-blue-800',
+ border: 'border-blue-200',
+ },
+}
+
+const showToast = ({ type, message, action, duration }: ToastProps) => {
+ const styles = toastStyles[type]
+
+ toast.custom(
+ t => (
+
+
+
+
+
{message}
+ {action && (
+
+
+
+ )}
+
+
+
+
+ ),
+ { duration: duration || 4000 }
+ )
+}
+
+export default showToast
+
+//пример использования: showToast({ type: 'error', message: 'Неверный email или пароль' })
diff --git a/frontend/components/ui/TextInput.tsx b/frontend/components/ui/TextInput.tsx
new file mode 100644
index 0000000..eed2790
--- /dev/null
+++ b/frontend/components/ui/TextInput.tsx
@@ -0,0 +1,9 @@
+import React from 'react'
+
+const TextInput = () => {
+ return (
+ TextInput
+ )
+}
+
+export default TextInput
\ No newline at end of file
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 2a52ec9..d6edc9e 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -10,9 +10,15 @@
"dependencies": {
"@babylonjs/core": "^6.44.0",
"@babylonjs/loaders": "^6.49.0",
+ "@tanstack/react-query": "^5.85.5",
+ "axios": "^1.11.0",
"next": "^15.4.3",
+ "next-auth": "^4.24.11",
"react": "19.1.0",
- "react-dom": "19.1.0"
+ "react-dom": "19.1.0",
+ "react-hot-toast": "^2.6.0",
+ "react-select": "^5.10.2",
+ "zustand": "^5.0.8"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
@@ -22,6 +28,8 @@
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "15.4.3",
+ "prettier": "^3.6.2",
+ "prettier-plugin-tailwindcss": "^0.6.14",
"tailwindcss": "^4",
"typescript": "^5"
}
@@ -39,6 +47,145 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/@babel/code-frame": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
+ "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz",
+ "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.28.3",
+ "@babel/types": "^7.28.2",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
+ "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
+ "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz",
+ "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.2"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz",
+ "integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
+ "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/parser": "^7.27.2",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz",
+ "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.3",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.28.3",
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.2",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.28.2",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz",
+ "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@babylonjs/core": {
"version": "6.49.0",
"resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-6.49.0.tgz",
@@ -56,21 +203,21 @@
}
},
"node_modules/@emnapi/core": {
- "version": "1.4.5",
- "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz",
- "integrity": "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==",
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.5.0.tgz",
+ "integrity": "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
- "@emnapi/wasi-threads": "1.0.4",
+ "@emnapi/wasi-threads": "1.1.0",
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/runtime": {
- "version": "1.4.5",
- "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz",
- "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==",
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz",
+ "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==",
"license": "MIT",
"optional": true,
"dependencies": {
@@ -78,9 +225,9 @@
}
},
"node_modules/@emnapi/wasi-threads": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.4.tgz",
- "integrity": "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz",
+ "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -88,6 +235,120 @@
"tslib": "^2.4.0"
}
},
+ "node_modules/@emotion/babel-plugin": {
+ "version": "11.13.5",
+ "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
+ "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.16.7",
+ "@babel/runtime": "^7.18.3",
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/serialize": "^1.3.3",
+ "babel-plugin-macros": "^3.1.0",
+ "convert-source-map": "^1.5.0",
+ "escape-string-regexp": "^4.0.0",
+ "find-root": "^1.1.0",
+ "source-map": "^0.5.7",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/cache": {
+ "version": "11.14.0",
+ "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz",
+ "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/sheet": "^1.4.0",
+ "@emotion/utils": "^1.4.2",
+ "@emotion/weak-memoize": "^0.4.0",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/hash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
+ "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/memoize": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
+ "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/react": {
+ "version": "11.14.0",
+ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
+ "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.13.5",
+ "@emotion/cache": "^11.14.0",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
+ "@emotion/utils": "^1.4.2",
+ "@emotion/weak-memoize": "^0.4.0",
+ "hoist-non-react-statics": "^3.3.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emotion/serialize": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
+ "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/unitless": "^0.10.0",
+ "@emotion/utils": "^1.4.2",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@emotion/sheet": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
+ "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/unitless": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
+ "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/use-insertion-effect-with-fallbacks": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz",
+ "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@emotion/utils": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
+ "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/weak-memoize": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
+ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
+ "license": "MIT"
+ },
"node_modules/@eslint-community/eslint-utils": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
@@ -229,6 +490,31 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
+ "node_modules/@floating-ui/core": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz",
+ "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/utils": "^0.2.10"
+ }
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz",
+ "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/core": "^1.7.3",
+ "@floating-ui/utils": "^0.2.10"
+ }
+ },
+ "node_modules/@floating-ui/utils": {
+ "version": "0.2.10",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
+ "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
+ "license": "MIT"
+ },
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@@ -730,7 +1016,6 @@
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0",
@@ -752,7 +1037,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
@@ -762,14 +1046,12 @@
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
- "dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.30",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz",
"integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
@@ -790,9 +1072,9 @@
}
},
"node_modules/@next/env": {
- "version": "15.5.0",
- "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.0.tgz",
- "integrity": "sha512-sDaprBAfzCQiOgo2pO+LhnV0Wt2wBgartjrr+dpcTORYVnnXD0gwhHhiiyIih9hQbq+JnbqH4odgcFWhqCGidw==",
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.2.tgz",
+ "integrity": "sha512-Qe06ew4zt12LeO6N7j8/nULSOe3fMXE4dM6xgpBQNvdzyK1sv5y4oAP3bq4LamrvGCZtmRYnW8URFCeX5nFgGg==",
"license": "MIT"
},
"node_modules/@next/eslint-plugin-next": {
@@ -806,9 +1088,9 @@
}
},
"node_modules/@next/swc-darwin-arm64": {
- "version": "15.5.0",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.0.tgz",
- "integrity": "sha512-v7Jj9iqC6enxIRBIScD/o0lH7QKvSxq2LM8UTyqJi+S2w2QzhMYjven4vgu/RzgsdtdbpkyCxBTzHl/gN5rTRg==",
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.2.tgz",
+ "integrity": "sha512-8bGt577BXGSd4iqFygmzIfTYizHb0LGWqH+qgIF/2EDxS5JsSdERJKA8WgwDyNBZgTIIA4D8qUtoQHmxIIquoQ==",
"cpu": [
"arm64"
],
@@ -822,9 +1104,9 @@
}
},
"node_modules/@next/swc-darwin-x64": {
- "version": "15.5.0",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.0.tgz",
- "integrity": "sha512-s2Nk6ec+pmYmAb/utawuURy7uvyYKDk+TRE5aqLRsdnj3AhwC9IKUBmhfnLmY/+P+DnwqpeXEFIKe9tlG0p6CA==",
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.2.tgz",
+ "integrity": "sha512-2DjnmR6JHK4X+dgTXt5/sOCu/7yPtqpYt8s8hLkHFK3MGkka2snTv3yRMdHvuRtJVkPwCGsvBSwmoQCHatauFQ==",
"cpu": [
"x64"
],
@@ -838,9 +1120,9 @@
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
- "version": "15.5.0",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.0.tgz",
- "integrity": "sha512-mGlPJMZReU4yP5fSHjOxiTYvZmwPSWn/eF/dcg21pwfmiUCKS1amFvf1F1RkLHPIMPfocxLViNWFvkvDB14Isg==",
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.2.tgz",
+ "integrity": "sha512-3j7SWDBS2Wov/L9q0mFJtEvQ5miIqfO4l7d2m9Mo06ddsgUK8gWfHGgbjdFlCp2Ek7MmMQZSxpGFqcC8zGh2AA==",
"cpu": [
"arm64"
],
@@ -854,9 +1136,9 @@
}
},
"node_modules/@next/swc-linux-arm64-musl": {
- "version": "15.5.0",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.0.tgz",
- "integrity": "sha512-biWqIOE17OW/6S34t1X8K/3vb1+svp5ji5QQT/IKR+VfM3B7GvlCwmz5XtlEan2ukOUf9tj2vJJBffaGH4fGRw==",
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.2.tgz",
+ "integrity": "sha512-s6N8k8dF9YGc5T01UPQ08yxsK6fUow5gG1/axWc1HVVBYQBgOjca4oUZF7s4p+kwhkB1bDSGR8QznWrFZ/Rt5g==",
"cpu": [
"arm64"
],
@@ -870,9 +1152,9 @@
}
},
"node_modules/@next/swc-linux-x64-gnu": {
- "version": "15.5.0",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.0.tgz",
- "integrity": "sha512-zPisT+obYypM/l6EZ0yRkK3LEuoZqHaSoYKj+5jiD9ESHwdr6QhnabnNxYkdy34uCigNlWIaCbjFmQ8FY5AlxA==",
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.2.tgz",
+ "integrity": "sha512-o1RV/KOODQh6dM6ZRJGZbc+MOAHww33Vbs5JC9Mp1gDk8cpEO+cYC/l7rweiEalkSm5/1WGa4zY7xrNwObN4+Q==",
"cpu": [
"x64"
],
@@ -886,9 +1168,9 @@
}
},
"node_modules/@next/swc-linux-x64-musl": {
- "version": "15.5.0",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.0.tgz",
- "integrity": "sha512-+t3+7GoU9IYmk+N+FHKBNFdahaReoAktdOpXHFIPOU1ixxtdge26NgQEEkJkCw2dHT9UwwK5zw4mAsURw4E8jA==",
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.2.tgz",
+ "integrity": "sha512-/VUnh7w8RElYZ0IV83nUcP/J4KJ6LLYliiBIri3p3aW2giF+PAVgZb6mk8jbQSB3WlTai8gEmCAr7kptFa1H6g==",
"cpu": [
"x64"
],
@@ -902,9 +1184,9 @@
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
- "version": "15.5.0",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.0.tgz",
- "integrity": "sha512-d8MrXKh0A+c9DLiy1BUFwtg3Hu90Lucj3k6iKTUdPOv42Ve2UiIG8HYi3UAb8kFVluXxEfdpCoPPCSODk5fDcw==",
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.2.tgz",
+ "integrity": "sha512-sMPyTvRcNKXseNQ/7qRfVRLa0VhR0esmQ29DD6pqvG71+JdVnESJaHPA8t7bc67KD5spP3+DOCNLhqlEI2ZgQg==",
"cpu": [
"arm64"
],
@@ -918,9 +1200,9 @@
}
},
"node_modules/@next/swc-win32-x64-msvc": {
- "version": "15.5.0",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.0.tgz",
- "integrity": "sha512-Fe1tGHxOWEyQjmygWkkXSwhFcTJuimrNu52JEuwItrKJVV4iRjbWp9I7zZjwqtiNnQmxoEvoisn8wueFLrNpvQ==",
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.2.tgz",
+ "integrity": "sha512-W5VvyZHnxG/2ukhZF/9Ikdra5fdNftxI6ybeVKYvBPDtyx7x4jPPSNduUkfH5fo3zG0JQ0bPxgy41af2JX5D4Q==",
"cpu": [
"x64"
],
@@ -981,6 +1263,15 @@
"node": ">=12.4.0"
}
},
+ "node_modules/@panva/hkdf": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz",
+ "integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
"node_modules/@rtsao/scc": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@@ -1280,6 +1571,32 @@
"tailwindcss": "4.1.12"
}
},
+ "node_modules/@tanstack/query-core": {
+ "version": "5.85.5",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.85.5.tgz",
+ "integrity": "sha512-KO0WTob4JEApv69iYp1eGvfMSUkgw//IpMnq+//cORBzXf0smyRwPLrUvEe5qtAEGjwZTXrjxg+oJNP/C00t6w==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/react-query": {
+ "version": "5.85.5",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.85.5.tgz",
+ "integrity": "sha512-/X4EFNcnPiSs8wM2v+b6DqS5mmGeuJQvxBglmDxl6ZQb5V26ouD2SJYAcC3VjbNwqhY2zjxVD15rDA5nGbMn3A==",
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/query-core": "5.85.5"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": "^18 || ^19"
+ }
+ },
"node_modules/@tybys/wasm-util": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.0.tgz",
@@ -1322,38 +1639,52 @@
"undici-types": "~6.21.0"
}
},
+ "node_modules/@types/parse-json": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
+ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
+ "license": "MIT"
+ },
"node_modules/@types/react": {
- "version": "19.1.11",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.11.tgz",
- "integrity": "sha512-lr3jdBw/BGj49Eps7EvqlUaoeA0xpj3pc0RoJkHpYaCHkVK7i28dKyImLQb3JVlqs3aYSXf7qYuWOW/fgZnTXQ==",
- "dev": true,
+ "version": "19.1.12",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.12.tgz",
+ "integrity": "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==",
"license": "MIT",
"dependencies": {
"csstype": "^3.0.2"
}
},
"node_modules/@types/react-dom": {
- "version": "19.1.7",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.7.tgz",
- "integrity": "sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==",
+ "version": "19.1.9",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz",
+ "integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"@types/react": "^19.0.0"
}
},
+ "node_modules/@types/react-transition-group": {
+ "version": "4.4.12",
+ "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
+ "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*"
+ }
+ },
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.40.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.40.0.tgz",
- "integrity": "sha512-w/EboPlBwnmOBtRbiOvzjD+wdiZdgFeo17lkltrtn7X37vagKKWJABvyfsJXTlHe6XBzugmYgd4A4nW+k8Mixw==",
+ "version": "8.41.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.41.0.tgz",
+ "integrity": "sha512-8fz6oa6wEKZrhXWro/S3n2eRJqlRcIa6SlDh59FXJ5Wp5XRZ8B9ixpJDcjadHq47hMx0u+HW6SNa6LjJQ6NLtw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.40.0",
- "@typescript-eslint/type-utils": "8.40.0",
- "@typescript-eslint/utils": "8.40.0",
- "@typescript-eslint/visitor-keys": "8.40.0",
+ "@typescript-eslint/scope-manager": "8.41.0",
+ "@typescript-eslint/type-utils": "8.41.0",
+ "@typescript-eslint/utils": "8.41.0",
+ "@typescript-eslint/visitor-keys": "8.41.0",
"graphemer": "^1.4.0",
"ignore": "^7.0.0",
"natural-compare": "^1.4.0",
@@ -1367,7 +1698,7 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "@typescript-eslint/parser": "^8.40.0",
+ "@typescript-eslint/parser": "^8.41.0",
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
@@ -1383,16 +1714,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "8.40.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.40.0.tgz",
- "integrity": "sha512-jCNyAuXx8dr5KJMkecGmZ8KI61KBUhkCob+SD+C+I5+Y1FWI2Y3QmY4/cxMCC5WAsZqoEtEETVhUiUMIGCf6Bw==",
+ "version": "8.41.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.41.0.tgz",
+ "integrity": "sha512-gTtSdWX9xiMPA/7MV9STjJOOYtWwIJIYxkQxnSV1U3xcE+mnJSH3f6zI0RYP+ew66WSlZ5ed+h0VCxsvdC1jJg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/scope-manager": "8.40.0",
- "@typescript-eslint/types": "8.40.0",
- "@typescript-eslint/typescript-estree": "8.40.0",
- "@typescript-eslint/visitor-keys": "8.40.0",
+ "@typescript-eslint/scope-manager": "8.41.0",
+ "@typescript-eslint/types": "8.41.0",
+ "@typescript-eslint/typescript-estree": "8.41.0",
+ "@typescript-eslint/visitor-keys": "8.41.0",
"debug": "^4.3.4"
},
"engines": {
@@ -1408,14 +1739,14 @@
}
},
"node_modules/@typescript-eslint/project-service": {
- "version": "8.40.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.40.0.tgz",
- "integrity": "sha512-/A89vz7Wf5DEXsGVvcGdYKbVM9F7DyFXj52lNYUDS1L9yJfqjW/fIp5PgMuEJL/KeqVTe2QSbXAGUZljDUpArw==",
+ "version": "8.41.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.41.0.tgz",
+ "integrity": "sha512-b8V9SdGBQzQdjJ/IO3eDifGpDBJfvrNTp2QD9P2BeqWTGrRibgfgIlBSw6z3b6R7dPzg752tOs4u/7yCLxksSQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.40.0",
- "@typescript-eslint/types": "^8.40.0",
+ "@typescript-eslint/tsconfig-utils": "^8.41.0",
+ "@typescript-eslint/types": "^8.41.0",
"debug": "^4.3.4"
},
"engines": {
@@ -1430,14 +1761,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "8.40.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.40.0.tgz",
- "integrity": "sha512-y9ObStCcdCiZKzwqsE8CcpyuVMwRouJbbSrNuThDpv16dFAj429IkM6LNb1dZ2m7hK5fHyzNcErZf7CEeKXR4w==",
+ "version": "8.41.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.41.0.tgz",
+ "integrity": "sha512-n6m05bXn/Cd6DZDGyrpXrELCPVaTnLdPToyhBoFkLIMznRUQUEQdSp96s/pcWSQdqOhrgR1mzJ+yItK7T+WPMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.40.0",
- "@typescript-eslint/visitor-keys": "8.40.0"
+ "@typescript-eslint/types": "8.41.0",
+ "@typescript-eslint/visitor-keys": "8.41.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1448,9 +1779,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
- "version": "8.40.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.40.0.tgz",
- "integrity": "sha512-jtMytmUaG9d/9kqSl/W3E3xaWESo4hFDxAIHGVW/WKKtQhesnRIJSAJO6XckluuJ6KDB5woD1EiqknriCtAmcw==",
+ "version": "8.41.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.41.0.tgz",
+ "integrity": "sha512-TDhxYFPUYRFxFhuU5hTIJk+auzM/wKvWgoNYOPcOf6i4ReYlOoYN8q1dV5kOTjNQNJgzWN3TUUQMtlLOcUgdUw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1465,15 +1796,15 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
- "version": "8.40.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.40.0.tgz",
- "integrity": "sha512-eE60cK4KzAc6ZrzlJnflXdrMqOBaugeukWICO2rB0KNvwdIMaEaYiywwHMzA1qFpTxrLhN9Lp4E/00EgWcD3Ow==",
+ "version": "8.41.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.41.0.tgz",
+ "integrity": "sha512-63qt1h91vg3KsjVVonFJWjgSK7pZHSQFKH6uwqxAH9bBrsyRhO6ONoKyXxyVBzG1lJnFAJcKAcxLS54N1ee1OQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.40.0",
- "@typescript-eslint/typescript-estree": "8.40.0",
- "@typescript-eslint/utils": "8.40.0",
+ "@typescript-eslint/types": "8.41.0",
+ "@typescript-eslint/typescript-estree": "8.41.0",
+ "@typescript-eslint/utils": "8.41.0",
"debug": "^4.3.4",
"ts-api-utils": "^2.1.0"
},
@@ -1490,9 +1821,9 @@
}
},
"node_modules/@typescript-eslint/types": {
- "version": "8.40.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.40.0.tgz",
- "integrity": "sha512-ETdbFlgbAmXHyFPwqUIYrfc12ArvpBhEVgGAxVYSwli26dn8Ko+lIo4Su9vI9ykTZdJn+vJprs/0eZU0YMAEQg==",
+ "version": "8.41.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.41.0.tgz",
+ "integrity": "sha512-9EwxsWdVqh42afLbHP90n2VdHaWU/oWgbH2P0CfcNfdKL7CuKpwMQGjwev56vWu9cSKU7FWSu6r9zck6CVfnag==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1504,16 +1835,16 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.40.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.40.0.tgz",
- "integrity": "sha512-k1z9+GJReVVOkc1WfVKs1vBrR5MIKKbdAjDTPvIK3L8De6KbFfPFt6BKpdkdk7rZS2GtC/m6yI5MYX+UsuvVYQ==",
+ "version": "8.41.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.41.0.tgz",
+ "integrity": "sha512-D43UwUYJmGhuwHfY7MtNKRZMmfd8+p/eNSfFe6tH5mbVDto+VQCayeAt35rOx3Cs6wxD16DQtIKw/YXxt5E0UQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/project-service": "8.40.0",
- "@typescript-eslint/tsconfig-utils": "8.40.0",
- "@typescript-eslint/types": "8.40.0",
- "@typescript-eslint/visitor-keys": "8.40.0",
+ "@typescript-eslint/project-service": "8.41.0",
+ "@typescript-eslint/tsconfig-utils": "8.41.0",
+ "@typescript-eslint/types": "8.41.0",
+ "@typescript-eslint/visitor-keys": "8.41.0",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
@@ -1589,16 +1920,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "8.40.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.40.0.tgz",
- "integrity": "sha512-Cgzi2MXSZyAUOY+BFwGs17s7ad/7L+gKt6Y8rAVVWS+7o6wrjeFN4nVfTpbE25MNcxyJ+iYUXflbs2xR9h4UBg==",
+ "version": "8.41.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.41.0.tgz",
+ "integrity": "sha512-udbCVstxZ5jiPIXrdH+BZWnPatjlYwJuJkDA4Tbo3WyYLh8NvB+h/bKeSZHDOFKfphsZYJQqaFtLeXEqurQn1A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.7.0",
- "@typescript-eslint/scope-manager": "8.40.0",
- "@typescript-eslint/types": "8.40.0",
- "@typescript-eslint/typescript-estree": "8.40.0"
+ "@typescript-eslint/scope-manager": "8.41.0",
+ "@typescript-eslint/types": "8.41.0",
+ "@typescript-eslint/typescript-estree": "8.41.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1613,13 +1944,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.40.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.40.0.tgz",
- "integrity": "sha512-8CZ47QwalyRjsypfwnbI3hKy5gJDPmrkLjkgMxhi0+DZZ2QNx2naS6/hWoVYUHU7LU2zleF68V9miaVZvhFfTA==",
+ "version": "8.41.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.41.0.tgz",
+ "integrity": "sha512-+GeGMebMCy0elMNg67LRNoVnUFPIm37iu5CmHESVx56/9Jsfdpsvbv605DQ81Pi/x11IdKUsS5nzgTYbCQU9fg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.40.0",
+ "@typescript-eslint/types": "8.41.0",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
@@ -2149,6 +2480,12 @@
"node": ">= 0.4"
}
},
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "license": "MIT"
+ },
"node_modules/available-typed-arrays": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
@@ -2175,6 +2512,17 @@
"node": ">=4"
}
},
+ "node_modules/axios": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz",
+ "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==",
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.4",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
"node_modules/axobject-query": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
@@ -2185,6 +2533,21 @@
"node": ">= 0.4"
}
},
+ "node_modules/babel-plugin-macros": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
+ "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.12.5",
+ "cosmiconfig": "^7.0.0",
+ "resolve": "^1.19.0"
+ },
+ "engines": {
+ "node": ">=10",
+ "npm": ">=6"
+ }
+ },
"node_modules/babylonjs-gltf2interface": {
"version": "6.49.0",
"resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-6.49.0.tgz",
@@ -2246,7 +2609,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
@@ -2277,7 +2639,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -2381,6 +2742,18 @@
"simple-swizzle": "^0.2.2"
}
},
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -2388,6 +2761,37 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+ "license": "MIT"
+ },
+ "node_modules/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cosmiconfig": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
+ "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/parse-json": "^4.0.0",
+ "import-fresh": "^3.2.1",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0",
+ "yaml": "^1.10.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -2407,7 +2811,6 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
- "dev": true,
"license": "MIT"
},
"node_modules/damerau-levenshtein": {
@@ -2475,7 +2878,6 @@
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -2532,6 +2934,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/detect-libc": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
@@ -2555,11 +2966,20 @@
"node": ">=0.10.0"
}
},
+ "node_modules/dom-helpers": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+ "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.8.7",
+ "csstype": "^3.0.2"
+ }
+ },
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
- "dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
@@ -2591,6 +3011,15 @@
"node": ">=10.13.0"
}
},
+ "node_modules/error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
"node_modules/es-abstract": {
"version": "1.24.0",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz",
@@ -2664,7 +3093,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -2674,7 +3102,6 @@
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -2712,7 +3139,6 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
@@ -2725,7 +3151,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
@@ -2772,7 +3197,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -3294,6 +3718,12 @@
"node": ">=8"
}
},
+ "node_modules/find-root": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
+ "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
+ "license": "MIT"
+ },
"node_modules/find-up": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
@@ -3332,6 +3762,26 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/follow-redirects": {
+ "version": "1.15.11",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
+ "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
"node_modules/for-each": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
@@ -3348,11 +3798,26 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/form-data": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
+ "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -3393,7 +3858,6 @@
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
@@ -3418,7 +3882,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
- "dev": true,
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
@@ -3502,11 +3965,19 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/goober": {
+ "version": "2.1.16",
+ "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.16.tgz",
+ "integrity": "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==",
+ "license": "MIT",
+ "peerDependencies": {
+ "csstype": "^3.0.10"
+ }
+ },
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -3585,7 +4056,6 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -3598,7 +4068,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
@@ -3614,7 +4083,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
@@ -3623,6 +4091,15 @@
"node": ">= 0.4"
}
},
+ "node_modules/hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "react-is": "^16.7.0"
+ }
+ },
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -3637,7 +4114,6 @@
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"parent-module": "^1.0.0",
@@ -3694,11 +4170,10 @@
}
},
"node_modules/is-arrayish": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
- "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
- "license": "MIT",
- "optional": true
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "license": "MIT"
},
"node_modules/is-async-function": {
"version": "2.1.1",
@@ -3780,7 +4255,6 @@
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
- "dev": true,
"license": "MIT",
"dependencies": {
"hasown": "^2.0.2"
@@ -4125,11 +4599,19 @@
"jiti": "lib/jiti-cli.mjs"
}
},
+ "node_modules/jose": {
+ "version": "4.15.9",
+ "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz",
+ "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/js-yaml": {
@@ -4145,6 +4627,18 @@
"js-yaml": "bin/js-yaml.js"
}
},
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
@@ -4152,6 +4646,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "license": "MIT"
+ },
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
@@ -4478,6 +4978,12 @@
"url": "https://opencollective.com/parcel"
}
},
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "license": "MIT"
+ },
"node_modules/locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@@ -4505,7 +5011,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
- "dev": true,
"license": "MIT",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
@@ -4514,6 +5019,18 @@
"loose-envify": "cli.js"
}
},
+ "node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/magic-string": {
"version": "0.30.18",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz",
@@ -4528,12 +5045,17 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
+ "node_modules/memoize-one": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
+ "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==",
+ "license": "MIT"
+ },
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -4558,6 +5080,27 @@
"node": ">=8.6"
}
},
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -4624,7 +5167,6 @@
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true,
"license": "MIT"
},
"node_modules/nanoid": {
@@ -4669,12 +5211,12 @@
"license": "MIT"
},
"node_modules/next": {
- "version": "15.5.0",
- "resolved": "https://registry.npmjs.org/next/-/next-15.5.0.tgz",
- "integrity": "sha512-N1lp9Hatw3a9XLt0307lGB4uTKsXDhyOKQo7uYMzX4i0nF/c27grcGXkLdb7VcT8QPYLBa8ouIyEoUQJ2OyeNQ==",
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/next/-/next-15.5.2.tgz",
+ "integrity": "sha512-H8Otr7abj1glFhbGnvUt3gz++0AF1+QoCXEBmd/6aKbfdFwrn0LpA836Ed5+00va/7HQSDD+mOoVhn3tNy3e/Q==",
"license": "MIT",
"dependencies": {
- "@next/env": "15.5.0",
+ "@next/env": "15.5.2",
"@swc/helpers": "0.5.15",
"caniuse-lite": "^1.0.30001579",
"postcss": "8.4.31",
@@ -4687,14 +5229,14 @@
"node": "^18.18.0 || ^19.8.0 || >= 20.0.0"
},
"optionalDependencies": {
- "@next/swc-darwin-arm64": "15.5.0",
- "@next/swc-darwin-x64": "15.5.0",
- "@next/swc-linux-arm64-gnu": "15.5.0",
- "@next/swc-linux-arm64-musl": "15.5.0",
- "@next/swc-linux-x64-gnu": "15.5.0",
- "@next/swc-linux-x64-musl": "15.5.0",
- "@next/swc-win32-arm64-msvc": "15.5.0",
- "@next/swc-win32-x64-msvc": "15.5.0",
+ "@next/swc-darwin-arm64": "15.5.2",
+ "@next/swc-darwin-x64": "15.5.2",
+ "@next/swc-linux-arm64-gnu": "15.5.2",
+ "@next/swc-linux-arm64-musl": "15.5.2",
+ "@next/swc-linux-x64-gnu": "15.5.2",
+ "@next/swc-linux-x64-musl": "15.5.2",
+ "@next/swc-win32-arm64-msvc": "15.5.2",
+ "@next/swc-win32-x64-msvc": "15.5.2",
"sharp": "^0.34.3"
},
"peerDependencies": {
@@ -4720,6 +5262,38 @@
}
}
},
+ "node_modules/next-auth": {
+ "version": "4.24.11",
+ "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.11.tgz",
+ "integrity": "sha512-pCFXzIDQX7xmHFs4KVH4luCjaCbuPRtZ9oBUjUhOk84mZ9WVPf94n87TxYI4rSRf9HmfHEF8Yep3JrYDVOo3Cw==",
+ "license": "ISC",
+ "dependencies": {
+ "@babel/runtime": "^7.20.13",
+ "@panva/hkdf": "^1.0.2",
+ "cookie": "^0.7.0",
+ "jose": "^4.15.5",
+ "oauth": "^0.9.15",
+ "openid-client": "^5.4.0",
+ "preact": "^10.6.3",
+ "preact-render-to-string": "^5.1.19",
+ "uuid": "^8.3.2"
+ },
+ "peerDependencies": {
+ "@auth/core": "0.34.2",
+ "next": "^12.2.5 || ^13 || ^14 || ^15",
+ "nodemailer": "^6.6.5",
+ "react": "^17.0.2 || ^18 || ^19",
+ "react-dom": "^17.0.2 || ^18 || ^19"
+ },
+ "peerDependenciesMeta": {
+ "@auth/core": {
+ "optional": true
+ },
+ "nodemailer": {
+ "optional": true
+ }
+ }
+ },
"node_modules/next/node_modules/postcss": {
"version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
@@ -4748,16 +5322,30 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/oauth": {
+ "version": "0.9.15",
+ "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
+ "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==",
+ "license": "MIT"
+ },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
+ "node_modules/object-hash": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
+ "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/object-inspect": {
"version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
@@ -4871,6 +5459,30 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/oidc-token-hash": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.1.1.tgz",
+ "integrity": "sha512-D7EmwxJV6DsEB6vOFLrBM2OzsVgQzgPWyHlV2OOAVj772n+WTXpudC9e9u5BVKQnYwaD30Ivhi9b+4UeBcGu9g==",
+ "license": "MIT",
+ "engines": {
+ "node": "^10.13.0 || >=12.0.0"
+ }
+ },
+ "node_modules/openid-client": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz",
+ "integrity": "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==",
+ "license": "MIT",
+ "dependencies": {
+ "jose": "^4.15.9",
+ "lru-cache": "^6.0.0",
+ "object-hash": "^2.2.0",
+ "oidc-token-hash": "^5.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -4943,7 +5555,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
- "dev": true,
"license": "MIT",
"dependencies": {
"callsites": "^3.0.0"
@@ -4952,6 +5563,24 @@
"node": ">=6"
}
},
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -4976,9 +5605,17 @@
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
- "dev": true,
"license": "MIT"
},
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -5037,6 +5674,28 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/preact": {
+ "version": "10.27.1",
+ "resolved": "https://registry.npmjs.org/preact/-/preact-10.27.1.tgz",
+ "integrity": "sha512-V79raXEWch/rbqoNc7nT9E4ep7lu+mI3+sBmfRD4i1M73R3WLYcCtdI0ibxGVf4eQL8ZIz2nFacqEC+rmnOORQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/preact"
+ }
+ },
+ "node_modules/preact-render-to-string": {
+ "version": "5.2.6",
+ "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz",
+ "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==",
+ "license": "MIT",
+ "dependencies": {
+ "pretty-format": "^3.8.0"
+ },
+ "peerDependencies": {
+ "preact": ">=10"
+ }
+ },
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -5047,11 +5706,119 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/prettier": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
+ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prettier-plugin-tailwindcss": {
+ "version": "0.6.14",
+ "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.14.tgz",
+ "integrity": "sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.21.3"
+ },
+ "peerDependencies": {
+ "@ianvs/prettier-plugin-sort-imports": "*",
+ "@prettier/plugin-hermes": "*",
+ "@prettier/plugin-oxc": "*",
+ "@prettier/plugin-pug": "*",
+ "@shopify/prettier-plugin-liquid": "*",
+ "@trivago/prettier-plugin-sort-imports": "*",
+ "@zackad/prettier-plugin-twig": "*",
+ "prettier": "^3.0",
+ "prettier-plugin-astro": "*",
+ "prettier-plugin-css-order": "*",
+ "prettier-plugin-import-sort": "*",
+ "prettier-plugin-jsdoc": "*",
+ "prettier-plugin-marko": "*",
+ "prettier-plugin-multiline-arrays": "*",
+ "prettier-plugin-organize-attributes": "*",
+ "prettier-plugin-organize-imports": "*",
+ "prettier-plugin-sort-imports": "*",
+ "prettier-plugin-style-order": "*",
+ "prettier-plugin-svelte": "*"
+ },
+ "peerDependenciesMeta": {
+ "@ianvs/prettier-plugin-sort-imports": {
+ "optional": true
+ },
+ "@prettier/plugin-hermes": {
+ "optional": true
+ },
+ "@prettier/plugin-oxc": {
+ "optional": true
+ },
+ "@prettier/plugin-pug": {
+ "optional": true
+ },
+ "@shopify/prettier-plugin-liquid": {
+ "optional": true
+ },
+ "@trivago/prettier-plugin-sort-imports": {
+ "optional": true
+ },
+ "@zackad/prettier-plugin-twig": {
+ "optional": true
+ },
+ "prettier-plugin-astro": {
+ "optional": true
+ },
+ "prettier-plugin-css-order": {
+ "optional": true
+ },
+ "prettier-plugin-import-sort": {
+ "optional": true
+ },
+ "prettier-plugin-jsdoc": {
+ "optional": true
+ },
+ "prettier-plugin-marko": {
+ "optional": true
+ },
+ "prettier-plugin-multiline-arrays": {
+ "optional": true
+ },
+ "prettier-plugin-organize-attributes": {
+ "optional": true
+ },
+ "prettier-plugin-organize-imports": {
+ "optional": true
+ },
+ "prettier-plugin-sort-imports": {
+ "optional": true
+ },
+ "prettier-plugin-style-order": {
+ "optional": true
+ },
+ "prettier-plugin-svelte": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/pretty-format": {
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz",
+ "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==",
+ "license": "MIT"
+ },
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"loose-envify": "^1.4.0",
@@ -5059,6 +5826,12 @@
"react-is": "^16.13.1"
}
},
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+ "license": "MIT"
+ },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -5111,13 +5884,66 @@
"react": "^19.1.0"
}
},
+ "node_modules/react-hot-toast": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.6.0.tgz",
+ "integrity": "sha512-bH+2EBMZ4sdyou/DPrfgIouFpcRLCJ+HoCA32UoAYHn6T3Ur5yfcDCeSr5mwldl6pFOsiocmrXMuoCJ1vV8bWg==",
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.1.3",
+ "goober": "^2.1.16"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "react": ">=16",
+ "react-dom": ">=16"
+ }
+ },
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
- "dev": true,
"license": "MIT"
},
+ "node_modules/react-select": {
+ "version": "5.10.2",
+ "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.10.2.tgz",
+ "integrity": "sha512-Z33nHdEFWq9tfnfVXaiM12rbJmk+QjFEztWLtmXqQhz6Al4UZZ9xc0wiatmGtUOCCnHN0WizL3tCMYRENX4rVQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.12.0",
+ "@emotion/cache": "^11.4.0",
+ "@emotion/react": "^11.8.1",
+ "@floating-ui/dom": "^1.0.1",
+ "@types/react-transition-group": "^4.4.0",
+ "memoize-one": "^6.0.0",
+ "prop-types": "^15.6.0",
+ "react-transition-group": "^4.3.0",
+ "use-isomorphic-layout-effect": "^1.2.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/react-transition-group": {
+ "version": "4.4.5",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+ "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@babel/runtime": "^7.5.5",
+ "dom-helpers": "^5.0.1",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.6.0",
+ "react-dom": ">=16.6.0"
+ }
+ },
"node_modules/reflect.getprototypeof": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
@@ -5166,7 +5992,6 @@
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
- "dev": true,
"license": "MIT",
"dependencies": {
"is-core-module": "^2.16.0",
@@ -5187,7 +6012,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
@@ -5513,6 +6337,22 @@
"is-arrayish": "^0.3.1"
}
},
+ "node_modules/simple-swizzle/node_modules/is-arrayish": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -5702,6 +6542,12 @@
}
}
},
+ "node_modules/stylis": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
+ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
+ "license": "MIT"
+ },
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -5719,7 +6565,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -5767,6 +6612,16 @@
"node": ">=18"
}
},
+ "node_modules/tar/node_modules/yallist": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
+ "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/tinyglobby": {
"version": "0.2.14",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
@@ -6036,6 +6891,29 @@
"punycode": "^2.1.0"
}
},
+ "node_modules/use-isomorphic-layout-effect": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz",
+ "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -6152,13 +7030,18 @@
}
},
"node_modules/yallist": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
- "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
- "dev": true,
- "license": "BlueOak-1.0.0",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "license": "ISC"
+ },
+ "node_modules/yaml": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+ "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+ "license": "ISC",
"engines": {
- "node": ">=18"
+ "node": ">= 6"
}
},
"node_modules/yocto-queue": {
@@ -6173,6 +7056,35 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "node_modules/zustand": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.8.tgz",
+ "integrity": "sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20.0"
+ },
+ "peerDependencies": {
+ "@types/react": ">=18.0.0",
+ "immer": ">=9.0.6",
+ "react": ">=18.0.0",
+ "use-sync-external-store": ">=1.2.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "immer": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "use-sync-external-store": {
+ "optional": true
+ }
+ }
}
}
}
diff --git a/frontend/package.json b/frontend/package.json
index 3adbca3..9b31920 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -11,9 +11,15 @@
"dependencies": {
"@babylonjs/core": "^6.44.0",
"@babylonjs/loaders": "^6.49.0",
+ "@tanstack/react-query": "^5.85.5",
+ "axios": "^1.11.0",
"next": "^15.4.3",
+ "next-auth": "^4.24.11",
"react": "19.1.0",
- "react-dom": "19.1.0"
+ "react-dom": "19.1.0",
+ "react-hot-toast": "^2.6.0",
+ "react-select": "^5.10.2",
+ "zustand": "^5.0.8"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
@@ -23,6 +29,8 @@
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "15.4.3",
+ "prettier": "^3.6.2",
+ "prettier-plugin-tailwindcss": "^0.6.14",
"tailwindcss": "^4",
"typescript": "^5"
}