diff --git a/ospabhost/backend/package-lock.json b/ospabhost/backend/package-lock.json index 74761b5..2b884ec 100644 --- a/ospabhost/backend/package-lock.json +++ b/ospabhost/backend/package-lock.json @@ -9,20 +9,22 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@prisma/client": "^5.14.0", + "@prisma/client": "^6.16.2", + "bcrypt": "^6.0.0", "bcryptjs": "^2.4.3", "cors": "^2.8.5", "dotenv": "^16.4.5", - "express": "^4.19.2", - "jsonwebtoken": "^9.0.2", - "prisma": "^5.14.0" + "express": "^4.21.2", + "jsonwebtoken": "^9.0.2" }, "devDependencies": { + "@types/bcrypt": "^6.0.0", "@types/bcryptjs": "^2.4.6", "@types/cors": "^2.8.17", - "@types/express": "^4.17.21", - "@types/jsonwebtoken": "^9.0.6", + "@types/express": "^4.17.23", + "@types/jsonwebtoken": "^9.0.10", "@types/node": "^20.12.12", + "prisma": "^6.16.2", "ts-node-dev": "^2.0.0", "typescript": "^5.4.5" } @@ -69,68 +71,97 @@ } }, "node_modules/@prisma/client": { - "version": "5.22.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.22.0.tgz", - "integrity": "sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==", + "version": "6.16.2", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.16.2.tgz", + "integrity": "sha512-E00PxBcalMfYO/TWnXobBVUai6eW/g5OsifWQsQDzJYm7yaY+IRLo7ZLsaefi0QkTpxfuhFcQ/w180i6kX3iJw==", "hasInstallScript": true, "license": "Apache-2.0", "engines": { - "node": ">=16.13" + "node": ">=18.18" }, "peerDependencies": { - "prisma": "*" + "prisma": "*", + "typescript": ">=5.1.0" }, "peerDependenciesMeta": { "prisma": { "optional": true + }, + "typescript": { + "optional": true } } }, + "node_modules/@prisma/config": { + "version": "6.16.2", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.16.2.tgz", + "integrity": "sha512-mKXSUrcqXj0LXWPmJsK2s3p9PN+aoAbyMx7m5E1v1FufofR1ZpPoIArjjzOIm+bJRLLvYftoNYLx1tbHgF9/yg==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "c12": "3.1.0", + "deepmerge-ts": "7.1.5", + "effect": "3.16.12", + "empathic": "2.0.0" + } + }, "node_modules/@prisma/debug": { - "version": "5.22.0", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.22.0.tgz", - "integrity": "sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==", + "version": "6.16.2", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.16.2.tgz", + "integrity": "sha512-bo4/gA/HVV6u8YK2uY6glhNsJ7r+k/i5iQ9ny/3q5bt9ijCj7WMPUwfTKPvtEgLP+/r26Z686ly11hhcLiQ8zA==", + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines": { - "version": "5.22.0", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.22.0.tgz", - "integrity": "sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==", + "version": "6.16.2", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.16.2.tgz", + "integrity": "sha512-7yf3AjfPUgsg/l7JSu1iEhsmZZ/YE00yURPjTikqm2z4btM0bCl2coFtTGfeSOWbQMmq45Jab+53yGUIAT1sjA==", + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "5.22.0", - "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", - "@prisma/fetch-engine": "5.22.0", - "@prisma/get-platform": "5.22.0" + "@prisma/debug": "6.16.2", + "@prisma/engines-version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", + "@prisma/fetch-engine": "6.16.2", + "@prisma/get-platform": "6.16.2" } }, "node_modules/@prisma/engines-version": { - "version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2.tgz", - "integrity": "sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==", + "version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43.tgz", + "integrity": "sha512-ThvlDaKIVrnrv97ujNFDYiQbeMQpLa0O86HFA2mNoip4mtFqM7U5GSz2ie1i2xByZtvPztJlNRgPsXGeM/kqAA==", + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/fetch-engine": { - "version": "5.22.0", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.22.0.tgz", - "integrity": "sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==", + "version": "6.16.2", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.16.2.tgz", + "integrity": "sha512-wPnZ8DMRqpgzye758ZvfAMiNJRuYpz+rhgEBZi60ZqDIgOU2694oJxiuu3GKFeYeR/hXxso4/2oBC243t/whxQ==", + "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "5.22.0", - "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", - "@prisma/get-platform": "5.22.0" + "@prisma/debug": "6.16.2", + "@prisma/engines-version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", + "@prisma/get-platform": "6.16.2" } }, "node_modules/@prisma/get-platform": { - "version": "5.22.0", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.22.0.tgz", - "integrity": "sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==", + "version": "6.16.2", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.16.2.tgz", + "integrity": "sha512-U/P36Uke5wS7r1+omtAgJpEB94tlT4SdlgaeTc6HVTTT93pXj7zZ+B/cZnmnvjcNPfWddgoDx8RLjmQwqGDYyA==", + "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "5.22.0" + "@prisma/debug": "6.16.2" } }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "devOptional": true, + "license": "MIT" + }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", @@ -159,6 +190,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/bcryptjs": { "version": "2.4.6", "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.6.tgz", @@ -256,9 +297,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.19.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.14.tgz", - "integrity": "sha512-gqiKWld3YIkmtrrg9zDvg9jfksZCcPywXVN7IauUGhilwGV/yOyeUsvpR796m/Jye0zUzMXPKe8Ct1B79A7N5Q==", + "version": "20.19.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.15.tgz", + "integrity": "sha512-W3bqcbLsRdFDVcmAM5l6oLlcl67vjevn8j1FPZ4nx+K5jNoWCh+FC/btxFoBPnvQlrHHDwfjp1kjIEDfwJ0Mog==", "dev": true, "license": "MIT", "dependencies": { @@ -389,6 +430,20 @@ "dev": true, "license": "MIT" }, + "node_modules/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/bcryptjs": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", @@ -478,6 +533,35 @@ "node": ">= 0.8" } }, + "node_modules/c12": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", + "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.3", + "confbox": "^0.2.2", + "defu": "^6.1.4", + "dotenv": "^16.6.1", + "exsolve": "^1.0.7", + "giget": "^2.0.0", + "jiti": "^2.4.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^1.0.0", + "pkg-types": "^2.2.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.5" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -508,28 +592,29 @@ } }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "devOptional": true, "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + } + }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" } }, "node_modules/concat-map": { @@ -539,6 +624,23 @@ "dev": true, "license": "MIT" }, + "node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -604,6 +706,23 @@ "ms": "2.0.0" } }, + "node_modules/deepmerge-ts": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", + "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "devOptional": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "devOptional": true, + "license": "MIT" + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -613,6 +732,13 @@ "node": ">= 0.8" } }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "devOptional": true, + "license": "MIT" + }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -684,6 +810,27 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, + "node_modules/effect": { + "version": "3.16.12", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.16.12.tgz", + "integrity": "sha512-N39iBk0K71F9nb442TLbTkjl24FLUzuvx2i1I2RsEAQsdAdUTuUoW0vlfUXgkMTUOnYqKnWcFfqw4hK4Pw27hg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "fast-check": "^3.23.1" + } + }, + "node_modules/empathic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", + "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", @@ -784,6 +931,36 @@ "url": "https://opencollective.com/express" } }, + "node_modules/exsolve": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", + "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/fast-check": { + "version": "3.23.2", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", + "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "devOptional": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^6.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -844,6 +1021,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -900,6 +1078,24 @@ "node": ">= 0.4" } }, + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.6.0", + "pathe": "^2.0.3" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -1088,6 +1284,16 @@ "node": ">=0.12.0" } }, + "node_modules/jiti": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "devOptional": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -1306,6 +1512,33 @@ "node": ">= 0.6" } }, + "node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -1316,6 +1549,26 @@ "node": ">=0.10.0" } }, + "node_modules/nypm": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz", + "integrity": "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.2", + "pathe": "^2.0.3", + "pkg-types": "^2.3.0", + "tinyexec": "^1.0.1" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": "^14.16.0 || >=16.10.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -1337,6 +1590,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "devOptional": true, + "license": "MIT" + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -1391,6 +1651,20 @@ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "license": "MIT" }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "devOptional": true, + "license": "MIT" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -1404,23 +1678,42 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, "node_modules/prisma": { - "version": "5.22.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.22.0.tgz", - "integrity": "sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==", + "version": "6.16.2", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.16.2.tgz", + "integrity": "sha512-aRvldGE5UUJTtVmFiH3WfNFNiqFlAtePUxcI0UEGlnXCX7DqhiMT5TRYwncHFeA/Reca5W6ToXXyCMTeFPdSXA==", + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@prisma/engines": "5.22.0" + "@prisma/config": "6.16.2", + "@prisma/engines": "6.16.2" }, "bin": { "prisma": "build/index.js" }, "engines": { - "node": ">=16.13" + "node": ">=18.18" }, - "optionalDependencies": { - "fsevents": "2.3.3" + "peerDependencies": { + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/proxy-addr": { @@ -1436,6 +1729,23 @@ "node": ">= 0.10" } }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "devOptional": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -1475,17 +1785,29 @@ "node": ">= 0.8" } }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "devOptional": true, "license": "MIT", "dependencies": { - "picomatch": "^2.2.1" - }, + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "devOptional": true, + "license": "MIT", "engines": { - "node": ">=8.10.0" + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/resolve": { @@ -1756,6 +2078,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tinyexec": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", + "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", + "devOptional": true, + "license": "MIT" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1867,6 +2196,44 @@ } } }, + "node_modules/ts-node-dev/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/ts-node-dev/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/tsconfig": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", @@ -1897,7 +2264,7 @@ "version": "5.9.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", diff --git a/ospabhost/backend/package.json b/ospabhost/backend/package.json index f458f5f..15911dd 100644 --- a/ospabhost/backend/package.json +++ b/ospabhost/backend/package.json @@ -12,21 +12,23 @@ "author": "", "license": "ISC", "dependencies": { - "@prisma/client": "^5.14.0", + "@prisma/client": "^6.16.2", + "bcrypt": "^6.0.0", "bcryptjs": "^2.4.3", "cors": "^2.8.5", "dotenv": "^16.4.5", - "express": "^4.19.2", - "jsonwebtoken": "^9.0.2", - "prisma": "^5.14.0" + "express": "^4.21.2", + "jsonwebtoken": "^9.0.2" }, "devDependencies": { + "@types/bcrypt": "^6.0.0", "@types/bcryptjs": "^2.4.6", "@types/cors": "^2.8.17", - "@types/express": "^4.17.21", - "@types/jsonwebtoken": "^9.0.6", + "@types/express": "^4.17.23", + "@types/jsonwebtoken": "^9.0.10", "@types/node": "^20.12.12", + "prisma": "^6.16.2", "ts-node-dev": "^2.0.0", "typescript": "^5.4.5" } -} \ No newline at end of file +} diff --git a/ospabhost/backend/prisma/migrations/20250916125853_pup/migration.sql b/ospabhost/backend/prisma/migrations/20250916125853_pup/migration.sql new file mode 100644 index 0000000..ae0bacc --- /dev/null +++ b/ospabhost/backend/prisma/migrations/20250916125853_pup/migration.sql @@ -0,0 +1,42 @@ +-- CreateTable +CREATE TABLE `User` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `username` VARCHAR(191) NOT NULL, + `email` VARCHAR(191) NOT NULL, + `password` VARCHAR(191) NOT NULL, + `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + + UNIQUE INDEX `User_email_key`(`email`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `Plan` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `name` VARCHAR(191) NOT NULL, + `price` DOUBLE NOT NULL, + `description` VARCHAR(191) NULL, + `isCustom` BOOLEAN NOT NULL DEFAULT false, + `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + `userId` INTEGER NOT NULL, + + UNIQUE INDEX `Plan_name_key`(`name`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `Service` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `name` VARCHAR(191) NOT NULL, + `price` DOUBLE NOT NULL, + `planId` INTEGER NULL, + + UNIQUE INDEX `Service_name_key`(`name`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- AddForeignKey +ALTER TABLE `Plan` ADD CONSTRAINT `Plan_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `Service` ADD CONSTRAINT `Service_planId_fkey` FOREIGN KEY (`planId`) REFERENCES `Plan`(`id`) ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/ospabhost/backend/prisma/migrations/20250916141627_add_operator_to_user/migration.sql b/ospabhost/backend/prisma/migrations/20250916141627_add_operator_to_user/migration.sql new file mode 100644 index 0000000..c87328f --- /dev/null +++ b/ospabhost/backend/prisma/migrations/20250916141627_add_operator_to_user/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE `user` ADD COLUMN `operator` INTEGER NOT NULL DEFAULT 0; diff --git a/ospabhost/backend/prisma/migrations/migration_lock.toml b/ospabhost/backend/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..592fc0b --- /dev/null +++ b/ospabhost/backend/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (e.g., Git) +provider = "mysql" diff --git a/ospabhost/backend/prisma/schema.prisma b/ospabhost/backend/prisma/schema.prisma index 0313c00..89747da 100644 --- a/ospabhost/backend/prisma/schema.prisma +++ b/ospabhost/backend/prisma/schema.prisma @@ -16,12 +16,26 @@ model User { email String @unique password String createdAt DateTime @default(now()) + plans Plan[] + operator Int @default(0) // Добавляем новую колонку operator } -// Пока что у тебя нет других моделей, но ты можешь -// добавить их сюда позже, например: -// model Plan { -// id Int @id @default(autoincrement()) -// name String @unique -// price Float -// } \ No newline at end of file +model Plan { + id Int @id @default(autoincrement()) + name String @unique + price Float + description String? + isCustom Boolean @default(false) + createdAt DateTime @default(now()) + userId Int + owner User @relation(fields: [userId], references: [id]) + services Service[] +} + +model Service { + id Int @id @default(autoincrement()) + name String @unique + price Float + planId Int? + plan Plan? @relation(fields: [planId], references: [id]) +} \ No newline at end of file diff --git a/ospabhost/backend/src/modules/auth/auth.controller.ts b/ospabhost/backend/src/modules/auth/auth.controller.ts index f2c8a78..c071009 100644 --- a/ospabhost/backend/src/modules/auth/auth.controller.ts +++ b/ospabhost/backend/src/modules/auth/auth.controller.ts @@ -4,30 +4,24 @@ import jwt from 'jsonwebtoken'; import { PrismaClient } from '@prisma/client'; const prisma = new PrismaClient(); - -// Замените 'любая_секретная_строка' на вашу переменную окружения JWT_SECRET -const JWT_SECRET = process.env.JWT_SECRET || 'любая_секретная_строка'; +const JWT_SECRET = process.env.JWT_SECRET || 'your_super_secret_key'; export const register = async (req: Request, res: Response) => { + const { username, email, password } = req.body; + + if (!username || !email || !password) { + return res.status(400).json({ message: 'Все поля обязательны.' }); + } + try { - const { username, email, password } = req.body as Partial<{ username: string; email: string; password: string }>; - - if (!username || !email || !password) { - return res.status(400).json({ message: 'Необходимо указать username, email и password' }); - } - - // Проверка, есть ли уже пользователь с таким email - const existingUser = await prisma.user.findUnique({ where: { email } }); + const existingUser = await prisma.user.findUnique({ where: { email } }); if (existingUser) { return res.status(409).json({ message: 'Пользователь с таким email уже существует.' }); } - // Хеширование пароля - const salt = await bcrypt.genSalt(10); - const hashedPassword = await bcrypt.hash(password, salt); + const hashedPassword = await bcrypt.hash(password, 10); - // Создание нового пользователя в базе данных - const newUser = await prisma.user.create({ + await prisma.user.create({ data: { username, email, @@ -35,74 +29,49 @@ export const register = async (req: Request, res: Response) => { }, }); - // Генерация JWT токена - const token = jwt.sign({ id: newUser.id }, JWT_SECRET, { expiresIn: '1h' }); - - res.status(201).json({ - message: 'Регистрация прошла успешно!', - token, - user: { - id: newUser.id, - username: newUser.username, - email: newUser.email, - }, - }); + res.status(201).json({ message: 'Регистрация прошла успешно!' }); } catch (error) { console.error('Ошибка при регистрации:', error); - res.status(500).json({ message: 'Внутренняя ошибка сервера' }); + res.status(500).json({ message: 'Ошибка сервера.' }); } }; export const login = async (req: Request, res: Response) => { - try { - const { email, password } = req.body as Partial<{ email: string; password: string }>; - if (!email || !password) { - return res.status(400).json({ message: 'Необходимо указать email и password' }); - } + const { email, password } = req.body; + + if (!email || !password) { + return res.status(400).json({ message: 'Необходимо указать email и password.' }); + } - // Поиск пользователя по email + try { const user = await prisma.user.findUnique({ where: { email } }); if (!user) { - return res.status(401).json({ message: 'Неверный email или пароль' }); + return res.status(401).json({ message: 'Неверный email или пароль.' }); } - // Проверка пароля - const isMatch = await bcrypt.compare(password, user.password); + const isMatch = await bcrypt.compare(password, user.password); if (!isMatch) { - return res.status(401).json({ message: 'Неверный email или пароль' }); + return res.status(401).json({ message: 'Неверный email или пароль.' }); } - // Генерация JWT токена - const token = jwt.sign({ id: user.id }, JWT_SECRET, { expiresIn: '1h' }); + const token = jwt.sign({ id: user.id }, JWT_SECRET, { expiresIn: '24h' }); - res.status(200).json({ - message: 'Вход выполнен успешно!', - token, - user: { - id: user.id, - username: user.username, - email: user.email, - }, - }); + res.status(200).json({ token }); } catch (error) { console.error('Ошибка при входе:', error); - res.status(500).json({ message: 'Внутренняя ошибка сервера' }); + res.status(500).json({ message: 'Ошибка сервера.' }); } }; -type AuthRequest = Request & { userId?: number }; - -export const getMe = async (req: AuthRequest, res: Response) => { +export const getMe = async (req: Request, res: Response) => { try { - // ID пользователя будет добавлен мидлваром - const userId = req.userId; + const userId = (req as any).userId; if (!userId) { - return res.status(401).json({ message: 'Не авторизован' }); + return res.status(401).json({ message: 'Не авторизован.' }); } - // Получение пользователя из базы данных, исключая пароль const user = await prisma.user.findUnique({ where: { id: userId }, select: { @@ -110,17 +79,18 @@ export const getMe = async (req: AuthRequest, res: Response) => { username: true, email: true, createdAt: true, + operator: true, // Добавляем поле operator }, }); if (!user) { - return res.status(404).json({ message: 'Пользователь не найден' }); + return res.status(404).json({ message: 'Пользователь не найден.' }); } res.status(200).json({ user }); } catch (error) { console.error('Ошибка при получении данных пользователя:', error); - res.status(500).json({ message: 'Внутренняя ошибка сервера' }); - } + res.status(500).json({ message: 'Ошибка сервера.' }); + } }; \ No newline at end of file diff --git a/ospabhost/backend/src/modules/auth/auth.middleware.ts b/ospabhost/backend/src/modules/auth/auth.middleware.ts index ffe2fc1..41189da 100644 --- a/ospabhost/backend/src/modules/auth/auth.middleware.ts +++ b/ospabhost/backend/src/modules/auth/auth.middleware.ts @@ -1,42 +1,33 @@ import { Request, Response, NextFunction } from 'express'; import jwt from 'jsonwebtoken'; -// Расширяем интерфейс Request, чтобы добавить поле userId -interface CustomRequest extends Request { +interface AuthRequest extends Request { userId?: number; } -// Замените 'любая_секретная_строка' на вашу переменную окружения JWT_SECRET -const JWT_SECRET = process.env.JWT_SECRET || 'любая_секретная_строка'; +const JWT_SECRET = process.env.JWT_SECRET || 'your_super_secret_key'; -export const authMiddleware = (req: CustomRequest, res: Response, next: NextFunction) => { +export const authMiddleware = (req: AuthRequest, res: Response, next: NextFunction) => { try { - // Получаем токен из заголовка Authorization const authHeader = req.headers.authorization; if (!authHeader) { - return res.status(401).json({ message: 'Нет токена авторизации' }); + return res.status(401).json({ message: 'Нет токена авторизации.' }); } - // Токен имеет формат 'Bearer <токен>', поэтому мы его разделяем const token = authHeader.split(' ')[1]; if (!token) { - return res.status(401).json({ message: 'Неправильный формат токена' }); + return res.status(401).json({ message: 'Неправильный формат токена.' }); } - // Проверяем валидность токена const decoded = jwt.verify(token, JWT_SECRET) as { id: number }; - - // Сохраняем ID пользователя в объекте запроса req.userId = decoded.id; - - // Передаем управление следующему мидлвару или контроллеру next(); } catch (error) { console.error('Ошибка в мидлваре аутентификации:', error); if (error instanceof jwt.JsonWebTokenError) { - return res.status(401).json({ message: 'Неверный или просроченный токен' }); + return res.status(401).json({ message: 'Неверный или просроченный токен.' }); } - res.status(500).json({ message: 'Внутренняя ошибка сервера' }); + res.status(500).json({ message: 'Ошибка сервера.' }); } }; \ No newline at end of file diff --git a/ospabhost/backend/src/modules/auth/auth.routes.ts b/ospabhost/backend/src/modules/auth/auth.routes.ts index 3835c17..ab4f9df 100644 --- a/ospabhost/backend/src/modules/auth/auth.routes.ts +++ b/ospabhost/backend/src/modules/auth/auth.routes.ts @@ -4,14 +4,8 @@ import { authMiddleware } from './auth.middleware'; const router = Router(); -// Маршрут для регистрации нового пользователя router.post('/register', register); - -// Маршрут для входа router.post('/login', login); - -// Маршрут для получения данных о текущем пользователе -// Он защищен мидлваром authMiddleware router.get('/me', authMiddleware, getMe); export default router; \ No newline at end of file diff --git a/ospabhost/backend/src/server.ts b/ospabhost/backend/src/server.ts new file mode 100644 index 0000000..e69de29 diff --git a/ospabhost/frontend/.env b/ospabhost/frontend/.env new file mode 100644 index 0000000..920d4e0 --- /dev/null +++ b/ospabhost/frontend/.env @@ -0,0 +1,2 @@ +VITE_CARD_NUMBER="2200700526174551" +VITE_SBP_QR_URL="https://www.tinkoff.ru/rm/r_EuYdiibJtU.jfgPfrHOCE/hTZjH20709" \ No newline at end of file diff --git a/ospabhost/frontend/package-lock.json b/ospabhost/frontend/package-lock.json index 4da34e5..164e297 100644 --- a/ospabhost/frontend/package-lock.json +++ b/ospabhost/frontend/package-lock.json @@ -11,7 +11,8 @@ "axios": "^1.12.2", "react": "^19.1.1", "react-dom": "^19.1.1", - "react-icons": "^5.5.0" + "react-icons": "^5.5.0", + "react-qr-code": "^2.0.18" }, "devDependencies": { "@eslint/js": "^9.33.0", @@ -1874,9 +1875,9 @@ "license": "MIT" }, "node_modules/autoprefixer": { - "version": "10.4.14", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", - "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", "dev": true, "funding": [ { @@ -1886,15 +1887,19 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "license": "MIT", "dependencies": { - "browserslist": "^4.21.5", - "caniuse-lite": "^1.0.30001464", - "fraction.js": "^4.2.0", + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", + "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -3137,7 +3142,6 @@ "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": { @@ -3225,13 +3229,16 @@ } }, "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, "node_modules/lines-and-columns": { @@ -3264,6 +3271,18 @@ "dev": true, "license": "MIT" }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -3427,7 +3446,6 @@ "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" @@ -3605,9 +3623,9 @@ } }, "node_modules/postcss": { - "version": "8.4.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", - "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "dev": true, "funding": [ { @@ -3617,13 +3635,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -3648,10 +3670,20 @@ } }, "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "dependencies": { "camelcase-css": "^2.0.1" @@ -3659,10 +3691,6 @@ "engines": { "node": "^12 || ^14 || >= 16" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, "peerDependencies": { "postcss": "^8.4.21" } @@ -3703,19 +3731,6 @@ } } }, - "node_modules/postcss-load-config/node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, "node_modules/postcss-nested": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", @@ -3773,6 +3788,17 @@ "node": ">= 0.8.0" } }, + "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==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "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", @@ -3789,6 +3815,12 @@ "node": ">=6" } }, + "node_modules/qr.js": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz", + "integrity": "sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ==", + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -3840,6 +3872,25 @@ "react": "*" } }, + "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==", + "license": "MIT" + }, + "node_modules/react-qr-code": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/react-qr-code/-/react-qr-code-2.0.18.tgz", + "integrity": "sha512-v1Jqz7urLMhkO6jkgJuBYhnqvXagzceg3qJUWayuCK/c6LTIonpWbwxR1f1APGd4xrW/QcQEovNrAojbUz65Tg==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.8.1", + "qr.js": "0.0.0" + }, + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-refresh": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", @@ -4256,34 +4307,34 @@ } }, "node_modules/tailwindcss": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.3.tgz", - "integrity": "sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==", + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", "dev": true, "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", - "chokidar": "^3.5.3", + "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", - "fast-glob": "^3.2.12", + "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "jiti": "^1.18.2", - "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", @@ -4293,35 +4344,6 @@ "node": ">=14.0.0" } }, - "node_modules/tailwindcss/node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -4631,35 +4653,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/vite/node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/ospabhost/frontend/package.json b/ospabhost/frontend/package.json index 3b28ada..744aef5 100644 --- a/ospabhost/frontend/package.json +++ b/ospabhost/frontend/package.json @@ -13,7 +13,8 @@ "axios": "^1.12.2", "react": "^19.1.1", "react-dom": "^19.1.1", - "react-icons": "^5.5.0" + "react-icons": "^5.5.0", + "react-qr-code": "^2.0.18" }, "devDependencies": { "@eslint/js": "^9.33.0", diff --git a/ospabhost/frontend/src/App.tsx b/ospabhost/frontend/src/App.tsx index 66844a9..0cb68e5 100644 --- a/ospabhost/frontend/src/App.tsx +++ b/ospabhost/frontend/src/App.tsx @@ -1,26 +1,28 @@ +// src/app.tsx import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; -import PageTmpl from './components/pagetempl'; -import HomePage from './pages/index'; -import MainPage from './pages/dashboard/mainpage'; -import LogoutPage from './pages/dashboard/logout'; -import LoginPage from './pages/login'; -import RegisterPage from './pages/register'; -import TariffsPage from './pages/tariffs'; -import AboutPage from './pages/about'; -import PrivateRoute from './components/privateroute'; +import Pagetempl from './components/pagetempl'; +import Homepage from './pages/index'; +import Dashboard from './pages/dashboard/mainpage'; +import Loginpage from './pages/login'; +import Registerpage from './pages/register'; +import Tariffspage from './pages/tariffs'; +import Aboutpage from './pages/about'; +import Privateroute from './components/privateroute'; +import { AuthProvider } from './context/authcontext'; // Import AuthProvider function App() { return ( - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - + {/* Wrap the entire application with AuthProvider */} + + } /> + } /> + } /> + } /> + } /> + } /> + + ); } diff --git a/ospabhost/frontend/src/components/header.tsx b/ospabhost/frontend/src/components/header.tsx index 573492f..d6d7ba3 100644 --- a/ospabhost/frontend/src/components/header.tsx +++ b/ospabhost/frontend/src/components/header.tsx @@ -1,57 +1,46 @@ import { Link } from 'react-router-dom'; -import { useState, useEffect } from 'react'; +import useAuth from '../context/useAuth'; const Header = () => { - const [isLoggedIn, setIsLoggedIn] = useState(false); + const { isLoggedIn, logout } = useAuth(); - useEffect(() => { - const checkLoginStatus = () => { - setIsLoggedIn(localStorage.getItem('isLoggedIn') === 'true'); - }; - - checkLoginStatus(); - window.addEventListener('storage', checkLoginStatus); - - return () => { - window.removeEventListener('storage', checkLoginStatus); - }; - }, []); + const handleLogout = () => { + logout(); + }; return ( - + ); }; diff --git a/ospabhost/frontend/src/components/privateroute.tsx b/ospabhost/frontend/src/components/privateroute.tsx index fc6de5a..543085c 100644 --- a/ospabhost/frontend/src/components/privateroute.tsx +++ b/ospabhost/frontend/src/components/privateroute.tsx @@ -1,13 +1,14 @@ import { Navigate } from 'react-router-dom'; import React from 'react'; +import useAuth from '../context/useAuth'; interface PrivateRouteProps { children: React.ReactNode; } const PrivateRoute: React.FC = ({ children }) => { - const isAuthenticated = localStorage.getItem('isLoggedIn') === 'true'; - return isAuthenticated ? children : ; + const { isLoggedIn } = useAuth(); + return isLoggedIn ? children : ; }; export default PrivateRoute; \ No newline at end of file diff --git a/ospabhost/frontend/src/context/authcontext.tsx b/ospabhost/frontend/src/context/authcontext.tsx new file mode 100644 index 0000000..caccb8e --- /dev/null +++ b/ospabhost/frontend/src/context/authcontext.tsx @@ -0,0 +1,49 @@ +// /src/context/authcontext.tsx +import React, { createContext, useState, useEffect } from 'react'; +import type { ReactNode } from 'react'; + +interface AuthContextType { + isLoggedIn: boolean; + login: (token: string) => void; + logout: () => void; +} + +// Создаем контекст с начальными значениями +const AuthContext = createContext({ + isLoggedIn: false, + login: () => {}, + logout: () => {}, +}); + +interface AuthProviderProps { + children: ReactNode; +} + +// Создаем провайдер, который будет управлять состоянием +export const AuthProvider = ({ children }: AuthProviderProps) => { + const [isLoggedIn, setIsLoggedIn] = useState(false); + + // Проверяем статус входа при загрузке приложения + useEffect(() => { + const token = localStorage.getItem('access_token'); + setIsLoggedIn(!!token); + }, []); + + const login = (token: string) => { + localStorage.setItem('access_token', token); + setIsLoggedIn(true); + }; + + const logout = () => { + localStorage.removeItem('access_token'); + setIsLoggedIn(false); + }; + + return ( + + {children} + + ); +}; + +export default AuthContext; \ No newline at end of file diff --git a/ospabhost/frontend/src/context/useAuth.ts b/ospabhost/frontend/src/context/useAuth.ts new file mode 100644 index 0000000..c80460a --- /dev/null +++ b/ospabhost/frontend/src/context/useAuth.ts @@ -0,0 +1,6 @@ +import { useContext } from 'react'; +import AuthContext from './authcontext'; + +const useAuth = () => useContext(AuthContext); + +export default useAuth; diff --git a/ospabhost/frontend/src/main.tsx b/ospabhost/frontend/src/main.tsx index bef5202..475dce2 100644 --- a/ospabhost/frontend/src/main.tsx +++ b/ospabhost/frontend/src/main.tsx @@ -1,7 +1,7 @@ import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import './index.css' -import App from './App.tsx' +import App from './app.tsx' createRoot(document.getElementById('root')!).render( diff --git a/ospabhost/frontend/src/pages/dashboard/billing.tsx b/ospabhost/frontend/src/pages/dashboard/billing.tsx new file mode 100644 index 0000000..e3b48b2 --- /dev/null +++ b/ospabhost/frontend/src/pages/dashboard/billing.tsx @@ -0,0 +1,101 @@ +import { useState } from 'react'; +import { Link } from 'react-router-dom'; +import QRCode from 'react-qr-code'; + +const Billing = () => { + const [amount, setAmount] = useState(0); + const [isPaymentGenerated, setIsPaymentGenerated] = useState(false); + const [copyStatus, setCopyStatus] = useState(''); + + const cardNumber = process.env.REACT_APP_CARD_NUMBER || ''; + const sbpUrl = process.env.REACT_APP_SBP_QR_URL || ''; + + const handleGeneratePayment = () => { + if (amount <= 0) { + alert('Пожалуйста, введите сумму больше нуля.'); + return; + } + if (!cardNumber || !sbpUrl) { + alert('Данные для оплаты не настроены. Пожалуйста, обратитесь к администратору.'); + return; + } + setIsPaymentGenerated(true); + }; + + const handleCopyCard = () => { + if (cardNumber) { + navigator.clipboard.writeText(cardNumber); + setCopyStatus('Номер карты скопирован!'); + setTimeout(() => setCopyStatus(''), 2000); + } + }; + + return ( +
+

Пополнение баланса

+ + {!isPaymentGenerated ? ( +
+

+ Пополните свой баланс, чтобы оплачивать услуги. Минимальная сумма пополнения: 1 руб. +

+
+ + setAmount(Number(e.target.value))} + className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-ospab-primary" + min="1" + /> +
+ +
+ ) : ( +
+

Для пополнения баланса, пожалуйста, переведите сумму **₽{amount}**.

+

+ Ваш заказ будет обработан вручную после проверки чека. +

+ +
+

Оплата по СБП

+
+ +
+

Отсканируйте QR-код через мобильное приложение вашего банка.

+
+ +
+

Оплата по номеру карты

+

{cardNumber}

+ + {copyStatus &&

{copyStatus}

} +
+ +
+

Важно:

+

После оплаты сделайте скриншот или сохраните чек и отправьте его нам в тикет поддержки, чтобы мы могли подтвердить платёж.

+
+ +

+ После подтверждения ваш баланс будет пополнен. Вы можете перейти в раздел Тикеты, чтобы отправить нам чек. +

+
+ )} +
+ ); +}; + +export default Billing; \ No newline at end of file diff --git a/ospabhost/frontend/src/pages/dashboard/checkverification.tsx b/ospabhost/frontend/src/pages/dashboard/checkverification.tsx new file mode 100644 index 0000000..7e18c04 --- /dev/null +++ b/ospabhost/frontend/src/pages/dashboard/checkverification.tsx @@ -0,0 +1,10 @@ +const CheckVerification = () => { + return ( +
+

Проверка чеков

+

Здесь будут отображаться чеки для проверки.

+
+ ); +}; + +export default CheckVerification; \ No newline at end of file diff --git a/ospabhost/frontend/src/pages/dashboard/logout.tsx b/ospabhost/frontend/src/pages/dashboard/logout.tsx index ba915b4..ff4d690 100644 --- a/ospabhost/frontend/src/pages/dashboard/logout.tsx +++ b/ospabhost/frontend/src/pages/dashboard/logout.tsx @@ -6,9 +6,8 @@ const LogoutPage = () => { useEffect(() => { // Удаляем все токены и флаг входа из localStorage - localStorage.removeItem('access_token'); - localStorage.removeItem('refresh_token'); - localStorage.removeItem('isLoggedIn'); + localStorage.removeItem('access_token'); + localStorage.removeItem('refresh_token'); console.log('Выполняется выход из системы...'); // После выхода перенаправляем пользователя на главную страницу navigate('/'); diff --git a/ospabhost/frontend/src/pages/dashboard/mainpage.tsx b/ospabhost/frontend/src/pages/dashboard/mainpage.tsx index fc56db5..586d497 100644 --- a/ospabhost/frontend/src/pages/dashboard/mainpage.tsx +++ b/ospabhost/frontend/src/pages/dashboard/mainpage.tsx @@ -1,26 +1,112 @@ -import { Link } from 'react-router-dom'; +import { useState, useEffect } from 'react'; +import { Routes, Route, Link, useNavigate } from 'react-router-dom'; +import axios from 'axios'; +import AuthContext from '../../context/authcontext'; +import { useContext } from 'react'; + +// Импортируем компоненты для вкладок +import Summary from './summary'; +import Servers from './servers'; +import Tickets from './tickets'; +import Billing from './billing'; +import Settings from './settings'; +import CheckVerification from './checkverification.tsx'; +import TicketResponse from './ticketresponse.tsx'; + +const Dashboard = () => { + const [activeTab, setActiveTab] = useState('summary'); + const [userData, setUserData] = useState(null); + const [loading, setLoading] = useState(true); + const navigate = useNavigate(); + const { logout } = useContext(AuthContext); + + useEffect(() => { + const fetchData = async () => { + try { + const token = localStorage.getItem('access_token'); + if (!token) { + logout(); + navigate('/login'); + return; + } + + const headers = { Authorization: `Bearer ${token}` }; + + const userRes = await axios.get('http://localhost:5000/api/auth/me', { headers }); + + // Моделируем остальные данные + const serversRes = { data: { servers: [] } }; + const ticketsRes = { data: { tickets: [] } }; + + setUserData({ + user: userRes.data.user, + balance: 1500, // Пример + servers: serversRes.data.servers, + tickets: ticketsRes.data.tickets, + }); + } catch (err) { + console.error('Ошибка загрузки данных:', err); + logout(); + navigate('/login'); + } finally { + setLoading(false); + } + }; + fetchData(); + }, [logout, navigate]); + + if (loading) { + return ( +
+

Загрузка...

+
+ ); + } + + if (!userData || !userData.user) { + return null; + } + + const isOperator = userData.user.operator === 1; -const MainPage = () => { return ( -
-
-

- Добро пожаловать в личный кабинет! -

-

- Здесь будет информация о твоих проектах и статистика. -

-
- - Выйти - -
+
+
+ +
+ +
+ + } /> + } /> + } /> + } /> + } /> + + {isOperator && ( + <> + } /> + } /> + + )} +
); }; -export default MainPage; \ No newline at end of file +export default Dashboard; \ No newline at end of file diff --git a/ospabhost/frontend/src/pages/dashboard/servers.tsx b/ospabhost/frontend/src/pages/dashboard/servers.tsx new file mode 100644 index 0000000..c010267 --- /dev/null +++ b/ospabhost/frontend/src/pages/dashboard/servers.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +interface ServersProps { + servers: unknown[]; +} + +const Servers: React.FC = ({ servers }) => { + return ( +
+

Серверы

+ {servers.length === 0 ? ( +

У вас пока нет активных серверов.

+ ) : ( +

Список ваших серверов будет здесь...

+ )} +
+ ); +}; + +export default Servers; \ No newline at end of file diff --git a/ospabhost/frontend/src/pages/dashboard/settings.tsx b/ospabhost/frontend/src/pages/dashboard/settings.tsx new file mode 100644 index 0000000..ba88322 --- /dev/null +++ b/ospabhost/frontend/src/pages/dashboard/settings.tsx @@ -0,0 +1,12 @@ +const Settings = () => { + return ( +
+

Настройки аккаунта

+

+ Здесь вы сможете изменить свои личные данные, email и пароль. +

+
+ ); +}; + +export default Settings; \ No newline at end of file diff --git a/ospabhost/frontend/src/pages/dashboard/summary.tsx b/ospabhost/frontend/src/pages/dashboard/summary.tsx new file mode 100644 index 0000000..d88dd41 --- /dev/null +++ b/ospabhost/frontend/src/pages/dashboard/summary.tsx @@ -0,0 +1,37 @@ +import { Link } from 'react-router-dom'; + +import type { UserData } from './types'; + +interface SummaryProps { + userData: UserData; +} + +const Summary = ({ userData }: SummaryProps) => { + return ( +
+

Сводка по аккаунту

+
+
+

Баланс:

+

₽ {userData.balance.toFixed(2)}

+ Пополнить баланс → +
+
+

Активные серверы:

+

{userData.servers.length}

+ Управлять → +
+
+

Открытые тикеты:

+

{userData.tickets.length}

+ Служба поддержки → +
+
+

+ Добро пожаловать в ваш личный кабинет, {userData.user?.username || 'пользователь'}! Здесь вы можете быстро получить доступ к основным разделам. +

+
+ ); +}; + +export default Summary; \ No newline at end of file diff --git a/ospabhost/frontend/src/pages/dashboard/ticketresponse.tsx b/ospabhost/frontend/src/pages/dashboard/ticketresponse.tsx new file mode 100644 index 0000000..c0df09f --- /dev/null +++ b/ospabhost/frontend/src/pages/dashboard/ticketresponse.tsx @@ -0,0 +1,10 @@ +const TicketResponse = () => { + return ( +
+

Ответы на тикеты

+

Здесь будут отображаться тикеты для ответов.

+
+ ); +}; + +export default TicketResponse; \ No newline at end of file diff --git a/ospabhost/frontend/src/pages/dashboard/tickets.tsx b/ospabhost/frontend/src/pages/dashboard/tickets.tsx new file mode 100644 index 0000000..21543dc --- /dev/null +++ b/ospabhost/frontend/src/pages/dashboard/tickets.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +interface TicketsProps { + tickets: unknown[]; +} + +const Tickets: React.FC = ({ tickets }) => { + return ( +
+

Тикеты поддержки

+ {tickets.length === 0 ? ( +

У вас пока нет открытых тикетов.

+ ) : ( +

Список ваших тикетов будет здесь...

+ )} +
+ ); +}; + +export default Tickets; \ No newline at end of file diff --git a/ospabhost/frontend/src/pages/dashboard/types.ts b/ospabhost/frontend/src/pages/dashboard/types.ts new file mode 100644 index 0000000..cebd8c5 --- /dev/null +++ b/ospabhost/frontend/src/pages/dashboard/types.ts @@ -0,0 +1,11 @@ +export interface User { + username: string; + operator: number; +} + +export interface UserData { + user: User; + balance: number; + servers: unknown[]; + tickets: unknown[]; +} \ No newline at end of file diff --git a/ospabhost/frontend/src/pages/login.tsx b/ospabhost/frontend/src/pages/login.tsx index 73f31e8..107a779 100644 --- a/ospabhost/frontend/src/pages/login.tsx +++ b/ospabhost/frontend/src/pages/login.tsx @@ -1,33 +1,34 @@ import { useState } from 'react'; import { Link, useNavigate } from 'react-router-dom'; import axios from 'axios'; +import useAuth from '../context/useAuth'; const LoginPage = () => { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); + const [error, setError] = useState(''); const navigate = useNavigate(); + const { login } = useAuth(); const handleLogin = async (e: React.FormEvent) => { e.preventDefault(); + setError(''); + try { const response = await axios.post('http://localhost:5000/api/auth/login', { email: email, password: password, }); - // Сохраняем токен в localStorage - localStorage.setItem('access_token', response.data.token); - localStorage.setItem('isLoggedIn', 'true'); - console.log('Успешный вход:', response.data); - navigate('/dashboard'); // Перенаправляем на личный кабинет - } catch (error) { - let errMsg = 'Ошибка входа. Проверьте правильность email и пароля.'; - if (axios.isAxiosError(error)) { - errMsg = error.response?.data?.message || errMsg; - console.error('Ошибка входа:', error.response?.data || error.message); + + login(response.data.token); + navigate('/dashboard/mainpage'); + + } catch (err) { + if (axios.isAxiosError(err) && err.response) { + setError(err.response.data.message || 'Неизвестная ошибка входа.'); } else { - console.error('Ошибка входа:', error); + setError('Произошла ошибка сети. Пожалуйста, попробуйте позже.'); } - alert(errMsg); } }; @@ -57,6 +58,9 @@ const LoginPage = () => { Войти + {error && ( +

{error}

+ )}

Нет аккаунта?{' '} diff --git a/ospabhost/frontend/src/pages/register.tsx b/ospabhost/frontend/src/pages/register.tsx index ff8d959..5ce1e0d 100644 --- a/ospabhost/frontend/src/pages/register.tsx +++ b/ospabhost/frontend/src/pages/register.tsx @@ -6,27 +6,30 @@ const RegisterPage = () => { const [username, setUsername] = useState(''); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); + const [error, setError] = useState(''); const navigate = useNavigate(); const handleRegister = async (e: React.FormEvent) => { e.preventDefault(); + setError(''); // Очищаем предыдущие ошибки + try { - const response = await axios.post('http://localhost:5000/api/auth/register', { + await axios.post('http://localhost:5000/api/auth/register', { username: username, email: email, password: password }); - console.log('Успешная регистрация:', response.data); - navigate('/login'); // Перенаправляем пользователя на страницу входа - } catch (error) { - let errMsg = 'Ошибка регистрации. Пожалуйста, попробуйте снова.'; - if (axios.isAxiosError(error)) { - errMsg = error.response?.data?.message || errMsg; - console.error('Ошибка регистрации:', error.response?.data || error.message); + + alert('Регистрация прошла успешно! Теперь вы можете войти.'); + navigate('/login'); + + } catch (err) { + if (axios.isAxiosError(err) && err.response) { + const errorMsg = err.response.data.message || 'Неизвестная ошибка регистрации.'; + setError(errorMsg); } else { - console.error('Ошибка регистрации:', error); + setError('Произошла ошибка сети. Пожалуйста, попробуйте позже.'); } - alert(errMsg); } }; @@ -63,6 +66,9 @@ const RegisterPage = () => { Зарегистрироваться + {error && ( +

{error}

+ )}

Уже есть аккаунт?{' '}