From 09b98ceeca9e352126d1223a967c5d9285136314 Mon Sep 17 00:00:00 2001
From: gailgou <717389475@qq.com>
Date: Wed, 29 Apr 2026 09:33:58 +0800
Subject: [PATCH] pet1
---
.gitignore | 1 +
client/index.html | 13 +
client/package-lock.json | 2662 +++++++++++++++++++++++++++
client/package.json | 24 +
client/src/App.jsx | 153 ++
client/src/components/Header.jsx | 156 ++
client/src/index.css | 396 ++++
client/src/main.jsx | 17 +
client/src/pages/DailyRecords.jsx | 506 +++++
client/src/pages/Dashboard.jsx | 208 +++
client/src/pages/Login.jsx | 81 +
client/src/pages/MedicalRecords.jsx | 488 +++++
client/src/pages/Moments.jsx | 450 +++++
client/src/pages/Pets.jsx | 285 +++
client/src/pages/Profile.jsx | 220 +++
client/src/pages/Register.jsx | 113 ++
client/src/pages/Reminders.jsx | 391 ++++
client/src/services/api.js | 88 +
client/src/store/authStore.js | 40 +
client/vite.config.js | 15 +
package.json | 30 +
server/.env | 4 +
server/.env.example | 4 +
server/config/db.js | 13 +
server/middleware/auth.js | 25 +
server/models/DailyRecord.js | 99 +
server/models/MedicalRecord.js | 100 +
server/models/Pet.js | 52 +
server/models/Post.js | 56 +
server/models/Reminder.js | 63 +
server/models/User.js | 57 +
server/package-lock.json | 1846 +++++++++++++++++++
server/package.json | 27 +
server/routes/auth.js | 203 ++
server/routes/dailyRecords.js | 183 ++
server/routes/medicalRecords.js | 259 +++
server/routes/pets.js | 204 ++
server/routes/posts.js | 333 ++++
server/routes/reminders.js | 255 +++
server/server.js | 57 +
40 files changed, 10177 insertions(+)
create mode 100644 .gitignore
create mode 100644 client/index.html
create mode 100644 client/package-lock.json
create mode 100644 client/package.json
create mode 100644 client/src/App.jsx
create mode 100644 client/src/components/Header.jsx
create mode 100644 client/src/index.css
create mode 100644 client/src/main.jsx
create mode 100644 client/src/pages/DailyRecords.jsx
create mode 100644 client/src/pages/Dashboard.jsx
create mode 100644 client/src/pages/Login.jsx
create mode 100644 client/src/pages/MedicalRecords.jsx
create mode 100644 client/src/pages/Moments.jsx
create mode 100644 client/src/pages/Pets.jsx
create mode 100644 client/src/pages/Profile.jsx
create mode 100644 client/src/pages/Register.jsx
create mode 100644 client/src/pages/Reminders.jsx
create mode 100644 client/src/services/api.js
create mode 100644 client/src/store/authStore.js
create mode 100644 client/vite.config.js
create mode 100644 package.json
create mode 100644 server/.env
create mode 100644 server/.env.example
create mode 100644 server/config/db.js
create mode 100644 server/middleware/auth.js
create mode 100644 server/models/DailyRecord.js
create mode 100644 server/models/MedicalRecord.js
create mode 100644 server/models/Pet.js
create mode 100644 server/models/Post.js
create mode 100644 server/models/Reminder.js
create mode 100644 server/models/User.js
create mode 100644 server/package-lock.json
create mode 100644 server/package.json
create mode 100644 server/routes/auth.js
create mode 100644 server/routes/dailyRecords.js
create mode 100644 server/routes/medicalRecords.js
create mode 100644 server/routes/pets.js
create mode 100644 server/routes/posts.js
create mode 100644 server/routes/reminders.js
create mode 100644 server/server.js
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..40b878d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+node_modules/
\ No newline at end of file
diff --git a/client/index.html b/client/index.html
new file mode 100644
index 0000000..026a353
--- /dev/null
+++ b/client/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ 宠物日常管理平台
+
+
+
+
+
+
\ No newline at end of file
diff --git a/client/package-lock.json b/client/package-lock.json
new file mode 100644
index 0000000..e624f6e
--- /dev/null
+++ b/client/package-lock.json
@@ -0,0 +1,2662 @@
+{
+ "name": "pet-management-client",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "pet-management-client",
+ "version": "1.0.0",
+ "dependencies": {
+ "@ant-design/icons": "^5.2.6",
+ "antd": "^5.9.0",
+ "axios": "^1.5.0",
+ "dayjs": "^1.11.9",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-router-dom": "^6.15.0",
+ "zustand": "^4.4.1"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-react": "^4.0.4",
+ "vite": "^4.4.9"
+ }
+ },
+ "node_modules/@ant-design/colors": {
+ "version": "7.2.1",
+ "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-7.2.1.tgz",
+ "integrity": "sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@ant-design/fast-color": "^2.0.6"
+ }
+ },
+ "node_modules/@ant-design/cssinjs": {
+ "version": "1.24.0",
+ "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-1.24.0.tgz",
+ "integrity": "sha512-K4cYrJBsgvL+IoozUXYjbT6LHHNt+19a9zkvpBPxLjFHas1UpPM2A5MlhROb0BT8N8WoavM5VsP9MeSeNK/3mg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "@emotion/hash": "^0.8.0",
+ "@emotion/unitless": "^0.7.5",
+ "classnames": "^2.3.1",
+ "csstype": "^3.1.3",
+ "rc-util": "^5.35.0",
+ "stylis": "^4.3.4"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/@ant-design/cssinjs-utils": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.3.tgz",
+ "integrity": "sha512-nOoQMLW1l+xR1Co8NFVYiP8pZp3VjIIzqV6D6ShYF2ljtdwWJn5WSsH+7kvCktXL/yhEtWURKOfH5Xz/gzlwsg==",
+ "license": "MIT",
+ "dependencies": {
+ "@ant-design/cssinjs": "^1.21.0",
+ "@babel/runtime": "^7.23.2",
+ "rc-util": "^5.38.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@ant-design/fast-color": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-2.0.6.tgz",
+ "integrity": "sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=8.x"
+ }
+ },
+ "node_modules/@ant-design/icons": {
+ "version": "5.6.1",
+ "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-5.6.1.tgz",
+ "integrity": "sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==",
+ "license": "MIT",
+ "dependencies": {
+ "@ant-design/colors": "^7.0.0",
+ "@ant-design/icons-svg": "^4.4.0",
+ "@babel/runtime": "^7.24.8",
+ "classnames": "^2.2.6",
+ "rc-util": "^5.31.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/@ant-design/icons-svg": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz",
+ "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==",
+ "license": "MIT"
+ },
+ "node_modules/@ant-design/react-slick": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-1.1.2.tgz",
+ "integrity": "sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.4",
+ "classnames": "^2.2.5",
+ "json2mq": "^0.2.0",
+ "resize-observer-polyfill": "^1.5.1",
+ "throttle-debounce": "^5.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
+ "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz",
+ "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
+ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.0",
+ "@babel/generator": "^7.29.0",
+ "@babel/helper-compilation-targets": "^7.28.6",
+ "@babel/helper-module-transforms": "^7.28.6",
+ "@babel/helpers": "^7.28.6",
+ "@babel/parser": "^7.29.0",
+ "@babel/template": "^7.28.6",
+ "@babel/traverse": "^7.29.0",
+ "@babel/types": "^7.29.0",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.29.1",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
+ "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.0",
+ "@babel/types": "^7.29.0",
+ "@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-compilation-targets": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
+ "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.28.6",
+ "@babel/helper-validator-option": "^7.27.1",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
+ "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.28.6",
+ "@babel/types": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
+ "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.28.6",
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "@babel/traverse": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz",
+ "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==",
+ "dev": true,
+ "license": "MIT",
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz",
+ "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.29.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz",
+ "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.0"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
+ "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
+ "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
+ "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
+ "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.28.6",
+ "@babel/parser": "^7.28.6",
+ "@babel/types": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz",
+ "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.0",
+ "@babel/generator": "^7.29.0",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.29.0",
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.29.0",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
+ "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@emotion/hash": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
+ "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/unitless": {
+ "version": "0.7.5",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
+ "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==",
+ "license": "MIT"
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
+ "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
+ "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
+ "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
+ "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
+ "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
+ "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
+ "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
+ "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
+ "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
+ "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
+ "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
+ "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
+ "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
+ "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
+ "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
+ "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
+ "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
+ "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
+ "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
+ "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
+ "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
+ "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "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",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "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"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "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.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@rc-component/async-validator": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-5.1.0.tgz",
+ "integrity": "sha512-n4HcR5siNUXRX23nDizbZBQPO0ZM/5oTtmKZ6/eqL0L2bo747cklFdZGRN2f+c9qWGICwDzrhW0H7tE9PptdcA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.24.4"
+ },
+ "engines": {
+ "node": ">=14.x"
+ }
+ },
+ "node_modules/@rc-component/color-picker": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-2.0.1.tgz",
+ "integrity": "sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@ant-design/fast-color": "^2.0.6",
+ "@babel/runtime": "^7.23.6",
+ "classnames": "^2.2.6",
+ "rc-util": "^5.38.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/context": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/context/-/context-1.4.0.tgz",
+ "integrity": "sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "rc-util": "^5.27.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/mini-decimal": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@rc-component/mini-decimal/-/mini-decimal-1.1.3.tgz",
+ "integrity": "sha512-bk/FJ09fLf+NLODMAFll6CfYrHPBioTedhW6lxDBuuWucJEqFUd4l/D/5JgIi3dina6sYahB8iuPAZTNz2pMxw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.0"
+ },
+ "engines": {
+ "node": ">=8.x"
+ }
+ },
+ "node_modules/@rc-component/mutate-observer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/mutate-observer/-/mutate-observer-1.1.0.tgz",
+ "integrity": "sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.0",
+ "classnames": "^2.3.2",
+ "rc-util": "^5.24.4"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/portal": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/portal/-/portal-1.1.2.tgz",
+ "integrity": "sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.0",
+ "classnames": "^2.3.2",
+ "rc-util": "^5.24.4"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/qrcode": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/qrcode/-/qrcode-1.1.1.tgz",
+ "integrity": "sha512-LfLGNymzKdUPjXUbRP+xOhIWY4jQ+YMj5MmWAcgcAq1Ij8XP7tRmAXqyuv96XvLUBE/5cA8hLFl9eO1JQMujrA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/tour": {
+ "version": "1.15.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/tour/-/tour-1.15.1.tgz",
+ "integrity": "sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.0",
+ "@rc-component/portal": "^1.0.0-9",
+ "@rc-component/trigger": "^2.0.0",
+ "classnames": "^2.3.2",
+ "rc-util": "^5.24.4"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/trigger": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-2.3.1.tgz",
+ "integrity": "sha512-ORENF39PeXTzM+gQEshuk460Z8N4+6DkjpxlpE7Q3gYy1iBpLrx0FOJz3h62ryrJZ/3zCAUIkT1Pb/8hHWpb3A==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.23.2",
+ "@rc-component/portal": "^1.1.0",
+ "classnames": "^2.3.2",
+ "rc-motion": "^2.0.0",
+ "rc-resize-observer": "^1.3.1",
+ "rc-util": "^5.44.0"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@remix-run/router": {
+ "version": "1.23.2",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz",
+ "integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-beta.27",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
+ "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
+ "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.2"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
+ "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.28.0",
+ "@babel/plugin-transform-react-jsx-self": "^7.27.1",
+ "@babel/plugin-transform-react-jsx-source": "^7.27.1",
+ "@rolldown/pluginutils": "1.0.0-beta.27",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.17.0"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
+ }
+ },
+ "node_modules/antd": {
+ "version": "5.29.3",
+ "resolved": "https://registry.npmjs.org/antd/-/antd-5.29.3.tgz",
+ "integrity": "sha512-3DdbGCa9tWAJGcCJ6rzR8EJFsv2CtyEbkVabZE14pfgUHfCicWCj0/QzQVLDYg8CPfQk9BH7fHCoTXHTy7MP/A==",
+ "license": "MIT",
+ "dependencies": {
+ "@ant-design/colors": "^7.2.1",
+ "@ant-design/cssinjs": "^1.23.0",
+ "@ant-design/cssinjs-utils": "^1.1.3",
+ "@ant-design/fast-color": "^2.0.6",
+ "@ant-design/icons": "^5.6.1",
+ "@ant-design/react-slick": "~1.1.2",
+ "@babel/runtime": "^7.26.0",
+ "@rc-component/color-picker": "~2.0.1",
+ "@rc-component/mutate-observer": "^1.1.0",
+ "@rc-component/qrcode": "~1.1.0",
+ "@rc-component/tour": "~1.15.1",
+ "@rc-component/trigger": "^2.3.0",
+ "classnames": "^2.5.1",
+ "copy-to-clipboard": "^3.3.3",
+ "dayjs": "^1.11.11",
+ "rc-cascader": "~3.34.0",
+ "rc-checkbox": "~3.5.0",
+ "rc-collapse": "~3.9.0",
+ "rc-dialog": "~9.6.0",
+ "rc-drawer": "~7.3.0",
+ "rc-dropdown": "~4.2.1",
+ "rc-field-form": "~2.7.1",
+ "rc-image": "~7.12.0",
+ "rc-input": "~1.8.0",
+ "rc-input-number": "~9.5.0",
+ "rc-mentions": "~2.20.0",
+ "rc-menu": "~9.16.1",
+ "rc-motion": "^2.9.5",
+ "rc-notification": "~5.6.4",
+ "rc-pagination": "~5.1.0",
+ "rc-picker": "~4.11.3",
+ "rc-progress": "~4.0.0",
+ "rc-rate": "~2.13.1",
+ "rc-resize-observer": "^1.4.3",
+ "rc-segmented": "~2.7.0",
+ "rc-select": "~14.16.8",
+ "rc-slider": "~11.1.9",
+ "rc-steps": "~6.0.1",
+ "rc-switch": "~4.1.0",
+ "rc-table": "~7.54.0",
+ "rc-tabs": "~15.7.0",
+ "rc-textarea": "~1.10.2",
+ "rc-tooltip": "~6.4.0",
+ "rc-tree": "~5.13.1",
+ "rc-tree-select": "~5.27.0",
+ "rc-upload": "~4.11.0",
+ "rc-util": "^5.44.4",
+ "scroll-into-view-if-needed": "^3.1.0",
+ "throttle-debounce": "^5.0.2"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ant-design"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "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/axios": {
+ "version": "1.15.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.2.tgz",
+ "integrity": "sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A==",
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.15.11",
+ "form-data": "^4.0.5",
+ "proxy-from-env": "^2.1.0"
+ }
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.10.24",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.24.tgz",
+ "integrity": "sha512-I2NkZOOrj2XuguvWCK6OVh9GavsNjZjK908Rq3mIBK25+GD8vPX5w2WdxVqnQ7xx3SrZJiCiZFu+/Oz50oSYSA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.2",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz",
+ "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.10.12",
+ "caniuse-lite": "^1.0.30001782",
+ "electron-to-chromium": "^1.5.328",
+ "node-releases": "^2.0.36",
+ "update-browserslist-db": "^1.2.3"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "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",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001791",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001791.tgz",
+ "integrity": "sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/classnames": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
+ "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
+ "license": "MIT"
+ },
+ "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/compute-scroll-into-view": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz",
+ "integrity": "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==",
+ "license": "MIT"
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/copy-to-clipboard": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
+ "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==",
+ "license": "MIT",
+ "dependencies": {
+ "toggle-selection": "^1.0.6"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "license": "MIT"
+ },
+ "node_modules/dayjs": {
+ "version": "1.11.20",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz",
+ "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==",
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "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/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.344",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.344.tgz",
+ "integrity": "sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "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==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "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==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
+ "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/android-arm": "0.18.20",
+ "@esbuild/android-arm64": "0.18.20",
+ "@esbuild/android-x64": "0.18.20",
+ "@esbuild/darwin-arm64": "0.18.20",
+ "@esbuild/darwin-x64": "0.18.20",
+ "@esbuild/freebsd-arm64": "0.18.20",
+ "@esbuild/freebsd-x64": "0.18.20",
+ "@esbuild/linux-arm": "0.18.20",
+ "@esbuild/linux-arm64": "0.18.20",
+ "@esbuild/linux-ia32": "0.18.20",
+ "@esbuild/linux-loong64": "0.18.20",
+ "@esbuild/linux-mips64el": "0.18.20",
+ "@esbuild/linux-ppc64": "0.18.20",
+ "@esbuild/linux-riscv64": "0.18.20",
+ "@esbuild/linux-s390x": "0.18.20",
+ "@esbuild/linux-x64": "0.18.20",
+ "@esbuild/netbsd-x64": "0.18.20",
+ "@esbuild/openbsd-x64": "0.18.20",
+ "@esbuild/sunos-x64": "0.18.20",
+ "@esbuild/win32-arm64": "0.18.20",
+ "@esbuild/win32-ia32": "0.18.20",
+ "@esbuild/win32-x64": "0.18.20"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
+ "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+ "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/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz",
+ "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "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==",
+ "license": "MIT"
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json2mq": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz",
+ "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==",
+ "license": "MIT",
+ "dependencies": {
+ "string-convert": "^0.2.0"
+ }
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "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",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "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/ms": {
+ "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": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.38",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz",
+ "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/postcss": {
+ "version": "8.5.12",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz",
+ "integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==",
+ "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/proxy-from-env": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
+ "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/rc-cascader": {
+ "version": "3.34.0",
+ "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.34.0.tgz",
+ "integrity": "sha512-KpXypcvju9ptjW9FaN2NFcA2QH9E9LHKq169Y0eWtH4e/wHQ5Wh5qZakAgvb8EKZ736WZ3B0zLLOBsrsja5Dag==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.25.7",
+ "classnames": "^2.3.1",
+ "rc-select": "~14.16.2",
+ "rc-tree": "~5.13.0",
+ "rc-util": "^5.43.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-checkbox": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-3.5.0.tgz",
+ "integrity": "sha512-aOAQc3E98HteIIsSqm6Xk2FPKIER6+5vyEFMZfo73TqM+VVAIqOkHoPjgKLqSNtVLWScoaM7vY2ZrGEheI79yg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "^2.3.2",
+ "rc-util": "^5.25.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-collapse": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/rc-collapse/-/rc-collapse-3.9.0.tgz",
+ "integrity": "sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "2.x",
+ "rc-motion": "^2.3.4",
+ "rc-util": "^5.27.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-dialog": {
+ "version": "9.6.0",
+ "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-9.6.0.tgz",
+ "integrity": "sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "@rc-component/portal": "^1.0.0-8",
+ "classnames": "^2.2.6",
+ "rc-motion": "^2.3.0",
+ "rc-util": "^5.21.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-drawer": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-7.3.0.tgz",
+ "integrity": "sha512-DX6CIgiBWNpJIMGFO8BAISFkxiuKitoizooj4BDyee8/SnBn0zwO2FHrNDpqqepj0E/TFTDpmEBCyFuTgC7MOg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.23.9",
+ "@rc-component/portal": "^1.1.1",
+ "classnames": "^2.2.6",
+ "rc-motion": "^2.6.1",
+ "rc-util": "^5.38.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-dropdown": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-4.2.1.tgz",
+ "integrity": "sha512-YDAlXsPv3I1n42dv1JpdM7wJ+gSUBfeyPK59ZpBD9jQhK9jVuxpjj3NmWQHOBceA1zEPVX84T2wbdb2SD0UjmA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@rc-component/trigger": "^2.0.0",
+ "classnames": "^2.2.6",
+ "rc-util": "^5.44.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.11.0",
+ "react-dom": ">=16.11.0"
+ }
+ },
+ "node_modules/rc-field-form": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-2.7.1.tgz",
+ "integrity": "sha512-vKeSifSJ6HoLaAB+B8aq/Qgm8a3dyxROzCtKNCsBQgiverpc4kWDQihoUwzUj+zNWJOykwSY4dNX3QrGwtVb9A==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.0",
+ "@rc-component/async-validator": "^5.0.3",
+ "rc-util": "^5.32.2"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-image": {
+ "version": "7.12.0",
+ "resolved": "https://registry.npmjs.org/rc-image/-/rc-image-7.12.0.tgz",
+ "integrity": "sha512-cZ3HTyyckPnNnUb9/DRqduqzLfrQRyi+CdHjdqgsyDpI3Ln5UX1kXnAhPBSJj9pVRzwRFgqkN7p9b6HBDjmu/Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.2",
+ "@rc-component/portal": "^1.0.2",
+ "classnames": "^2.2.6",
+ "rc-dialog": "~9.6.0",
+ "rc-motion": "^2.6.2",
+ "rc-util": "^5.34.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-input": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/rc-input/-/rc-input-1.8.0.tgz",
+ "integrity": "sha512-KXvaTbX+7ha8a/k+eg6SYRVERK0NddX8QX7a7AnRvUa/rEH0CNMlpcBzBkhI0wp2C8C4HlMoYl8TImSN+fuHKA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "classnames": "^2.2.1",
+ "rc-util": "^5.18.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/rc-input-number": {
+ "version": "9.5.0",
+ "resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-9.5.0.tgz",
+ "integrity": "sha512-bKaEvB5tHebUURAEXw35LDcnRZLq3x1k7GxfAqBMzmpHkDGzjAtnUL8y4y5N15rIFIg5IJgwr211jInl3cipag==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "@rc-component/mini-decimal": "^1.0.1",
+ "classnames": "^2.2.5",
+ "rc-input": "~1.8.0",
+ "rc-util": "^5.40.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-mentions": {
+ "version": "2.20.0",
+ "resolved": "https://registry.npmjs.org/rc-mentions/-/rc-mentions-2.20.0.tgz",
+ "integrity": "sha512-w8HCMZEh3f0nR8ZEd466ATqmXFCMGMN5UFCzEUL0bM/nGw/wOS2GgRzKBcm19K++jDyuWCOJOdgcKGXU3fXfbQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.22.5",
+ "@rc-component/trigger": "^2.0.0",
+ "classnames": "^2.2.6",
+ "rc-input": "~1.8.0",
+ "rc-menu": "~9.16.0",
+ "rc-textarea": "~1.10.0",
+ "rc-util": "^5.34.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-menu": {
+ "version": "9.16.1",
+ "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-9.16.1.tgz",
+ "integrity": "sha512-ghHx6/6Dvp+fw8CJhDUHFHDJ84hJE3BXNCzSgLdmNiFErWSOaZNsihDAsKq9ByTALo/xkNIwtDFGIl6r+RPXBg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "@rc-component/trigger": "^2.0.0",
+ "classnames": "2.x",
+ "rc-motion": "^2.4.3",
+ "rc-overflow": "^1.3.1",
+ "rc-util": "^5.27.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-motion": {
+ "version": "2.9.5",
+ "resolved": "https://registry.npmjs.org/rc-motion/-/rc-motion-2.9.5.tgz",
+ "integrity": "sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "classnames": "^2.2.1",
+ "rc-util": "^5.44.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-notification": {
+ "version": "5.6.4",
+ "resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-5.6.4.tgz",
+ "integrity": "sha512-KcS4O6B4qzM3KH7lkwOB7ooLPZ4b6J+VMmQgT51VZCeEcmghdeR4IrMcFq0LG+RPdnbe/ArT086tGM8Snimgiw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "2.x",
+ "rc-motion": "^2.9.0",
+ "rc-util": "^5.20.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-overflow": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/rc-overflow/-/rc-overflow-1.5.0.tgz",
+ "integrity": "sha512-Lm/v9h0LymeUYJf0x39OveU52InkdRXqnn2aYXfWmo8WdOonIKB2kfau+GF0fWq6jPgtdO9yMqveGcK6aIhJmg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "classnames": "^2.2.1",
+ "rc-resize-observer": "^1.0.0",
+ "rc-util": "^5.37.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-pagination": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/rc-pagination/-/rc-pagination-5.1.0.tgz",
+ "integrity": "sha512-8416Yip/+eclTFdHXLKTxZvn70duYVGTvUUWbckCCZoIl3jagqke3GLsFrMs0bsQBikiYpZLD9206Ej4SOdOXQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "^2.3.2",
+ "rc-util": "^5.38.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-picker": {
+ "version": "4.11.3",
+ "resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-4.11.3.tgz",
+ "integrity": "sha512-MJ5teb7FlNE0NFHTncxXQ62Y5lytq6sh5nUw0iH8OkHL/TjARSEvSHpr940pWgjGANpjCwyMdvsEV55l5tYNSg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.24.7",
+ "@rc-component/trigger": "^2.0.0",
+ "classnames": "^2.2.1",
+ "rc-overflow": "^1.3.2",
+ "rc-resize-observer": "^1.4.0",
+ "rc-util": "^5.43.0"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "date-fns": ">= 2.x",
+ "dayjs": ">= 1.x",
+ "luxon": ">= 3.x",
+ "moment": ">= 2.x",
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ },
+ "peerDependenciesMeta": {
+ "date-fns": {
+ "optional": true
+ },
+ "dayjs": {
+ "optional": true
+ },
+ "luxon": {
+ "optional": true
+ },
+ "moment": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/rc-progress": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-4.0.0.tgz",
+ "integrity": "sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "^2.2.6",
+ "rc-util": "^5.16.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-rate": {
+ "version": "2.13.1",
+ "resolved": "https://registry.npmjs.org/rc-rate/-/rc-rate-2.13.1.tgz",
+ "integrity": "sha512-QUhQ9ivQ8Gy7mtMZPAjLbxBt5y9GRp65VcUyGUMF3N3fhiftivPHdpuDIaWIMOTEprAjZPC08bls1dQB+I1F2Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "^2.2.5",
+ "rc-util": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-resize-observer": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz",
+ "integrity": "sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.20.7",
+ "classnames": "^2.2.1",
+ "rc-util": "^5.44.1",
+ "resize-observer-polyfill": "^1.5.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-segmented": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rc-segmented/-/rc-segmented-2.7.1.tgz",
+ "integrity": "sha512-izj1Nw/Dw2Vb7EVr+D/E9lUTkBe+kKC+SAFSU9zqr7WV2W5Ktaa9Gc7cB2jTqgk8GROJayltaec+DBlYKc6d+g==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "classnames": "^2.2.1",
+ "rc-motion": "^2.4.4",
+ "rc-util": "^5.17.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/rc-select": {
+ "version": "14.16.8",
+ "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.16.8.tgz",
+ "integrity": "sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "@rc-component/trigger": "^2.1.1",
+ "classnames": "2.x",
+ "rc-motion": "^2.0.1",
+ "rc-overflow": "^1.3.1",
+ "rc-util": "^5.16.1",
+ "rc-virtual-list": "^3.5.2"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-dom": "*"
+ }
+ },
+ "node_modules/rc-slider": {
+ "version": "11.1.9",
+ "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-11.1.9.tgz",
+ "integrity": "sha512-h8IknhzSh3FEM9u8ivkskh+Ef4Yo4JRIY2nj7MrH6GQmrwV6mcpJf5/4KgH5JaVI1H3E52yCdpOlVyGZIeph5A==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "^2.2.5",
+ "rc-util": "^5.36.0"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-steps": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/rc-steps/-/rc-steps-6.0.1.tgz",
+ "integrity": "sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.16.7",
+ "classnames": "^2.2.3",
+ "rc-util": "^5.16.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-switch": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-4.1.0.tgz",
+ "integrity": "sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.21.0",
+ "classnames": "^2.2.1",
+ "rc-util": "^5.30.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-table": {
+ "version": "7.54.0",
+ "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.54.0.tgz",
+ "integrity": "sha512-/wDTkki6wBTjwylwAGjpLKYklKo9YgjZwAU77+7ME5mBoS32Q4nAwoqhA2lSge6fobLW3Tap6uc5xfwaL2p0Sw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "@rc-component/context": "^1.4.0",
+ "classnames": "^2.2.5",
+ "rc-resize-observer": "^1.1.0",
+ "rc-util": "^5.44.3",
+ "rc-virtual-list": "^3.14.2"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-tabs": {
+ "version": "15.7.0",
+ "resolved": "https://registry.npmjs.org/rc-tabs/-/rc-tabs-15.7.0.tgz",
+ "integrity": "sha512-ZepiE+6fmozYdWf/9gVp7k56PKHB1YYoDsKeQA1CBlJ/POIhjkcYiv0AGP0w2Jhzftd3AVvZP/K+V+Lpi2ankA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.2",
+ "classnames": "2.x",
+ "rc-dropdown": "~4.2.0",
+ "rc-menu": "~9.16.0",
+ "rc-motion": "^2.6.2",
+ "rc-resize-observer": "^1.0.0",
+ "rc-util": "^5.34.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-textarea": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/rc-textarea/-/rc-textarea-1.10.2.tgz",
+ "integrity": "sha512-HfaeXiaSlpiSp0I/pvWpecFEHpVysZ9tpDLNkxQbMvMz6gsr7aVZ7FpWP9kt4t7DB+jJXesYS0us1uPZnlRnwQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "^2.2.1",
+ "rc-input": "~1.8.0",
+ "rc-resize-observer": "^1.0.0",
+ "rc-util": "^5.27.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-tooltip": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-6.4.0.tgz",
+ "integrity": "sha512-kqyivim5cp8I5RkHmpsp1Nn/Wk+1oeloMv9c7LXNgDxUpGm+RbXJGL+OPvDlcRnx9DBeOe4wyOIl4OKUERyH1g==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.2",
+ "@rc-component/trigger": "^2.0.0",
+ "classnames": "^2.3.1",
+ "rc-util": "^5.44.3"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-tree": {
+ "version": "5.13.1",
+ "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-5.13.1.tgz",
+ "integrity": "sha512-FNhIefhftobCdUJshO7M8uZTA9F4OPGVXqGfZkkD/5soDeOhwO06T/aKTrg0WD8gRg/pyfq+ql3aMymLHCTC4A==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "2.x",
+ "rc-motion": "^2.0.1",
+ "rc-util": "^5.16.1",
+ "rc-virtual-list": "^3.5.1"
+ },
+ "engines": {
+ "node": ">=10.x"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-dom": "*"
+ }
+ },
+ "node_modules/rc-tree-select": {
+ "version": "5.27.0",
+ "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-5.27.0.tgz",
+ "integrity": "sha512-2qTBTzwIT7LRI1o7zLyrCzmo5tQanmyGbSaGTIf7sYimCklAToVVfpMC6OAldSKolcnjorBYPNSKQqJmN3TCww==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.25.7",
+ "classnames": "2.x",
+ "rc-select": "~14.16.2",
+ "rc-tree": "~5.13.0",
+ "rc-util": "^5.43.0"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-dom": "*"
+ }
+ },
+ "node_modules/rc-upload": {
+ "version": "4.11.0",
+ "resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-4.11.0.tgz",
+ "integrity": "sha512-ZUyT//2JAehfHzjWowqROcwYJKnZkIUGWaTE/VogVrepSl7AFNbQf4+zGfX4zl9Vrj/Jm8scLO0R6UlPDKK4wA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "classnames": "^2.2.5",
+ "rc-util": "^5.2.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-util": {
+ "version": "5.44.4",
+ "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.44.4.tgz",
+ "integrity": "sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "react-is": "^18.2.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-virtual-list": {
+ "version": "3.19.2",
+ "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.19.2.tgz",
+ "integrity": "sha512-Ys6NcjwGkuwkeaWBDqfI3xWuZ7rDiQXlH1o2zLfFzATfEgXcqpk8CkgMfbJD81McqjcJVez25a3kPxCR807evA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.20.0",
+ "classnames": "^2.2.6",
+ "rc-resize-observer": "^1.0.0",
+ "rc-util": "^5.36.0"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/react": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
+ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
+ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.2"
+ },
+ "peerDependencies": {
+ "react": "^18.3.1"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
+ "license": "MIT"
+ },
+ "node_modules/react-refresh": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
+ "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-router": {
+ "version": "6.30.3",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz",
+ "integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==",
+ "license": "MIT",
+ "dependencies": {
+ "@remix-run/router": "1.23.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "6.30.3",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz",
+ "integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==",
+ "license": "MIT",
+ "dependencies": {
+ "@remix-run/router": "1.23.2",
+ "react-router": "6.30.3"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
+ }
+ },
+ "node_modules/resize-observer-polyfill": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
+ "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==",
+ "license": "MIT"
+ },
+ "node_modules/rollup": {
+ "version": "3.30.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.30.0.tgz",
+ "integrity": "sha512-kQvGasUgN+AlWGliFn2POSajRQEsULVYFGTvOZmK06d7vCD+YhZztt70kGk3qaeAXeWYL5eO7zx+rAubBc55eA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=14.18.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.23.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
+ "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "node_modules/scroll-into-view-if-needed": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz",
+ "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==",
+ "license": "MIT",
+ "dependencies": {
+ "compute-scroll-into-view": "^3.0.2"
+ }
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/string-convert": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz",
+ "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==",
+ "license": "MIT"
+ },
+ "node_modules/stylis": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.4.0.tgz",
+ "integrity": "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==",
+ "license": "MIT"
+ },
+ "node_modules/throttle-debounce": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz",
+ "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.22"
+ }
+ },
+ "node_modules/toggle-selection": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
+ "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==",
+ "license": "MIT"
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/use-sync-external-store": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
+ "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "4.5.14",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.14.tgz",
+ "integrity": "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.18.10",
+ "postcss": "^8.4.27",
+ "rollup": "^3.27.1"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ },
+ "peerDependencies": {
+ "@types/node": ">= 14",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/zustand": {
+ "version": "4.5.7",
+ "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz",
+ "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==",
+ "license": "MIT",
+ "dependencies": {
+ "use-sync-external-store": "^1.2.2"
+ },
+ "engines": {
+ "node": ">=12.7.0"
+ },
+ "peerDependencies": {
+ "@types/react": ">=16.8",
+ "immer": ">=9.0.6",
+ "react": ">=16.8"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "immer": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ }
+ }
+ }
+ }
+}
diff --git a/client/package.json b/client/package.json
new file mode 100644
index 0000000..a37bafd
--- /dev/null
+++ b/client/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "pet-management-client",
+ "version": "1.0.0",
+ "private": true,
+ "dependencies": {
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-router-dom": "^6.15.0",
+ "axios": "^1.5.0",
+ "antd": "^5.9.0",
+ "@ant-design/icons": "^5.2.6",
+ "dayjs": "^1.11.9",
+ "zustand": "^4.4.1"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-react": "^4.0.4",
+ "vite": "^4.4.9"
+ },
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ }
+}
\ No newline at end of file
diff --git a/client/src/App.jsx b/client/src/App.jsx
new file mode 100644
index 0000000..9375b17
--- /dev/null
+++ b/client/src/App.jsx
@@ -0,0 +1,153 @@
+import React, { useEffect } from 'react';
+import { Routes, Route, Navigate, useLocation } from 'react-router-dom';
+import { Layout, message } from 'antd';
+import useAuthStore from './store/authStore';
+import Header from './components/Header';
+import Login from './pages/Login';
+import Register from './pages/Register';
+import Dashboard from './pages/Dashboard';
+import Pets from './pages/Pets';
+import DailyRecords from './pages/DailyRecords';
+import Reminders from './pages/Reminders';
+import MedicalRecords from './pages/MedicalRecords';
+import Moments from './pages/Moments';
+import Profile from './pages/Profile';
+
+const { Content } = Layout;
+
+const ProtectedRoute = ({ children }) => {
+ const { isAuthenticated } = useAuthStore();
+ const location = useLocation();
+
+ if (!isAuthenticated) {
+ return ;
+ }
+
+ return children;
+};
+
+const PublicRoute = ({ children }) => {
+ const { isAuthenticated } = useAuthStore();
+ const location = useLocation();
+ const from = location.state?.from?.pathname || '/dashboard';
+
+ if (isAuthenticated) {
+ return ;
+ }
+
+ return children;
+};
+
+function App() {
+ const { isAuthenticated } = useAuthStore();
+ const location = useLocation();
+
+ useEffect(() => {
+ message.config({
+ top: 50,
+ duration: 3,
+ maxCount: 3
+ });
+ }, []);
+
+ const isAuthPage = ['/login', '/register'].includes(location.pathname);
+
+ return (
+
+ {isAuthenticated && !isAuthPage && }
+
+
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+
+ ) : (
+
+ )
+ }
+ />
+
+
+
+ );
+}
+
+export default App;
\ No newline at end of file
diff --git a/client/src/components/Header.jsx b/client/src/components/Header.jsx
new file mode 100644
index 0000000..7d24658
--- /dev/null
+++ b/client/src/components/Header.jsx
@@ -0,0 +1,156 @@
+import React from 'react';
+import { Layout, Menu, Dropdown, Avatar, Badge } from 'antd';
+import { useNavigate, useLocation } from 'react-router-dom';
+import {
+ DashboardOutlined,
+ TeamOutlined,
+ FileTextOutlined,
+ BellOutlined,
+ MedicineBoxOutlined,
+ ShareAltOutlined,
+ UserOutlined,
+ LogoutOutlined,
+ DownOutlined
+} from '@ant-design/icons';
+import useAuthStore from '../store/authStore';
+
+const { Header: AntHeader } = Layout;
+
+const Header = () => {
+ const navigate = useNavigate();
+ const location = useLocation();
+ const { user, logout } = useAuthStore();
+
+ const menuItems = [
+ {
+ key: '/dashboard',
+ icon: ,
+ label: '仪表盘'
+ },
+ {
+ key: '/pets',
+ icon: ,
+ label: '宠物管理'
+ },
+ {
+ key: '/daily-records',
+ icon: ,
+ label: '日常记录'
+ },
+ {
+ key: '/reminders',
+ icon: ,
+ label: '提醒管理'
+ },
+ {
+ key: '/medical-records',
+ icon: ,
+ label: '就医记录'
+ },
+ {
+ key: '/moments',
+ icon: ,
+ label: '朋友圈'
+ }
+ ];
+
+ const handleMenuClick = (e) => {
+ navigate(e.key);
+ };
+
+ const handleLogout = () => {
+ logout();
+ navigate('/login');
+ };
+
+ const userMenuItems = [
+ {
+ key: 'profile',
+ icon: ,
+ label: '个人中心',
+ onClick: () => navigate('/profile')
+ },
+ {
+ type: 'divider'
+ },
+ {
+ key: 'logout',
+ icon: ,
+ label: '退出登录',
+ onClick: handleLogout
+ }
+ ];
+
+ return (
+
+
+
+
+
+
+
+ {
+ e.currentTarget.style.background = '#f5f5f5';
+ }}
+ onMouseLeave={(e) => {
+ e.currentTarget.style.background = 'transparent';
+ }}
+ >
+
}
+ src={user?.avatar}
+ style={{ marginRight: 8 }}
+ />
+
+ {user?.nickname || user?.username}
+
+
+
+
+
+
+ );
+};
+
+export default Header;
\ No newline at end of file
diff --git a/client/src/index.css b/client/src/index.css
new file mode 100644
index 0000000..58442ad
--- /dev/null
+++ b/client/src/index.css
@@ -0,0 +1,396 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
+ 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
+ 'Noto Color Emoji';
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.auth-page {
+ min-height: 100vh;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+}
+
+.auth-card {
+ width: 100%;
+ max-width: 400px;
+ padding: 32px;
+ background: white;
+ border-radius: 12px;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
+}
+
+.auth-title {
+ text-align: center;
+ margin-bottom: 32px;
+ font-size: 24px;
+ font-weight: 600;
+ color: #333;
+}
+
+.auth-title span {
+ color: #1890ff;
+}
+
+.page-card {
+ background: white;
+ border-radius: 8px;
+ padding: 24px;
+ margin-bottom: 24px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+}
+
+.page-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 24px;
+}
+
+.page-title {
+ font-size: 20px;
+ font-weight: 600;
+ color: #333;
+ margin: 0;
+}
+
+.pet-card {
+ cursor: pointer;
+ transition: all 0.3s;
+}
+
+.pet-card:hover {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+ transform: translateY(-2px);
+}
+
+.pet-avatar {
+ width: 80px;
+ height: 80px;
+ border-radius: 50%;
+ object-fit: cover;
+ margin-right: 16px;
+}
+
+.post-card {
+ margin-bottom: 16px;
+}
+
+.post-header {
+ display: flex;
+ align-items: center;
+ margin-bottom: 12px;
+}
+
+.post-user-avatar {
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ margin-right: 12px;
+}
+
+.post-content {
+ margin-bottom: 12px;
+ line-height: 1.6;
+}
+
+.post-images {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ margin-bottom: 12px;
+}
+
+.post-image {
+ width: 200px;
+ height: 200px;
+ object-fit: cover;
+ border-radius: 8px;
+}
+
+.post-actions {
+ display: flex;
+ gap: 24px;
+ padding-top: 12px;
+ border-top: 1px solid #f0f0f0;
+}
+
+.post-action {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ cursor: pointer;
+ color: #666;
+ transition: color 0.3s;
+}
+
+.post-action:hover,
+.post-action.active {
+ color: #1890ff;
+}
+
+.comment-section {
+ margin-top: 16px;
+ padding-top: 16px;
+ border-top: 1px solid #f0f0f0;
+}
+
+.comment-item {
+ display: flex;
+ margin-bottom: 12px;
+}
+
+.comment-avatar {
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+ margin-right: 12px;
+}
+
+.comment-content {
+ flex: 1;
+ background: #f5f5f5;
+ padding: 10px 12px;
+ border-radius: 8px;
+}
+
+.comment-username {
+ font-weight: 500;
+ color: #1890ff;
+ margin-right: 8px;
+}
+
+.comment-input-section {
+ display: flex;
+ gap: 12px;
+ margin-top: 12px;
+}
+
+.dashboard-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ gap: 24px;
+}
+
+.stat-card {
+ background: white;
+ padding: 24px;
+ border-radius: 8px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+ text-align: center;
+}
+
+.stat-value {
+ font-size: 32px;
+ font-weight: 700;
+ color: #1890ff;
+ margin-bottom: 8px;
+}
+
+.stat-label {
+ font-size: 14px;
+ color: #666;
+}
+
+.reminder-item {
+ display: flex;
+ align-items: center;
+ padding: 12px;
+ border-radius: 8px;
+ margin-bottom: 8px;
+ background: #fafafa;
+}
+
+.reminder-item.urgent {
+ background: #fff2f0;
+ border-left: 4px solid #ff4d4f;
+}
+
+.reminder-type {
+ width: 40px;
+ height: 40px;
+ border-radius: 8px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-right: 12px;
+ font-size: 18px;
+}
+
+.reminder-type.疫苗 {
+ background: #e6f7ff;
+ color: #1890ff;
+}
+
+.reminder-type.驱虫 {
+ background: #f6ffed;
+ color: #52c41a;
+}
+
+.reminder-type.体检 {
+ background: #fff7e6;
+ color: #fa8c16;
+}
+
+.reminder-type.其他 {
+ background: #f9f0ff;
+ color: #722ed1;
+}
+
+.medical-record-item {
+ padding: 16px;
+ border-bottom: 1px solid #f0f0f0;
+}
+
+.medical-record-item:last-child {
+ border-bottom: none;
+}
+
+.timeline-custom .ant-timeline-item-content {
+ left: 16px;
+}
+
+.empty-state {
+ text-align: center;
+ padding: 60px 24px;
+ color: #999;
+}
+
+.empty-state .anticon {
+ font-size: 64px;
+ margin-bottom: 16px;
+ color: #d9d9d9;
+}
+
+.loading-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ min-height: 400px;
+}
+
+.modal-content {
+ max-height: 70vh;
+ overflow-y: auto;
+}
+
+.tags-container {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+
+.tag-item {
+ display: inline-flex;
+ align-items: center;
+ padding: 4px 12px;
+ background: #f0f5ff;
+ border-radius: 4px;
+ font-size: 13px;
+ color: #1890ff;
+}
+
+.tag-close {
+ margin-left: 8px;
+ cursor: pointer;
+ opacity: 0.6;
+}
+
+.tag-close:hover {
+ opacity: 1;
+}
+
+.image-upload-container {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 12px;
+}
+
+.image-preview {
+ width: 100px;
+ height: 100px;
+ border-radius: 8px;
+ object-fit: cover;
+ position: relative;
+}
+
+.image-preview img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ border-radius: 8px;
+}
+
+.image-preview .remove-btn {
+ position: absolute;
+ top: -8px;
+ right: -8px;
+ width: 24px;
+ height: 24px;
+ border-radius: 50%;
+ background: #ff4d4f;
+ color: white;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ font-size: 12px;
+}
+
+.nav-item {
+ display: flex;
+ align-items: center;
+ padding: 12px 20px;
+ cursor: pointer;
+ transition: all 0.3s;
+ border-radius: 8px;
+ margin-bottom: 4px;
+}
+
+.nav-item:hover {
+ background: rgba(255, 255, 255, 0.1);
+}
+
+.nav-item.active {
+ background: #1890ff;
+}
+
+.nav-item-icon {
+ font-size: 18px;
+ margin-right: 12px;
+}
+
+.nav-item-text {
+ font-size: 14px;
+}
+
+@media (max-width: 768px) {
+ .page-card {
+ padding: 16px;
+ }
+
+ .page-header {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 16px;
+ }
+
+ .dashboard-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .post-image {
+ width: 100%;
+ height: auto;
+ max-width: 200px;
+ }
+}
\ No newline at end of file
diff --git a/client/src/main.jsx b/client/src/main.jsx
new file mode 100644
index 0000000..ba54496
--- /dev/null
+++ b/client/src/main.jsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import { BrowserRouter } from 'react-router-dom';
+import { ConfigProvider } from 'antd';
+import zhCN from 'antd/locale/zh_CN';
+import App from './App';
+import './index.css';
+
+ReactDOM.createRoot(document.getElementById('root')).render(
+
+
+
+
+
+
+
+);
\ No newline at end of file
diff --git a/client/src/pages/DailyRecords.jsx b/client/src/pages/DailyRecords.jsx
new file mode 100644
index 0000000..d143b0f
--- /dev/null
+++ b/client/src/pages/DailyRecords.jsx
@@ -0,0 +1,506 @@
+import React, { useState, useEffect } from 'react';
+import {
+ Card,
+ List,
+ Button,
+ Modal,
+ Form,
+ Select,
+ DatePicker,
+ TimePicker,
+ Input,
+ InputNumber,
+ Row,
+ Col,
+ Tag,
+ message,
+ Empty,
+ Spin,
+ Divider,
+ Popconfirm
+} from 'antd';
+import { PlusOutlined, DeleteOutlined, EditOutlined } from '@ant-design/icons';
+import { dailyRecordApi, petApi } from '../services/api';
+import dayjs from 'dayjs';
+
+const { Option } = Select;
+const { TextArea } = Input;
+
+const DailyRecords = () => {
+ const [loading, setLoading] = useState(false);
+ const [records, setRecords] = useState([]);
+ const [pets, setPets] = useState([]);
+ const [modalVisible, setModalVisible] = useState(false);
+ const [editingRecord, setEditingRecord] = useState(null);
+ const [selectedPet, setSelectedPet] = useState(null);
+ const [form] = Form.useForm();
+ const [mealsForm] = Form.useForm();
+ const [bowelForm] = Form.useForm();
+ const [exerciseForm] = Form.useForm();
+
+ const [tempMeals, setTempMeals] = useState([]);
+ const [tempBowels, setTempBowels] = useState([]);
+ const [tempExercises, setTempExercises] = useState([]);
+
+ useEffect(() => {
+ fetchData();
+ }, [selectedPet]);
+
+ const fetchData = async () => {
+ setLoading(true);
+ try {
+ const [petsRes, recordsRes] = await Promise.all([
+ petApi.getPets(),
+ dailyRecordApi.getRecords(selectedPet ? { petId: selectedPet } : {})
+ ]);
+ setPets(petsRes.data?.data?.pets || []);
+ setRecords(recordsRes.data?.data?.records || []);
+ } catch (error) {
+ message.error('获取数据失败');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const handleAdd = () => {
+ setEditingRecord(null);
+ form.resetFields();
+ setTempMeals([]);
+ setTempBowels([]);
+ setTempExercises([]);
+ setModalVisible(true);
+ };
+
+ const handleEdit = (record) => {
+ setEditingRecord(record);
+ form.setFieldsValue({
+ petId: record.petId?._id || record.petId,
+ recordDate: dayjs(record.recordDate),
+ notes: record.notes
+ });
+ setTempMeals(record.meals || []);
+ setTempBowels(record.bowelMovements || []);
+ setTempExercises(record.exercises || []);
+ setModalVisible(true);
+ };
+
+ const handleDelete = async (id) => {
+ try {
+ await dailyRecordApi.deleteRecord(id);
+ message.success('记录删除成功');
+ fetchData();
+ } catch (error) {
+ message.error('删除失败');
+ }
+ };
+
+ const handleAddMeal = (values) => {
+ setTempMeals([
+ ...tempMeals,
+ {
+ ...values,
+ time: values.time ? values.time.toDate() : new Date()
+ }
+ ]);
+ mealsForm.resetFields();
+ };
+
+ const handleAddBowel = (values) => {
+ setTempBowels([
+ ...tempBowels,
+ {
+ ...values,
+ time: values.time ? values.time.toDate() : new Date()
+ }
+ ]);
+ bowelForm.resetFields();
+ };
+
+ const handleAddExercise = (values) => {
+ setTempExercises([
+ ...tempExercises,
+ {
+ ...values,
+ time: values.time ? values.time.toDate() : new Date()
+ }
+ ]);
+ exerciseForm.resetFields();
+ };
+
+ const handleSubmit = async (values) => {
+ const data = {
+ ...values,
+ recordDate: values.recordDate ? values.recordDate.toDate() : new Date(),
+ meals: tempMeals,
+ bowelMovements: tempBowels,
+ exercises: tempExercises
+ };
+
+ try {
+ if (editingRecord) {
+ await dailyRecordApi.updateRecord(editingRecord._id, data);
+ message.success('记录更新成功');
+ } else {
+ await dailyRecordApi.createRecord(data);
+ message.success('记录添加成功');
+ }
+ setModalVisible(false);
+ fetchData();
+ } catch (error) {
+ message.error(editingRecord ? '更新失败' : '添加失败');
+ }
+ };
+
+ const getPetName = (petId) => {
+ const pet = pets.find((p) => p._id === petId || p._id === petId?._id);
+ return pet?.name || '未知宠物';
+ };
+
+ if (loading) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+
+
📋 日常记录
+
+
+ } onClick={handleAdd}>
+ 添加记录
+
+
+
+
+ {records.length > 0 ? (
+
(
+ handleEdit(record)} />,
+ handleDelete(record._id)}
+ okText="确定"
+ cancelText="取消"
+ >
+
+
+ ]}
+ >
+
+
+
+
+ 🐾 {getPetName(record.petId)}
+
+
+
+ {dayjs(record.recordDate).format('YYYY-MM-DD')}
+
+
+
+
+
+
+
🍽️ 饮食记录
+ {record.meals?.length > 0 ? (
+
+ {record.meals.map((meal, idx) => (
+
+ {meal.type} {meal.food}
+ {meal.amount && ` (${meal.amount})`}
+
+ ))}
+
+ ) : (
+
暂无记录
+ )}
+
+
+
+
+
+
🚽 排便记录
+ {record.bowelMovements?.length > 0 ? (
+
+ {record.bowelMovements.map((bowel, idx) => (
+
+ {bowel.type} {bowel.quantity} - {bowel.consistency}
+
+ ))}
+
+ ) : (
+
暂无记录
+ )}
+
+
+
+
+
+
🏃 运动记录
+ {record.exercises?.length > 0 ? (
+
+ {record.exercises.map((exercise, idx) => (
+
+ {exercise.type} {exercise.duration} 分钟
+ {exercise.location && ` @ ${exercise.location}`}
+
+ ))}
+
+ ) : (
+
暂无记录
+ )}
+
+
+
+
+ {record.notes && (
+
+ 备注:
+ {record.notes}
+
+ )}
+
+
+ )}
+ />
+ ) : (
+
+ } onClick={handleAdd}>
+ 添加第一条记录
+
+
+ )}
+
+ setModalVisible(false)}
+ footer={null}
+ width={800}
+ >
+
+
+ {tempMeals.map((meal, idx) => (
+ setTempMeals(tempMeals.filter((_, i) => i !== idx))}
+ style={{ marginBottom: 8 }}
+ >
+ {meal.type}: {meal.food} {meal.amount && `(${meal.amount})`}
+
+ ))}
+
+
+ 🚽 排便记录
+
+
+ {tempBowels.map((bowel, idx) => (
+ setTempBowels(tempBowels.filter((_, i) => i !== idx))}
+ style={{ marginBottom: 8 }}
+ >
+ {bowel.type} - {bowel.quantity} - {bowel.consistency}
+
+ ))}
+
+
+ 🏃 运动记录
+
+
+ {tempExercises.map((exercise, idx) => (
+ setTempExercises(tempExercises.filter((_, i) => i !== idx))}
+ style={{ marginBottom: 8 }}
+ >
+ {exercise.type}: {exercise.duration} 分钟
+ {exercise.location && ` @ ${exercise.location}`}
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default DailyRecords;
\ No newline at end of file
diff --git a/client/src/pages/Dashboard.jsx b/client/src/pages/Dashboard.jsx
new file mode 100644
index 0000000..cc6ccb6
--- /dev/null
+++ b/client/src/pages/Dashboard.jsx
@@ -0,0 +1,208 @@
+import React, { useState, useEffect } from 'react';
+import { Card, Statistic, Row, Col, List, Tag, Empty, Spin, message } from 'antd';
+import {
+ TeamOutlined,
+ FileTextOutlined,
+ BellOutlined,
+ MedicineBoxOutlined,
+ CalendarOutlined,
+ ClockCircleOutlined
+} from '@ant-design/icons';
+import { petApi, reminderApi, dailyRecordApi, medicalRecordApi } from '../services/api';
+import dayjs from 'dayjs';
+
+const Dashboard = () => {
+ const [loading, setLoading] = useState(true);
+ const [stats, setStats] = useState({
+ petCount: 0,
+ reminderCount: 0,
+ recordCount: 0,
+ medicalCount: 0
+ });
+ const [upcomingReminders, setUpcomingReminders] = useState([]);
+ const [recentRecords, setRecentRecords] = useState([]);
+
+ useEffect(() => {
+ fetchDashboardData();
+ }, []);
+
+ const fetchDashboardData = async () => {
+ setLoading(true);
+ try {
+ const [petsRes, remindersRes, recordsRes, medicalRes] = await Promise.all([
+ petApi.getPets(),
+ reminderApi.getReminders({ upcoming: 'true' }),
+ dailyRecordApi.getRecords(),
+ medicalRecordApi.getRecords()
+ ]);
+
+ setStats({
+ petCount: petsRes.data?.data?.pets?.length || 0,
+ reminderCount: remindersRes.data?.data?.reminders?.length || 0,
+ recordCount: recordsRes.data?.data?.records?.length || 0,
+ medicalCount: medicalRes.data?.data?.records?.length || 0
+ });
+
+ setUpcomingReminders(remindersRes.data?.data?.reminders?.slice(0, 5) || []);
+ setRecentRecords(recordsRes.data?.data?.records?.slice(0, 5) || []);
+ } catch (error) {
+ message.error('获取数据失败');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const getTypeColor = (type) => {
+ const colors = {
+ '疫苗': 'blue',
+ '驱虫': 'green',
+ '体检': 'orange',
+ '其他': 'purple'
+ };
+ return colors[type] || 'default';
+ };
+
+ if (loading) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+ }
+ valueStyle={{ color: '#1890ff' }}
+ />
+
+
+
+
+ }
+ valueStyle={{ color: '#52c41a' }}
+ />
+
+
+
+
+ }
+ valueStyle={{ color: '#fa8c16' }}
+ />
+
+
+
+
+ }
+ valueStyle={{ color: '#722ed1' }}
+ />
+
+
+
+
+
+
+ 待办事项}>
+ {upcomingReminders.length > 0 ? (
+ (
+
+ {item.type}
+
+ ]}
+ >
+
+ {item.type === '疫苗' ? '💉' : item.type === '驱虫' ? '🐛' : item.type === '体检' ? '🏥' : '📋'}
+
+ }
+ title={item.title}
+ description={
+
+
+ {dayjs(item.reminderDate).format('YYYY-MM-DD')}
+
+ {item.reminderTime}
+ {item.petId && (
+
+ 🐾 {item.petId?.name}
+
+ )}
+
+ }
+ />
+
+ )}
+ />
+ ) : (
+
+ )}
+
+
+
+
+ 记录}>
+ {recentRecords.length > 0 ? (
+ (
+
+
+ {item.petId?.name || '未命名宠物'}
+
+ {dayjs(item.recordDate).format('YYYY-MM-DD')}
+
+
+ }
+ description={
+
+ {item.meals?.length > 0 && (
+ 🍽️ 饮食: {item.meals.length} 次
+ )}
+ {item.bowelMovements?.length > 0 && (
+ 🚽 排便: {item.bowelMovements.length} 次
+ )}
+ {item.exercises?.length > 0 && (
+ 🏃 运动: {item.exercises.length} 次
+ )}
+
+ }
+ />
+
+ )}
+ />
+ ) : (
+
+ )}
+
+
+
+
+ );
+};
+
+export default Dashboard;
\ No newline at end of file
diff --git a/client/src/pages/Login.jsx b/client/src/pages/Login.jsx
new file mode 100644
index 0000000..e93876e
--- /dev/null
+++ b/client/src/pages/Login.jsx
@@ -0,0 +1,81 @@
+import React, { useState } from 'react';
+import { Form, Input, Button, message, Card } from 'antd';
+import { UserOutlined, LockOutlined, EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons';
+import { Link, useNavigate } from 'react-router-dom';
+import { authApi } from '../services/api';
+import useAuthStore from '../store/authStore';
+
+const Login = () => {
+ const [loading, setLoading] = useState(false);
+ const [showPassword, setShowPassword] = useState(false);
+ const navigate = useNavigate();
+ const { login } = useAuthStore();
+
+ const onFinish = async (values) => {
+ setLoading(true);
+ try {
+ const response = await authApi.login(values);
+ if (response.data.success) {
+ const { token, user } = response.data.data;
+ login(user, token);
+ message.success('登录成功!');
+ navigate('/dashboard');
+ }
+ } catch (error) {
+ message.error(error.response?.data?.message || '登录失败,请检查邮箱和密码');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ return (
+
+
+
+ 🐾 宠物日常管理平台
+
+ 登录
+
+
+ } placeholder="邮箱地址" />
+
+
+
+ }
+ placeholder="密码"
+ iconRender={(visible) => (visible ? : )}
+ />
+
+
+
+
+
+
+
+ 还没有账号? 立即注册
+
+
+
+
+ );
+};
+
+export default Login;
\ No newline at end of file
diff --git a/client/src/pages/MedicalRecords.jsx b/client/src/pages/MedicalRecords.jsx
new file mode 100644
index 0000000..a500621
--- /dev/null
+++ b/client/src/pages/MedicalRecords.jsx
@@ -0,0 +1,488 @@
+import React, { useState, useEffect } from 'react';
+import {
+ Card,
+ List,
+ Button,
+ Modal,
+ Form,
+ Select,
+ DatePicker,
+ Input,
+ InputNumber,
+ Tag,
+ message,
+ Empty,
+ Spin,
+ Divider,
+ Popconfirm,
+ Timeline,
+ Collapse
+} from 'antd';
+import { PlusOutlined, DeleteOutlined, EditOutlined, MedicineBoxOutlined } from '@ant-design/icons';
+import { medicalRecordApi, petApi } from '../services/api';
+import dayjs from 'dayjs';
+
+const { Option } = Select;
+const { TextArea } = Input;
+const { Panel } = Collapse;
+
+const MedicalRecords = () => {
+ const [loading, setLoading] = useState(false);
+ const [records, setRecords] = useState([]);
+ const [pets, setPets] = useState([]);
+ const [modalVisible, setModalVisible] = useState(false);
+ const [editingRecord, setEditingRecord] = useState(null);
+ const [selectedPet, setSelectedPet] = useState(null);
+ const [form] = Form.useForm();
+ const [medicationsForm] = Form.useForm();
+ const [labResultsForm] = Form.useForm();
+
+ const [tempMedications, setTempMedications] = useState([]);
+ const [tempLabResults, setTempLabResults] = useState([]);
+
+ useEffect(() => {
+ fetchData();
+ }, [selectedPet]);
+
+ const fetchData = async () => {
+ setLoading(true);
+ try {
+ const [petsRes, recordsRes] = await Promise.all([
+ petApi.getPets(),
+ medicalRecordApi.getRecords(selectedPet ? { petId: selectedPet } : {})
+ ]);
+ setPets(petsRes.data?.data?.pets || []);
+ setRecords(recordsRes.data?.data?.records || []);
+ } catch (error) {
+ message.error('获取数据失败');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const handleAdd = () => {
+ setEditingRecord(null);
+ form.resetFields();
+ setTempMedications([]);
+ setTempLabResults([]);
+ setModalVisible(true);
+ };
+
+ const handleEdit = (record) => {
+ setEditingRecord(record);
+ form.setFieldsValue({
+ ...record,
+ petId: record.petId?._id || record.petId,
+ visitDate: dayjs(record.visitDate),
+ nextVisitDate: record.nextVisitDate ? dayjs(record.nextVisitDate) : null
+ });
+ setTempMedications(record.medications || []);
+ setTempLabResults(record.labResults || []);
+ setModalVisible(true);
+ };
+
+ const handleDelete = async (id) => {
+ try {
+ await medicalRecordApi.deleteRecord(id);
+ message.success('记录删除成功');
+ fetchData();
+ } catch (error) {
+ message.error('删除失败');
+ }
+ };
+
+ const handleAddMedication = (values) => {
+ setTempMedications([...tempMedications, values]);
+ medicationsForm.resetFields();
+ };
+
+ const handleAddLabResult = (values) => {
+ setTempLabResults([...tempLabResults, values]);
+ labResultsForm.resetFields();
+ };
+
+ const handleSubmit = async (values) => {
+ const data = {
+ ...values,
+ visitDate: values.visitDate.toDate(),
+ nextVisitDate: values.nextVisitDate ? values.nextVisitDate.toDate() : null,
+ medications: tempMedications,
+ labResults: tempLabResults
+ };
+
+ try {
+ if (editingRecord) {
+ await medicalRecordApi.updateRecord(editingRecord._id, data);
+ message.success('记录更新成功');
+ } else {
+ await medicalRecordApi.createRecord(data);
+ message.success('记录添加成功');
+ }
+ setModalVisible(false);
+ fetchData();
+ } catch (error) {
+ message.error(editingRecord ? '更新失败' : '添加失败');
+ }
+ };
+
+ if (loading) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+
+
🏥 就医记录
+
+
+ } onClick={handleAdd}>
+ 添加记录
+
+
+
+
+ {records.length > 0 ? (
+
+ {records.map((record, idx) => (
+
+
+
+ {record.petId?.name || '未知宠物'}
+
+ {record.visitType}
+
+
+
+ {dayjs(record.visitDate).format('YYYY-MM-DD')}
+ {record.hospital && 🏥 {record.hospital}}
+
+
+ }
+ extra={
+ e.stopPropagation()}>
+
handleEdit(record)} />
+ handleDelete(record._id)}
+ okText="确定"
+ cancelText="取消"
+ >
+
+
+
+ }
+ >
+
+ {record.symptoms && (
+
+
+
症状描述
+
+ {record.symptoms}
+
+
+
+ )}
+ {record.diagnosis && (
+
+
+
诊断结果
+
+ {record.diagnosis}
+
+
+
+ )}
+ {record.treatment && (
+
+
+
治疗方案
+
+ {record.treatment}
+
+
+
+ )}
+ {record.medications?.length > 0 && (
+
+
+
用药记录
+
+ {record.medications.map((med, mIdx) => (
+
+ 💊 {med.name}
+ {med.dosage && ` - 剂量: ${med.dosage}`}
+ {med.frequency && ` - 频率: ${med.frequency}`}
+ {med.duration && ` - 持续: ${med.duration}`}
+
+ ))}
+
+
+
+ )}
+ {record.labResults?.length > 0 && (
+
+
+
检查结果
+
+ {record.labResults.map((lab, lIdx) => (
+
+
🔬 {lab.name}
+ {lab.result && ` - 结果: ${lab.result}`}
+ {lab.referenceRange && ` (参考范围: ${lab.referenceRange})`}
+ {lab.notes &&
{lab.notes}
}
+
+ ))}
+
+
+
+ )}
+
+
+
+
+
+ {record.doctor && (
+ 👨⚕️ 医生: {record.doctor}
+ )}
+ {record.cost > 0 && (
+ 💰 费用: ¥{record.cost}
+ )}
+ {record.nextVisitDate && (
+ 📅 下次复诊: {dayjs(record.nextVisitDate).format('YYYY-MM-DD')}
+ )}
+
+
+ {record.notes && (
+
+ 备注:
+ {record.notes}
+
+ )}
+
+ ))}
+
+ ) : (
+
+ } onClick={handleAdd}>
+ 添加第一条记录
+
+
+ )}
+
+ setModalVisible(false)}
+ footer={null}
+ width={700}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 💊 用药记录
+
+
+ {tempMedications.map((med, idx) => (
+ setTempMedications(tempMedications.filter((_, i) => i !== idx))}
+ style={{ marginBottom: 8 }}
+ >
+ {med.name} {med.dosage && `(${med.dosage})`}
+
+ ))}
+
+
+ 🔬 检查结果
+
+
+ {tempLabResults.map((lab, idx) => (
+ setTempLabResults(tempLabResults.filter((_, i) => i !== idx))}
+ style={{ marginBottom: 8 }}
+ >
+ {lab.name}: {lab.result}
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default MedicalRecords;
\ No newline at end of file
diff --git a/client/src/pages/Moments.jsx b/client/src/pages/Moments.jsx
new file mode 100644
index 0000000..8542b1e
--- /dev/null
+++ b/client/src/pages/Moments.jsx
@@ -0,0 +1,450 @@
+import React, { useState, useEffect } from 'react';
+import {
+ Card,
+ List,
+ Button,
+ Modal,
+ Form,
+ Select,
+ Input,
+ Tag,
+ message,
+ Empty,
+ Spin,
+ Avatar,
+ InputNumber,
+ Divider,
+ Image,
+ Tabs
+} from 'antd';
+import {
+ PlusOutlined,
+ HeartOutlined,
+ HeartFilled,
+ MessageOutlined,
+ DeleteOutlined,
+ UserOutlined,
+ ShareAltOutlined
+} from '@ant-design/icons';
+import { postApi, petApi } from '../services/api';
+import dayjs from 'dayjs';
+import relativeTime from 'dayjs/plugin/relativeTime';
+import 'dayjs/locale/zh-cn';
+
+dayjs.extend(relativeTime);
+dayjs.locale('zh-cn');
+
+const { Option } = Select;
+const { TextArea } = Input;
+
+const Moments = () => {
+ const [loading, setLoading] = useState(false);
+ const [posts, setPosts] = useState([]);
+ const [pets, setPets] = useState([]);
+ const [modalVisible, setModalVisible] = useState(false);
+ const [activeTab, setActiveTab] = useState('all');
+ const [form] = Form.useForm();
+ const [commentInputs, setCommentInputs] = useState({});
+ const [tempImages, setTempImages] = useState([]);
+ const [tempTags, setTempTags] = useState([]);
+ const [tagInput, setTagInput] = useState('');
+
+ useEffect(() => {
+ fetchData();
+ }, [activeTab]);
+
+ const fetchData = async () => {
+ setLoading(true);
+ try {
+ const [petsRes, postsRes] = await Promise.all([
+ petApi.getPets(),
+ activeTab === 'my' ? postApi.getMyPosts() : postApi.getPosts()
+ ]);
+ setPets(petsRes.data?.data?.pets || []);
+ setPosts(postsRes.data?.data?.posts || []);
+ } catch (error) {
+ message.error('获取数据失败');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const handleAdd = () => {
+ form.resetFields();
+ setTempImages([]);
+ setTempTags([]);
+ setModalVisible(true);
+ };
+
+ const handleSubmit = async (values) => {
+ const data = {
+ ...values,
+ images: tempImages,
+ tags: tempTags
+ };
+
+ try {
+ await postApi.createPost(data);
+ message.success('帖子发布成功');
+ setModalVisible(false);
+ fetchData();
+ } catch (error) {
+ message.error('发布失败');
+ }
+ };
+
+ const handleLike = async (postId) => {
+ try {
+ const response = await postApi.likePost(postId);
+ if (response.data.success) {
+ setPosts(posts.map(post => {
+ if (post._id === postId) {
+ const { isLiked, likeCount } = response.data.data;
+ return {
+ ...post,
+ likes: isLiked ? [...new Set([...post.likes, 'current-user'])] : post.likes.filter(id => id !== 'current-user'),
+ _likeCount: likeCount
+ };
+ }
+ return post;
+ }));
+ }
+ } catch (error) {
+ message.error('操作失败');
+ }
+ };
+
+ const handleComment = async (postId) => {
+ const content = commentInputs[postId];
+ if (!content?.trim()) {
+ message.warning('请输入评论内容');
+ return;
+ }
+
+ try {
+ const response = await postApi.commentPost(postId, { content });
+ if (response.data.success) {
+ setPosts(posts.map(post => {
+ if (post._id === postId) {
+ return {
+ ...post,
+ comments: response.data.data.comments
+ };
+ }
+ return post;
+ }));
+ setCommentInputs({ ...commentInputs, [postId]: '' });
+ message.success('评论成功');
+ }
+ } catch (error) {
+ message.error('评论失败');
+ }
+ };
+
+ const handleDelete = async (postId) => {
+ try {
+ await postApi.deletePost(postId);
+ message.success('帖子删除成功');
+ fetchData();
+ } catch (error) {
+ message.error('删除失败');
+ }
+ };
+
+ const handleAddImage = () => {
+ const imageUrl = prompt('请输入图片URL:');
+ if (imageUrl?.trim()) {
+ setTempImages([...tempImages, imageUrl.trim()]);
+ }
+ };
+
+ const handleAddTag = (e) => {
+ if (e.key === 'Enter' && tagInput.trim()) {
+ e.preventDefault();
+ if (!tempTags.includes(tagInput.trim())) {
+ setTempTags([...tempTags, tagInput.trim()]);
+ }
+ setTagInput('');
+ }
+ };
+
+ const tabs = [
+ {
+ key: 'all',
+ label: (
+
+ 全部动态
+
+ )
+ },
+ {
+ key: 'my',
+ label: (
+
+ 我的动态
+
+ )
+ }
+ ];
+
+ if (loading) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+
+
🌐 宠物朋友圈
+ } onClick={handleAdd}>
+ 发布动态
+
+
+
+
+
+
+ {posts.length > 0 ? (
+ {
+ const isLiked = post.likes?.length > 0;
+ const likeCount = post._likeCount || post.likes?.length || 0;
+ const commentCount = post.comments?.length || 0;
+
+ return (
+
+
+
+
}
+ src={post.userId?.avatar}
+ className="post-user-avatar"
+ />
+
+
+ {post.userId?.nickname || post.userId?.username}
+
+
+ {dayjs(post.createdAt).fromNow()}
+ {post.petId && (
+
+ 🐾 {post.petId.name}
+
+ )}
+
+
+
+
+ {post.content}
+
+ {post.images?.length > 0 && (
+
+ {post.images.slice(0, 9).map((img, idx) => (
+
+ ))}
+
+ )}
+
+ {post.tags?.length > 0 && (
+
+ {post.tags.map((tag, idx) => (
+ #{tag}
+ ))}
+
+ )}
+
+
+
handleLike(post._id)}
+ >
+ {isLiked ? : }
+ {likeCount}
+
+
+
+ {commentCount}
+
+
+
+ {post.comments?.length > 0 && (
+
+ {post.comments.map((comment, idx) => (
+
+
}
+ src={comment.userId?.avatar}
+ className="comment-avatar"
+ />
+
+
+ {comment.userId?.nickname || comment.userId?.username}
+
+ {comment.content}
+
+
+ ))}
+
+ )}
+
+
+ setCommentInputs({ ...commentInputs, [post._id]: e.target.value })}
+ onPressEnter={() => handleComment(post._id)}
+ />
+
+
+
+ {activeTab === 'my' && (
+
+ }
+ onClick={() => handleDelete(post._id)}
+ >
+ 删除
+
+
+ )}
+
+
+ );
+ }}
+ />
+ ) : (
+
+ } onClick={handleAdd}>
+ 发布第一条动态
+
+
+ )}
+
+
+
setModalVisible(false)}
+ footer={null}
+ width={600}
+ >
+
+
+
+
+
+
+
+
+
+
+ {tempImages.map((img, idx) => (
+
+

+
setTempImages(tempImages.filter((_, i) => i !== idx))}
+ >
+ ×
+
+
+ ))}
+
+
+
+ 点击按钮添加图片URL(最多9张)
+
+
+
+
+ setTagInput(e.target.value)}
+ onKeyDown={handleAddTag}
+ style={{ marginBottom: 8 }}
+ />
+
+ {tempTags.map((tag, idx) => (
+
+ #{tag}
+ setTempTags(tempTags.filter((_, i) => i !== idx))}
+ >
+ ×
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Moments;
\ No newline at end of file
diff --git a/client/src/pages/Pets.jsx b/client/src/pages/Pets.jsx
new file mode 100644
index 0000000..fbb494c
--- /dev/null
+++ b/client/src/pages/Pets.jsx
@@ -0,0 +1,285 @@
+import React, { useState, useEffect } from 'react';
+import {
+ Card,
+ List,
+ Button,
+ Modal,
+ Form,
+ Input,
+ Select,
+ DatePicker,
+ InputNumber,
+ Switch,
+ message,
+ Popconfirm,
+ Empty,
+ Spin,
+ Avatar
+} from 'antd';
+import {
+ PlusOutlined,
+ EditOutlined,
+ DeleteOutlined,
+ TeamOutlined,
+ UserOutlined
+} from '@ant-design/icons';
+import { petApi } from '../services/api';
+import dayjs from 'dayjs';
+
+const { Option } = Select;
+const { TextArea } = Input;
+
+const Pets = () => {
+ const [loading, setLoading] = useState(false);
+ const [pets, setPets] = useState([]);
+ const [modalVisible, setModalVisible] = useState(false);
+ const [editingPet, setEditingPet] = useState(null);
+ const [form] = Form.useForm();
+
+ useEffect(() => {
+ fetchPets();
+ }, []);
+
+ const fetchPets = async () => {
+ setLoading(true);
+ try {
+ const response = await petApi.getPets();
+ setPets(response.data?.data?.pets || []);
+ } catch (error) {
+ message.error('获取宠物列表失败');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const handleAdd = () => {
+ setEditingPet(null);
+ form.resetFields();
+ setModalVisible(true);
+ };
+
+ const handleEdit = (pet) => {
+ setEditingPet(pet);
+ form.setFieldsValue({
+ ...pet,
+ birthDate: pet.birthDate ? dayjs(pet.birthDate) : null
+ });
+ setModalVisible(true);
+ };
+
+ const handleDelete = async (id) => {
+ try {
+ await petApi.deletePet(id);
+ message.success('宠物删除成功');
+ fetchPets();
+ } catch (error) {
+ message.error('删除失败');
+ }
+ };
+
+ const handleSubmit = async (values) => {
+ try {
+ if (editingPet) {
+ await petApi.updatePet(editingPet._id, values);
+ message.success('宠物信息更新成功');
+ } else {
+ await petApi.createPet(values);
+ message.success('宠物添加成功');
+ }
+ setModalVisible(false);
+ fetchPets();
+ } catch (error) {
+ message.error(editingPet ? '更新失败' : '添加失败');
+ }
+ };
+
+ if (loading) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+
+
🐾 我的宠物
+ } onClick={handleAdd}>
+ 添加宠物
+
+
+
+ {pets.length > 0 ? (
+
(
+
+ handleEdit(pet)} />,
+ handleDelete(pet._id)}
+ okText="确定"
+ cancelText="取消"
+ >
+
+
+ ]}
+ >
+
+ ) : (
+ }
+ style={{ backgroundColor: '#1890ff' }}
+ />
+ )
+ }
+ title={
+
+ {pet.name}
+
+ {pet.gender === '公' ? '♂' : pet.gender === '母' ? '♀' : ''}
+
+
+ }
+ description={
+
+
+ 种类:
+ {pet.species}
+ {pet.breed && ` (${pet.breed})`}
+
+ {pet.birthDate && (
+
+ 生日:
+ {dayjs(pet.birthDate).format('YYYY-MM-DD')}
+
+ )}
+ {pet.weight && (
+
+ 体重:
+ {pet.weight} kg
+
+ )}
+
+ 绝育:
+ {pet.isNeutered ? '已绝育' : '未绝育'}
+
+ {pet.description && (
+
+ {pet.description}
+
+ )}
+
+ }
+ />
+
+
+ )}
+ />
+ ) : (
+
+ } onClick={handleAdd}>
+ 添加第一个宠物
+
+
+ )}
+
+ setModalVisible(false)}
+ footer={null}
+ width={600}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Pets;
\ No newline at end of file
diff --git a/client/src/pages/Profile.jsx b/client/src/pages/Profile.jsx
new file mode 100644
index 0000000..a0b63c3
--- /dev/null
+++ b/client/src/pages/Profile.jsx
@@ -0,0 +1,220 @@
+import React, { useState, useEffect } from 'react';
+import {
+ Card,
+ Form,
+ Input,
+ Button,
+ Avatar,
+ message,
+ Spin,
+ Divider,
+ Statistic,
+ Row,
+ Col,
+ Tag
+} from 'antd';
+import { UserOutlined, TeamOutlined, FileTextOutlined, ShareAltOutlined } from '@ant-design/icons';
+import { authApi, petApi, dailyRecordApi, postApi } from '../services/api';
+import useAuthStore from '../store/authStore';
+
+const { TextArea } = Input;
+
+const Profile = () => {
+ const [loading, setLoading] = useState(false);
+ const [stats, setStats] = useState({
+ petCount: 0,
+ recordCount: 0,
+ postCount: 0
+ });
+ const { user, updateUser } = useAuthStore();
+ const [form] = Form.useForm();
+
+ useEffect(() => {
+ fetchData();
+ if (user) {
+ form.setFieldsValue({
+ nickname: user.nickname || '',
+ avatar: user.avatar || '',
+ bio: user.bio || ''
+ });
+ }
+ }, [user]);
+
+ const fetchData = async () => {
+ setLoading(true);
+ try {
+ const [petsRes, recordsRes, postsRes] = await Promise.all([
+ petApi.getPets(),
+ dailyRecordApi.getRecords(),
+ postApi.getMyPosts()
+ ]);
+
+ setStats({
+ petCount: petsRes.data?.data?.pets?.length || 0,
+ recordCount: recordsRes.data?.data?.records?.length || 0,
+ postCount: postsRes.data?.data?.posts?.length || 0
+ });
+ } catch (error) {
+ message.error('获取数据失败');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const handleSubmit = async (values) => {
+ setLoading(true);
+ try {
+ const response = await authApi.updateProfile(values);
+ if (response.data.success) {
+ const updatedUser = response.data.data.user;
+ updateUser(updatedUser);
+ message.success('个人信息更新成功');
+ }
+ } catch (error) {
+ message.error('更新失败');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ if (loading && !stats.petCount) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+
+
👤 个人中心
+
+
+
+
+
+
+
}
+ src={user?.avatar}
+ style={{ marginBottom: 16 }}
+ />
+
+ {user?.nickname || user?.username}
+
+
@{user?.username}
+ {user?.email && (
+
+ 📧 {user?.email}
+
+ )}
+
+
+ 数据统计
+
+
+
+ }
+ valueStyle={{ fontSize: 24, color: '#1890ff' }}
+ />
+
+
+ }
+ valueStyle={{ fontSize: 24, color: '#52c41a' }}
+ />
+
+
+ }
+ valueStyle={{ fontSize: 24, color: '#fa8c16' }}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
用户名
+
{user?.username}
+
+
+
+
+
+
+
+
+
+
+
💡 提示:
+
+ - 用户名和邮箱地址无法修改
+ - 如果需要修改密码,请联系管理员
+ - 您的所有数据仅对您自己可见
+
+
+
+
+
+
+ );
+};
+
+export default Profile;
\ No newline at end of file
diff --git a/client/src/pages/Register.jsx b/client/src/pages/Register.jsx
new file mode 100644
index 0000000..6dd8954
--- /dev/null
+++ b/client/src/pages/Register.jsx
@@ -0,0 +1,113 @@
+import React, { useState } from 'react';
+import { Form, Input, Button, message, Card } from 'antd';
+import { UserOutlined, LockOutlined, MailOutlined } from '@ant-design/icons';
+import { Link, useNavigate } from 'react-router-dom';
+import { authApi } from '../services/api';
+import useAuthStore from '../store/authStore';
+
+const Register = () => {
+ const [loading, setLoading] = useState(false);
+ const navigate = useNavigate();
+ const { login } = useAuthStore();
+
+ const onFinish = async (values) => {
+ setLoading(true);
+ try {
+ const response = await authApi.register(values);
+ if (response.data.success) {
+ const { token, user } = response.data.data;
+ login(user, token);
+ message.success('注册成功!');
+ navigate('/dashboard');
+ }
+ } catch (error) {
+ const errors = error.response?.data?.errors;
+ if (errors && errors.length > 0) {
+ message.error(errors[0].msg);
+ } else {
+ message.error(error.response?.data?.message || '注册失败,请稍后重试');
+ }
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ return (
+
+ );
+};
+
+export default Register;
\ No newline at end of file
diff --git a/client/src/pages/Reminders.jsx b/client/src/pages/Reminders.jsx
new file mode 100644
index 0000000..1093bda
--- /dev/null
+++ b/client/src/pages/Reminders.jsx
@@ -0,0 +1,391 @@
+import React, { useState, useEffect } from 'react';
+import {
+ Card,
+ List,
+ Button,
+ Modal,
+ Form,
+ Select,
+ DatePicker,
+ TimePicker,
+ Input,
+ Switch,
+ Tag,
+ message,
+ Empty,
+ Spin,
+ Divider,
+ Popconfirm,
+ Tabs
+} from 'antd';
+import {
+ PlusOutlined,
+ DeleteOutlined,
+ EditOutlined,
+ CheckOutlined,
+ MedicineBoxOutlined,
+ BellOutlined
+} from '@ant-design/icons';
+import { reminderApi, petApi } from '../services/api';
+import dayjs from 'dayjs';
+
+const { Option } = Select;
+const { TextArea } = Input;
+
+const Reminders = () => {
+ const [loading, setLoading] = useState(false);
+ const [reminders, setReminders] = useState([]);
+ const [pets, setPets] = useState([]);
+ const [modalVisible, setModalVisible] = useState(false);
+ const [editingReminder, setEditingReminder] = useState(null);
+ const [activeTab, setActiveTab] = useState('upcoming');
+ const [form] = Form.useForm();
+
+ useEffect(() => {
+ fetchData();
+ }, [activeTab]);
+
+ const fetchData = async () => {
+ setLoading(true);
+ try {
+ const [petsRes, remindersRes] = await Promise.all([
+ petApi.getPets(),
+ reminderApi.getReminders(activeTab === 'upcoming' ? { upcoming: 'true' } : {})
+ ]);
+ setPets(petsRes.data?.data?.pets || []);
+ setReminders(remindersRes.data?.data?.reminders || []);
+ } catch (error) {
+ message.error('获取数据失败');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const handleAdd = () => {
+ setEditingReminder(null);
+ form.resetFields();
+ setModalVisible(true);
+ };
+
+ const handleEdit = (reminder) => {
+ setEditingReminder(reminder);
+ form.setFieldsValue({
+ ...reminder,
+ petId: reminder.petId?._id || reminder.petId,
+ reminderDate: dayjs(reminder.reminderDate),
+ reminderTime: reminder.reminderTime ? dayjs(reminder.reminderTime, 'HH:mm') : null
+ });
+ setModalVisible(true);
+ };
+
+ const handleDelete = async (id) => {
+ try {
+ await reminderApi.deleteReminder(id);
+ message.success('提醒删除成功');
+ fetchData();
+ } catch (error) {
+ message.error('删除失败');
+ }
+ };
+
+ const handleComplete = async (id) => {
+ try {
+ await reminderApi.updateReminder(id, { isCompleted: true });
+ message.success('已标记为完成');
+ fetchData();
+ } catch (error) {
+ message.error('操作失败');
+ }
+ };
+
+ const handleSubmit = async (values) => {
+ const data = {
+ ...values,
+ reminderDate: values.reminderDate.toDate(),
+ reminderTime: values.reminderTime ? values.reminderTime.format('HH:mm') : '09:00'
+ };
+
+ try {
+ if (editingReminder) {
+ await reminderApi.updateReminder(editingReminder._id, data);
+ message.success('提醒更新成功');
+ } else {
+ await reminderApi.createReminder(data);
+ message.success('提醒创建成功');
+ }
+ setModalVisible(false);
+ fetchData();
+ } catch (error) {
+ message.error(editingReminder ? '更新失败' : '创建失败');
+ }
+ };
+
+ const getTypeIcon = (type) => {
+ switch (type) {
+ case '疫苗':
+ return '💉';
+ case '驱虫':
+ return '🐛';
+ case '体检':
+ return '🏥';
+ default:
+ return '📋';
+ }
+ };
+
+ const isUrgent = (date) => {
+ const diff = dayjs(date).diff(dayjs(), 'day');
+ return diff >= 0 && diff <= 3;
+ };
+
+ const tabs = [
+ {
+ key: 'upcoming',
+ label: (
+
+ 待办提醒
+
+ )
+ },
+ {
+ key: 'all',
+ label: (
+
+ 全部提醒
+
+ )
+ }
+ ];
+
+ if (loading) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+
+
⏰ 提醒管理
+ } onClick={handleAdd}>
+ 添加提醒
+
+
+
+
+
+
+ {reminders.length > 0 ? (
+ {
+ const urgent = isUrgent(reminder.reminderDate);
+ return (
+ }
+ onClick={() => handleComplete(reminder._id)}
+ >
+ 完成
+
+ ),
+ handleEdit(reminder)} />,
+ handleDelete(reminder._id)}
+ okText="确定"
+ cancelText="取消"
+ >
+
+
+ ]}
+ style={{
+ background: reminder.isCompleted ? '#f5f5f5' : 'white',
+ opacity: reminder.isCompleted ? 0.6 : 1,
+ textDecoration: reminder.isCompleted ? 'line-through' : 'none'
+ }}
+ >
+
+ {getTypeIcon(reminder.type)}
+
+ }
+ title={
+
+
+ {reminder.type}
+
+
+ {reminder.title}
+
+ {reminder.isCompleted && (
+
+ 已完成
+
+ )}
+
+ }
+ description={
+
+
+ 宠物:
+ {reminder.petId?.name || '未指定'}
+
+
+ 日期:
+ {dayjs(reminder.reminderDate).format('YYYY-MM-DD')}
+
+ 时间:
+ {reminder.reminderTime}
+
+
+ {reminder.isRepeating && (
+
+ 重复:
+ 每 {reminder.repeatFrequency} {reminder.repeatInterval}
+
+ )}
+ {reminder.description && (
+
+ {reminder.description}
+
+ )}
+
+ }
+ />
+
+ );
+ }}
+ />
+ ) : (
+
+ } onClick={handleAdd}>
+ 添加第一个提醒
+
+
+ )}
+
+
+ setModalVisible(false)}
+ footer={null}
+ width={600}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 提醒时间
+
+
+
+
+
+
+
+
+
+ 重复设置
+
+
+
+
+
+ prevValues.isRepeating !== currentValues.isRepeating}
+ >
+ {({ getFieldValue }) =>
+ getFieldValue('isRepeating') ? (
+
+
+
+ 次
+
+
+
+
+
+ ) : null
+ }
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Reminders;
\ No newline at end of file
diff --git a/client/src/services/api.js b/client/src/services/api.js
new file mode 100644
index 0000000..d27edce
--- /dev/null
+++ b/client/src/services/api.js
@@ -0,0 +1,88 @@
+import axios from 'axios';
+import useAuthStore from '../store/authStore';
+
+const api = axios.create({
+ baseURL: '/api',
+ timeout: 10000,
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+});
+
+api.interceptors.request.use(
+ (config) => {
+ const { token } = useAuthStore.getState();
+ if (token) {
+ config.headers.Authorization = `Bearer ${token}`;
+ }
+ return config;
+ },
+ (error) => {
+ return Promise.reject(error);
+ }
+);
+
+api.interceptors.response.use(
+ (response) => {
+ return response;
+ },
+ (error) => {
+ if (error.response && error.response.status === 401) {
+ useAuthStore.getState().logout();
+ if (window.location.pathname !== '/login' && window.location.pathname !== '/register') {
+ window.location.href = '/login';
+ }
+ }
+ return Promise.reject(error);
+ }
+);
+
+export const authApi = {
+ register: (data) => api.post('/auth/register', data),
+ login: (data) => api.post('/auth/login', data),
+ getProfile: () => api.get('/auth/profile'),
+ updateProfile: (data) => api.put('/auth/profile', data)
+};
+
+export const petApi = {
+ getPets: () => api.get('/pets'),
+ getPet: (id) => api.get(`/pets/${id}`),
+ createPet: (data) => api.post('/pets', data),
+ updatePet: (id, data) => api.put(`/pets/${id}`, data),
+ deletePet: (id) => api.delete(`/pets/${id}`)
+};
+
+export const dailyRecordApi = {
+ getRecords: (params) => api.get('/daily-records', { params }),
+ createRecord: (data) => api.post('/daily-records', data),
+ updateRecord: (id, data) => api.put(`/daily-records/${id}`, data),
+ deleteRecord: (id) => api.delete(`/daily-records/${id}`)
+};
+
+export const reminderApi = {
+ getReminders: (params) => api.get('/reminders', { params }),
+ getReminder: (id) => api.get(`/reminders/${id}`),
+ createReminder: (data) => api.post('/reminders', data),
+ updateReminder: (id, data) => api.put(`/reminders/${id}`, data),
+ deleteReminder: (id) => api.delete(`/reminders/${id}`)
+};
+
+export const medicalRecordApi = {
+ getRecords: (params) => api.get('/medical-records', { params }),
+ getRecord: (id) => api.get(`/medical-records/${id}`),
+ createRecord: (data) => api.post('/medical-records', data),
+ updateRecord: (id, data) => api.put(`/medical-records/${id}`, data),
+ deleteRecord: (id) => api.delete(`/medical-records/${id}`)
+};
+
+export const postApi = {
+ getPosts: (params) => api.get('/posts', { params }),
+ getMyPosts: (params) => api.get('/posts/my', { params }),
+ getPost: (id) => api.get(`/posts/${id}`),
+ createPost: (data) => api.post('/posts', data),
+ likePost: (id) => api.post(`/posts/${id}/like`),
+ commentPost: (id, data) => api.post(`/posts/${id}/comment`, data),
+ deletePost: (id) => api.delete(`/posts/${id}`)
+};
+
+export default api;
\ No newline at end of file
diff --git a/client/src/store/authStore.js b/client/src/store/authStore.js
new file mode 100644
index 0000000..5e15e54
--- /dev/null
+++ b/client/src/store/authStore.js
@@ -0,0 +1,40 @@
+import { create } from 'zustand';
+import { persist } from 'zustand/middleware';
+
+const useAuthStore = create(
+ persist(
+ (set) => ({
+ user: null,
+ token: null,
+ isAuthenticated: false,
+
+ login: (userData, token) => {
+ set({
+ user: userData,
+ token,
+ isAuthenticated: true
+ });
+ },
+
+ logout: () => {
+ set({
+ user: null,
+ token: null,
+ isAuthenticated: false
+ });
+ },
+
+ updateUser: (userData) => {
+ set((state) => ({
+ ...state,
+ user: { ...state.user, ...userData }
+ }));
+ }
+ }),
+ {
+ name: 'auth-storage'
+ }
+ )
+);
+
+export default useAuthStore;
\ No newline at end of file
diff --git a/client/vite.config.js b/client/vite.config.js
new file mode 100644
index 0000000..3087740
--- /dev/null
+++ b/client/vite.config.js
@@ -0,0 +1,15 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+
+export default defineConfig({
+ plugins: [react()],
+ server: {
+ port: 3000,
+ proxy: {
+ '/api': {
+ target: 'http://localhost:5000',
+ changeOrigin: true
+ }
+ }
+ }
+});
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..b3f3558
--- /dev/null
+++ b/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "pet-management-platform",
+ "version": "1.0.0",
+ "description": "宠物日常管理平台 - 前后端全栈应用",
+ "private": true,
+ "workspaces": [
+ "server",
+ "client"
+ ],
+ "scripts": {
+ "install:all": "npm install && cd server && npm install && cd ../client && npm install",
+ "dev:server": "cd server && npm run dev",
+ "dev:client": "cd client && npm run dev",
+ "start:server": "cd server && npm start",
+ "build:client": "cd client && npm run build",
+ "dev": "concurrently \"npm run dev:server\" \"npm run dev:client\""
+ },
+ "keywords": [
+ "pet",
+ "management",
+ "react",
+ "express",
+ "mongodb"
+ ],
+ "author": "",
+ "license": "MIT",
+ "devDependencies": {
+ "concurrently": "^8.2.0"
+ }
+}
\ No newline at end of file
diff --git a/server/.env b/server/.env
new file mode 100644
index 0000000..00bcc42
--- /dev/null
+++ b/server/.env
@@ -0,0 +1,4 @@
+PORT=5000
+MONGODB_URI=mongodb://localhost:27017/pet-management
+JWT_SECRET=pet_management_jwt_secret_key_2024
+NODE_ENV=development
\ No newline at end of file
diff --git a/server/.env.example b/server/.env.example
new file mode 100644
index 0000000..8887700
--- /dev/null
+++ b/server/.env.example
@@ -0,0 +1,4 @@
+PORT=5000
+MONGODB_URI=mongodb://localhost:27017/pet-management
+JWT_SECRET=your_jwt_secret_key_here
+NODE_ENV=development
\ No newline at end of file
diff --git a/server/config/db.js b/server/config/db.js
new file mode 100644
index 0000000..8ad5bb2
--- /dev/null
+++ b/server/config/db.js
@@ -0,0 +1,13 @@
+const mongoose = require('mongoose');
+
+const connectDB = async () => {
+ try {
+ const conn = await mongoose.connect(process.env.MONGODB_URI);
+ console.log(`MongoDB 连接成功: ${conn.connection.host}`);
+ } catch (error) {
+ console.error('MongoDB 连接失败:', error.message);
+ process.exit(1);
+ }
+};
+
+module.exports = connectDB;
\ No newline at end of file
diff --git a/server/middleware/auth.js b/server/middleware/auth.js
new file mode 100644
index 0000000..e6947b7
--- /dev/null
+++ b/server/middleware/auth.js
@@ -0,0 +1,25 @@
+const jwt = require('jsonwebtoken');
+
+const auth = (req, res, next) => {
+ try {
+ const token = req.header('Authorization')?.replace('Bearer ', '');
+
+ if (!token) {
+ return res.status(401).json({
+ success: false,
+ message: '没有访问令牌,访问被拒绝'
+ });
+ }
+
+ const decoded = jwt.verify(token, process.env.JWT_SECRET);
+ req.user = decoded.userId;
+ next();
+ } catch (error) {
+ res.status(401).json({
+ success: false,
+ message: '令牌无效或已过期'
+ });
+ }
+};
+
+module.exports = auth;
\ No newline at end of file
diff --git a/server/models/DailyRecord.js b/server/models/DailyRecord.js
new file mode 100644
index 0000000..4d31de4
--- /dev/null
+++ b/server/models/DailyRecord.js
@@ -0,0 +1,99 @@
+const mongoose = require('mongoose');
+
+const dailyRecordSchema = new mongoose.Schema({
+ userId: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'User',
+ required: true
+ },
+ petId: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'Pet',
+ required: true
+ },
+ recordDate: {
+ type: Date,
+ default: Date.now
+ },
+ meals: [{
+ type: {
+ type: String,
+ enum: ['早餐', '午餐', '晚餐', '加餐'],
+ required: true
+ },
+ food: {
+ type: String,
+ required: true
+ },
+ amount: {
+ type: String,
+ default: ''
+ },
+ time: {
+ type: Date,
+ default: Date.now
+ },
+ notes: {
+ type: String,
+ default: ''
+ }
+ }],
+ bowelMovements: [{
+ type: {
+ type: String,
+ enum: ['大便', '小便'],
+ required: true
+ },
+ time: {
+ type: Date,
+ default: Date.now
+ },
+ quantity: {
+ type: String,
+ enum: ['少量', '正常', '大量'],
+ default: '正常'
+ },
+ consistency: {
+ type: String,
+ enum: ['正常', '软便', '腹泻', '便秘'],
+ default: '正常'
+ },
+ notes: {
+ type: String,
+ default: ''
+ }
+ }],
+ exercises: [{
+ type: {
+ type: String,
+ enum: ['散步', '跑步', '玩耍', '训练', '其他'],
+ default: '其他'
+ },
+ duration: {
+ type: Number,
+ default: 0
+ },
+ time: {
+ type: Date,
+ default: Date.now
+ },
+ location: {
+ type: String,
+ default: ''
+ },
+ notes: {
+ type: String,
+ default: ''
+ }
+ }],
+ notes: {
+ type: String,
+ default: ''
+ }
+}, {
+ timestamps: true
+});
+
+dailyRecordSchema.index({ petId: 1, recordDate: 1 });
+
+module.exports = mongoose.model('DailyRecord', dailyRecordSchema);
\ No newline at end of file
diff --git a/server/models/MedicalRecord.js b/server/models/MedicalRecord.js
new file mode 100644
index 0000000..4804019
--- /dev/null
+++ b/server/models/MedicalRecord.js
@@ -0,0 +1,100 @@
+const mongoose = require('mongoose');
+
+const medicalRecordSchema = new mongoose.Schema({
+ userId: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'User',
+ required: true
+ },
+ petId: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'Pet',
+ required: true
+ },
+ visitDate: {
+ type: Date,
+ required: true
+ },
+ hospital: {
+ type: String,
+ default: ''
+ },
+ doctor: {
+ type: String,
+ default: ''
+ },
+ visitType: {
+ type: String,
+ enum: ['常规检查', '治疗', '手术', '疫苗接种', '急诊', '其他'],
+ default: '常规检查'
+ },
+ symptoms: {
+ type: String,
+ default: ''
+ },
+ diagnosis: {
+ type: String,
+ default: ''
+ },
+ treatment: {
+ type: String,
+ default: ''
+ },
+ medications: [{
+ name: {
+ type: String,
+ required: true
+ },
+ dosage: {
+ type: String,
+ default: ''
+ },
+ frequency: {
+ type: String,
+ default: ''
+ },
+ duration: {
+ type: String,
+ default: ''
+ }
+ }],
+ labResults: [{
+ name: {
+ type: String,
+ required: true
+ },
+ result: {
+ type: String,
+ default: ''
+ },
+ referenceRange: {
+ type: String,
+ default: ''
+ },
+ notes: {
+ type: String,
+ default: ''
+ }
+ }],
+ images: [{
+ type: String
+ }],
+ cost: {
+ type: Number,
+ default: 0
+ },
+ nextVisitDate: {
+ type: Date,
+ default: null
+ },
+ notes: {
+ type: String,
+ default: ''
+ }
+}, {
+ timestamps: true
+});
+
+medicalRecordSchema.index({ petId: 1, visitDate: -1 });
+
+module.exports = mongoose.model('MedicalRecord', medicalRecordSchema);
\ No newline at end of file
diff --git a/server/models/Pet.js b/server/models/Pet.js
new file mode 100644
index 0000000..95d0371
--- /dev/null
+++ b/server/models/Pet.js
@@ -0,0 +1,52 @@
+const mongoose = require('mongoose');
+
+const petSchema = new mongoose.Schema({
+ userId: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'User',
+ required: true
+ },
+ name: {
+ type: String,
+ required: true,
+ trim: true
+ },
+ species: {
+ type: String,
+ required: true,
+ enum: ['狗', '猫', '鸟', '兔子', '仓鼠', '其他']
+ },
+ breed: {
+ type: String,
+ default: ''
+ },
+ gender: {
+ type: String,
+ enum: ['公', '母', '未知'],
+ default: '未知'
+ },
+ birthDate: {
+ type: Date,
+ default: null
+ },
+ weight: {
+ type: Number,
+ default: null
+ },
+ avatar: {
+ type: String,
+ default: ''
+ },
+ description: {
+ type: String,
+ default: ''
+ },
+ isNeutered: {
+ type: Boolean,
+ default: false
+ }
+}, {
+ timestamps: true
+});
+
+module.exports = mongoose.model('Pet', petSchema);
\ No newline at end of file
diff --git a/server/models/Post.js b/server/models/Post.js
new file mode 100644
index 0000000..a30fadc
--- /dev/null
+++ b/server/models/Post.js
@@ -0,0 +1,56 @@
+const mongoose = require('mongoose');
+
+const postSchema = new mongoose.Schema({
+ userId: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'User',
+ required: true
+ },
+ petId: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'Pet',
+ default: null
+ },
+ content: {
+ type: String,
+ required: true,
+ maxlength: 500
+ },
+ images: [{
+ type: String
+ }],
+ likes: [{
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'User'
+ }],
+ comments: [{
+ userId: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'User',
+ required: true
+ },
+ content: {
+ type: String,
+ required: true,
+ maxlength: 200
+ },
+ createdAt: {
+ type: Date,
+ default: Date.now
+ }
+ }],
+ tags: [{
+ type: String
+ }],
+ visibility: {
+ type: String,
+ enum: ['公开', '好友可见', '仅自己可见'],
+ default: '公开'
+ }
+}, {
+ timestamps: true
+});
+
+postSchema.index({ userId: 1, createdAt: -1 });
+
+module.exports = mongoose.model('Post', postSchema);
\ No newline at end of file
diff --git a/server/models/Reminder.js b/server/models/Reminder.js
new file mode 100644
index 0000000..b1ca2bf
--- /dev/null
+++ b/server/models/Reminder.js
@@ -0,0 +1,63 @@
+const mongoose = require('mongoose');
+
+const reminderSchema = new mongoose.Schema({
+ userId: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'User',
+ required: true
+ },
+ petId: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'Pet',
+ required: true
+ },
+ type: {
+ type: String,
+ enum: ['疫苗', '驱虫', '体检', '其他'],
+ required: true
+ },
+ title: {
+ type: String,
+ required: true
+ },
+ description: {
+ type: String,
+ default: ''
+ },
+ reminderDate: {
+ type: Date,
+ required: true
+ },
+ reminderTime: {
+ type: String,
+ default: '09:00'
+ },
+ isRepeating: {
+ type: Boolean,
+ default: false
+ },
+ repeatInterval: {
+ type: String,
+ enum: ['天', '周', '月', '年'],
+ default: '月'
+ },
+ repeatFrequency: {
+ type: Number,
+ default: 1
+ },
+ isCompleted: {
+ type: Boolean,
+ default: false
+ },
+ completedDate: {
+ type: Date,
+ default: null
+ }
+}, {
+ timestamps: true
+});
+
+reminderSchema.index({ userId: 1, reminderDate: 1 });
+reminderSchema.index({ petId: 1 });
+
+module.exports = mongoose.model('Reminder', reminderSchema);
\ No newline at end of file
diff --git a/server/models/User.js b/server/models/User.js
new file mode 100644
index 0000000..fe0226e
--- /dev/null
+++ b/server/models/User.js
@@ -0,0 +1,57 @@
+const mongoose = require('mongoose');
+const bcrypt = require('bcryptjs');
+
+const userSchema = new mongoose.Schema({
+ username: {
+ type: String,
+ required: true,
+ unique: true,
+ trim: true,
+ minlength: 3,
+ maxlength: 30
+ },
+ email: {
+ type: String,
+ required: true,
+ unique: true,
+ trim: true,
+ lowercase: true
+ },
+ password: {
+ type: String,
+ required: true,
+ minlength: 6
+ },
+ avatar: {
+ type: String,
+ default: ''
+ },
+ nickname: {
+ type: String,
+ default: ''
+ },
+ bio: {
+ type: String,
+ default: ''
+ }
+}, {
+ timestamps: true
+});
+
+userSchema.pre('save', async function(next) {
+ if (!this.isModified('password')) return next();
+
+ try {
+ const salt = await bcrypt.genSalt(10);
+ this.password = await bcrypt.hash(this.password, salt);
+ next();
+ } catch (error) {
+ next(error);
+ }
+});
+
+userSchema.methods.comparePassword = async function(candidatePassword) {
+ return await bcrypt.compare(candidatePassword, this.password);
+};
+
+module.exports = mongoose.model('User', userSchema);
\ No newline at end of file
diff --git a/server/package-lock.json b/server/package-lock.json
new file mode 100644
index 0000000..037ded7
--- /dev/null
+++ b/server/package-lock.json
@@ -0,0 +1,1846 @@
+{
+ "name": "pet-management-server",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "pet-management-server",
+ "version": "1.0.0",
+ "license": "ISC",
+ "dependencies": {
+ "bcryptjs": "^2.4.3",
+ "cors": "^2.8.5",
+ "dotenv": "^16.3.1",
+ "express": "^4.18.2",
+ "express-validator": "^7.0.1",
+ "jsonwebtoken": "^9.0.2",
+ "mongoose": "^7.5.0",
+ "multer": "^1.4.5-lts.1"
+ },
+ "devDependencies": {
+ "nodemon": "^3.0.1"
+ }
+ },
+ "node_modules/@mongodb-js/saslprep": {
+ "version": "1.4.9",
+ "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.9.tgz",
+ "integrity": "sha512-RXSxsokhAF/4nWys8An8npsqOI33Ex1Hlzqjw2pZOO+GKtMAR2noGnUdsFiGwsaO/xXI+56mtjTmDA3JXJsvmA==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "sparse-bitfield": "^3.0.3"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "25.6.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz",
+ "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==",
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.19.0"
+ }
+ },
+ "node_modules/@types/webidl-conversions": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
+ "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/whatwg-url": {
+ "version": "8.2.2",
+ "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz",
+ "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "@types/webidl-conversions": "*"
+ }
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/append-field": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
+ "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==",
+ "license": "MIT"
+ },
+ "node_modules/array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
+ "license": "MIT"
+ },
+ "node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/bcryptjs": {
+ "version": "2.4.3",
+ "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
+ "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==",
+ "license": "MIT"
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/body-parser": {
+ "version": "1.20.5",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz",
+ "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "content-type": "~1.0.5",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "~1.2.0",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "on-finished": "~2.4.1",
+ "qs": "~6.15.1",
+ "raw-body": "~2.5.3",
+ "type-is": "~1.6.18",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/body-parser/node_modules/qs": {
+ "version": "6.15.1",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz",
+ "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
+ "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/bson": {
+ "version": "5.5.1",
+ "resolved": "https://registry.npmjs.org/bson/-/bson-5.5.1.tgz",
+ "integrity": "sha512-ix0EwukN2EpC0SRWIj/7B5+A6uQMQy6KMREI9qQqvgpkV2frH63T0UDVd1SYedL6dNCmDBYB3QtXi4ISk9YT+g==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=14.20.1"
+ }
+ },
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "license": "MIT"
+ },
+ "node_modules/busboy": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+ "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+ "dependencies": {
+ "streamsearch": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=10.16.0"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "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",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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/concat-stream": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+ "engines": [
+ "node >= 0.8"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
+ }
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "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/cookie-signature": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
+ "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
+ "license": "MIT"
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "license": "MIT"
+ },
+ "node_modules/cors": {
+ "version": "2.8.6",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
+ "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
+ "license": "MIT",
+ "dependencies": {
+ "object-assign": "^4",
+ "vary": "^1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "16.6.1",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
+ "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "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==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "license": "MIT"
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "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==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "license": "MIT"
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/express": {
+ "version": "4.22.1",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
+ "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "~1.3.8",
+ "array-flatten": "1.1.1",
+ "body-parser": "~1.20.3",
+ "content-disposition": "~0.5.4",
+ "content-type": "~1.0.4",
+ "cookie": "~0.7.1",
+ "cookie-signature": "~1.0.6",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "~1.3.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.0",
+ "merge-descriptors": "1.0.3",
+ "methods": "~1.1.2",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "~0.1.12",
+ "proxy-addr": "~2.0.7",
+ "qs": "~6.14.0",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.2.1",
+ "send": "~0.19.0",
+ "serve-static": "~1.16.2",
+ "setprototypeof": "1.2.0",
+ "statuses": "~2.0.1",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/express-validator": {
+ "version": "7.3.2",
+ "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-7.3.2.tgz",
+ "integrity": "sha512-ctLw1Vl6dXVH62dIQMDdTAQkrh480mkFuG6/SGXOaVlwPNukhRAe7EgJIMJ2TSAni8iwHBRp530zAZE5ZPF2IA==",
+ "license": "MIT",
+ "dependencies": {
+ "lodash": "^4.18.1",
+ "validator": "~13.15.23"
+ },
+ "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",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
+ "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "statuses": "~2.0.2",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz",
+ "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ignore-by-default": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
+ "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
+ "node_modules/ip-address": {
+ "version": "10.1.1",
+ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.1.tgz",
+ "integrity": "sha512-1FMu8/N15Ck1BL551Jf42NYIoin2unWjLQ2Fze/DXryJRl5twqtwNHlO39qERGbIOcKYWHdgRryhOC+NG4eaLw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "license": "MIT"
+ },
+ "node_modules/jsonwebtoken": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
+ "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==",
+ "license": "MIT",
+ "dependencies": {
+ "jws": "^4.0.1",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6"
+ }
+ },
+ "node_modules/jsonwebtoken/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/jwa": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
+ "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer-equal-constant-time": "^1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jws": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
+ "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
+ "license": "MIT",
+ "dependencies": {
+ "jwa": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/kareem": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz",
+ "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
+ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
+ "license": "MIT"
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/memory-pager": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
+ "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/merge-descriptors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
+ "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "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": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.5"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.6"
+ },
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ }
+ },
+ "node_modules/mongodb": {
+ "version": "5.9.2",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.9.2.tgz",
+ "integrity": "sha512-H60HecKO4Bc+7dhOv4sJlgvenK4fQNqqUIlXxZYQNbfEWSALGAwGoyJd/0Qwk4TttFXUOHJ2ZJQe/52ScaUwtQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bson": "^5.5.0",
+ "mongodb-connection-string-url": "^2.6.0",
+ "socks": "^2.7.1"
+ },
+ "engines": {
+ "node": ">=14.20.1"
+ },
+ "optionalDependencies": {
+ "@mongodb-js/saslprep": "^1.1.0"
+ },
+ "peerDependencies": {
+ "@aws-sdk/credential-providers": "^3.188.0",
+ "@mongodb-js/zstd": "^1.0.0",
+ "kerberos": "^1.0.0 || ^2.0.0",
+ "mongodb-client-encryption": ">=2.3.0 <3",
+ "snappy": "^7.2.2"
+ },
+ "peerDependenciesMeta": {
+ "@aws-sdk/credential-providers": {
+ "optional": true
+ },
+ "@mongodb-js/zstd": {
+ "optional": true
+ },
+ "kerberos": {
+ "optional": true
+ },
+ "mongodb-client-encryption": {
+ "optional": true
+ },
+ "snappy": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/mongodb-connection-string-url": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz",
+ "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/whatwg-url": "^8.2.1",
+ "whatwg-url": "^11.0.0"
+ }
+ },
+ "node_modules/mongoose": {
+ "version": "7.8.9",
+ "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-7.8.9.tgz",
+ "integrity": "sha512-V3GBAJbmOAdzEP8murOvlg7q1szlbe4jTBRyW+JBHRduJBe7F9dk5eyqJDTuYrdBcOOWfLbr6AgXrDK7F0/o5A==",
+ "license": "MIT",
+ "dependencies": {
+ "bson": "^5.5.0",
+ "kareem": "2.5.1",
+ "mongodb": "5.9.2",
+ "mpath": "0.9.0",
+ "mquery": "5.0.0",
+ "ms": "2.1.3",
+ "sift": "16.0.1"
+ },
+ "engines": {
+ "node": ">=14.20.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mongoose"
+ }
+ },
+ "node_modules/mongoose/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/mpath": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz",
+ "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/mquery": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz",
+ "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "4.x"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/mquery/node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/mquery/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "license": "MIT"
+ },
+ "node_modules/multer": {
+ "version": "1.4.5-lts.2",
+ "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz",
+ "integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==",
+ "deprecated": "Multer 1.x is impacted by a number of vulnerabilities, which have been patched in 2.x. You should upgrade to the latest 2.x version.",
+ "license": "MIT",
+ "dependencies": {
+ "append-field": "^1.0.0",
+ "busboy": "^1.0.0",
+ "concat-stream": "^1.5.2",
+ "mkdirp": "^0.5.4",
+ "object-assign": "^4.1.1",
+ "type-is": "^1.6.4",
+ "xtend": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/nodemon": {
+ "version": "3.1.14",
+ "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz",
+ "integrity": "sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chokidar": "^3.5.2",
+ "debug": "^4",
+ "ignore-by-default": "^1.0.1",
+ "minimatch": "^10.2.1",
+ "pstree.remy": "^1.1.8",
+ "semver": "^7.5.3",
+ "simple-update-notifier": "^2.0.0",
+ "supports-color": "^5.5.0",
+ "touch": "^3.1.0",
+ "undefsafe": "^2.0.5"
+ },
+ "bin": {
+ "nodemon": "bin/nodemon.js"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/nodemon"
+ }
+ },
+ "node_modules/nodemon/node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/nodemon/node_modules/ms": {
+ "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/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/path-to-regexp": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz",
+ "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==",
+ "license": "MIT"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "license": "MIT"
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/pstree.remy": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
+ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.14.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
+ "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
+ "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/readable-stream/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "license": "MIT"
+ },
+ "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/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/send": {
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
+ "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.1",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "~2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "~2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/send/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/serve-static": {
+ "version": "1.16.3",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",
+ "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "~0.19.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "license": "ISC"
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
+ "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/sift": {
+ "version": "16.0.1",
+ "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz",
+ "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==",
+ "license": "MIT"
+ },
+ "node_modules/simple-update-notifier": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
+ "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.5.3"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/smart-buffer": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+ "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/socks": {
+ "version": "2.8.8",
+ "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.8.tgz",
+ "integrity": "sha512-NlGELfPrgX2f1TAAcz0WawlLn+0r3FyhhCRpFFK2CemXenPYvzMWWZINv3eDNo9ucdwme7oCHRY0Jnbs4aIkog==",
+ "license": "MIT",
+ "dependencies": {
+ "ip-address": "^10.1.1",
+ "smart-buffer": "^4.2.0"
+ },
+ "engines": {
+ "node": ">= 10.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/sparse-bitfield": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
+ "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "memory-pager": "^1.0.2"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/streamsearch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+ "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/string_decoder/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "license": "MIT"
+ },
+ "node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/touch": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
+ "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "nodetouch": "bin/nodetouch.js"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
+ "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
+ "license": "MIT",
+ "dependencies": {
+ "punycode": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "license": "MIT",
+ "dependencies": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
+ "license": "MIT"
+ },
+ "node_modules/undefsafe": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
+ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/undici-types": {
+ "version": "7.19.2",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz",
+ "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==",
+ "license": "MIT"
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "license": "MIT"
+ },
+ "node_modules/utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/validator": {
+ "version": "13.15.35",
+ "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.35.tgz",
+ "integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+ "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/whatwg-url": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
+ "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "tr46": "^3.0.0",
+ "webidl-conversions": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4"
+ }
+ }
+ }
+}
diff --git a/server/package.json b/server/package.json
new file mode 100644
index 0000000..d2f8c29
--- /dev/null
+++ b/server/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "pet-management-server",
+ "version": "1.0.0",
+ "description": "宠物日常管理平台后端服务",
+ "main": "server.js",
+ "scripts": {
+ "start": "node server.js",
+ "dev": "nodemon server.js",
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": ["pet", "management", "express", "mongodb"],
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "bcryptjs": "^2.4.3",
+ "cors": "^2.8.5",
+ "dotenv": "^16.3.1",
+ "express": "^4.18.2",
+ "express-validator": "^7.0.1",
+ "jsonwebtoken": "^9.0.2",
+ "mongoose": "^7.5.0",
+ "multer": "^1.4.5-lts.1"
+ },
+ "devDependencies": {
+ "nodemon": "^3.0.1"
+ }
+}
\ No newline at end of file
diff --git a/server/routes/auth.js b/server/routes/auth.js
new file mode 100644
index 0000000..6267a0f
--- /dev/null
+++ b/server/routes/auth.js
@@ -0,0 +1,203 @@
+const express = require('express');
+const router = express.Router();
+const { body, validationResult } = require('express-validator');
+const jwt = require('jsonwebtoken');
+const User = require('../models/User');
+const auth = require('../middleware/auth');
+
+const generateToken = (userId) => {
+ return jwt.sign({ userId }, process.env.JWT_SECRET, { expiresIn: '7d' });
+};
+
+router.post('/register', [
+ body('username').isLength({ min: 3, max: 30 }).withMessage('用户名长度需在3-30个字符之间'),
+ body('email').isEmail().withMessage('请输入有效的邮箱地址'),
+ body('password').isLength({ min: 6 }).withMessage('密码至少需要6个字符')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const { username, email, password } = req.body;
+
+ const existingUser = await User.findOne({
+ $or: [{ email }, { username }]
+ });
+
+ if (existingUser) {
+ return res.status(400).json({
+ success: false,
+ message: '用户名或邮箱已被注册'
+ });
+ }
+
+ const user = new User({
+ username,
+ email,
+ password
+ });
+
+ await user.save();
+
+ const token = generateToken(user._id);
+
+ res.status(201).json({
+ success: true,
+ message: '注册成功',
+ data: {
+ token,
+ user: {
+ id: user._id,
+ username: user.username,
+ email: user.email,
+ avatar: user.avatar,
+ nickname: user.nickname
+ }
+ }
+ });
+ } catch (error) {
+ console.error('注册错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.post('/login', [
+ body('email').isEmail().withMessage('请输入有效的邮箱地址'),
+ body('password').exists().withMessage('请输入密码')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const { email, password } = req.body;
+
+ const user = await User.findOne({ email });
+ if (!user) {
+ return res.status(401).json({
+ success: false,
+ message: '邮箱或密码错误'
+ });
+ }
+
+ const isMatch = await user.comparePassword(password);
+ if (!isMatch) {
+ return res.status(401).json({
+ success: false,
+ message: '邮箱或密码错误'
+ });
+ }
+
+ const token = generateToken(user._id);
+
+ res.json({
+ success: true,
+ message: '登录成功',
+ data: {
+ token,
+ user: {
+ id: user._id,
+ username: user.username,
+ email: user.email,
+ avatar: user.avatar,
+ nickname: user.nickname
+ }
+ }
+ });
+ } catch (error) {
+ console.error('登录错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.get('/profile', auth, async (req, res) => {
+ try {
+ const user = await User.findById(req.user).select('-password');
+
+ if (!user) {
+ return res.status(404).json({
+ success: false,
+ message: '用户不存在'
+ });
+ }
+
+ res.json({
+ success: true,
+ data: {
+ user: {
+ id: user._id,
+ username: user.username,
+ email: user.email,
+ avatar: user.avatar,
+ nickname: user.nickname,
+ bio: user.bio
+ }
+ }
+ });
+ } catch (error) {
+ console.error('获取用户信息错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.put('/profile', auth, async (req, res) => {
+ try {
+ const { nickname, avatar, bio } = req.body;
+
+ const user = await User.findById(req.user);
+
+ if (!user) {
+ return res.status(404).json({
+ success: false,
+ message: '用户不存在'
+ });
+ }
+
+ if (nickname !== undefined) user.nickname = nickname;
+ if (avatar !== undefined) user.avatar = avatar;
+ if (bio !== undefined) user.bio = bio;
+
+ await user.save();
+
+ res.json({
+ success: true,
+ message: '个人信息更新成功',
+ data: {
+ user: {
+ id: user._id,
+ username: user.username,
+ email: user.email,
+ avatar: user.avatar,
+ nickname: user.nickname,
+ bio: user.bio
+ }
+ }
+ });
+ } catch (error) {
+ console.error('更新用户信息错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+module.exports = router;
\ No newline at end of file
diff --git a/server/routes/dailyRecords.js b/server/routes/dailyRecords.js
new file mode 100644
index 0000000..234bf82
--- /dev/null
+++ b/server/routes/dailyRecords.js
@@ -0,0 +1,183 @@
+const express = require('express');
+const router = express.Router();
+const { body, validationResult, param, query } = require('express-validator');
+const DailyRecord = require('../models/DailyRecord');
+const auth = require('../middleware/auth');
+
+router.get('/', auth, [
+ query('petId').optional().isMongoId().withMessage('无效的宠物ID'),
+ query('date').optional().isISO8601().withMessage('无效的日期格式')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const { petId, date } = req.query;
+ let query = { userId: req.user };
+
+ if (petId) {
+ query.petId = petId;
+ }
+
+ if (date) {
+ const startDate = new Date(date);
+ startDate.setHours(0, 0, 0, 0);
+ const endDate = new Date(date);
+ endDate.setHours(23, 59, 59, 999);
+ query.recordDate = { $gte: startDate, $lte: endDate };
+ }
+
+ const records = await DailyRecord.find(query)
+ .populate('petId', 'name species avatar')
+ .sort({ recordDate: -1 });
+
+ res.json({
+ success: true,
+ data: {
+ records
+ }
+ });
+ } catch (error) {
+ console.error('获取日常记录错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.post('/', auth, [
+ body('petId').isMongoId().withMessage('无效的宠物ID')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const { petId, recordDate, meals, bowelMovements, exercises, notes } = req.body;
+
+ const record = new DailyRecord({
+ userId: req.user,
+ petId,
+ recordDate: recordDate || Date.now(),
+ meals: meals || [],
+ bowelMovements: bowelMovements || [],
+ exercises: exercises || [],
+ notes
+ });
+
+ await record.save();
+
+ res.status(201).json({
+ success: true,
+ message: '日常记录添加成功',
+ data: {
+ record
+ }
+ });
+ } catch (error) {
+ console.error('添加日常记录错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.put('/:id', auth, [
+ param('id').isMongoId().withMessage('无效的记录ID')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const { recordDate, meals, bowelMovements, exercises, notes } = req.body;
+
+ const record = await DailyRecord.findOne({
+ _id: req.params.id,
+ userId: req.user
+ });
+
+ if (!record) {
+ return res.status(404).json({
+ success: false,
+ message: '记录不存在'
+ });
+ }
+
+ if (recordDate !== undefined) record.recordDate = recordDate;
+ if (meals !== undefined) record.meals = meals;
+ if (bowelMovements !== undefined) record.bowelMovements = bowelMovements;
+ if (exercises !== undefined) record.exercises = exercises;
+ if (notes !== undefined) record.notes = notes;
+
+ await record.save();
+
+ res.json({
+ success: true,
+ message: '日常记录更新成功',
+ data: {
+ record
+ }
+ });
+ } catch (error) {
+ console.error('更新日常记录错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.delete('/:id', auth, [
+ param('id').isMongoId().withMessage('无效的记录ID')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const record = await DailyRecord.findOneAndDelete({
+ _id: req.params.id,
+ userId: req.user
+ });
+
+ if (!record) {
+ return res.status(404).json({
+ success: false,
+ message: '记录不存在'
+ });
+ }
+
+ res.json({
+ success: true,
+ message: '日常记录删除成功'
+ });
+ } catch (error) {
+ console.error('删除日常记录错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+module.exports = router;
\ No newline at end of file
diff --git a/server/routes/medicalRecords.js b/server/routes/medicalRecords.js
new file mode 100644
index 0000000..2c31ec4
--- /dev/null
+++ b/server/routes/medicalRecords.js
@@ -0,0 +1,259 @@
+const express = require('express');
+const router = express.Router();
+const { body, validationResult, param, query } = require('express-validator');
+const MedicalRecord = require('../models/MedicalRecord');
+const auth = require('../middleware/auth');
+
+router.get('/', auth, [
+ query('petId').optional().isMongoId().withMessage('无效的宠物ID')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const { petId } = req.query;
+ let query = { userId: req.user };
+
+ if (petId) {
+ query.petId = petId;
+ }
+
+ const records = await MedicalRecord.find(query)
+ .populate('petId', 'name species avatar')
+ .sort({ visitDate: -1 });
+
+ res.json({
+ success: true,
+ data: {
+ records
+ }
+ });
+ } catch (error) {
+ console.error('获取就医记录错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.get('/:id', auth, [
+ param('id').isMongoId().withMessage('无效的记录ID')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const record = await MedicalRecord.findOne({
+ _id: req.params.id,
+ userId: req.user
+ }).populate('petId', 'name species avatar');
+
+ if (!record) {
+ return res.status(404).json({
+ success: false,
+ message: '就医记录不存在'
+ });
+ }
+
+ res.json({
+ success: true,
+ data: {
+ record
+ }
+ });
+ } catch (error) {
+ console.error('获取就医记录错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.post('/', auth, [
+ body('petId').isMongoId().withMessage('无效的宠物ID'),
+ body('visitDate').isISO8601().withMessage('请输入有效的就诊日期')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const {
+ petId,
+ visitDate,
+ hospital,
+ doctor,
+ visitType,
+ symptoms,
+ diagnosis,
+ treatment,
+ medications,
+ labResults,
+ images,
+ cost,
+ nextVisitDate,
+ notes
+ } = req.body;
+
+ const record = new MedicalRecord({
+ userId: req.user,
+ petId,
+ visitDate,
+ hospital,
+ doctor,
+ visitType: visitType || '常规检查',
+ symptoms,
+ diagnosis,
+ treatment,
+ medications: medications || [],
+ labResults: labResults || [],
+ images: images || [],
+ cost: cost || 0,
+ nextVisitDate,
+ notes
+ });
+
+ await record.save();
+
+ res.status(201).json({
+ success: true,
+ message: '就医记录添加成功',
+ data: {
+ record
+ }
+ });
+ } catch (error) {
+ console.error('添加就医记录错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.put('/:id', auth, [
+ param('id').isMongoId().withMessage('无效的记录ID')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const {
+ visitDate,
+ hospital,
+ doctor,
+ visitType,
+ symptoms,
+ diagnosis,
+ treatment,
+ medications,
+ labResults,
+ images,
+ cost,
+ nextVisitDate,
+ notes
+ } = req.body;
+
+ const record = await MedicalRecord.findOne({
+ _id: req.params.id,
+ userId: req.user
+ });
+
+ if (!record) {
+ return res.status(404).json({
+ success: false,
+ message: '就医记录不存在'
+ });
+ }
+
+ if (visitDate !== undefined) record.visitDate = visitDate;
+ if (hospital !== undefined) record.hospital = hospital;
+ if (doctor !== undefined) record.doctor = doctor;
+ if (visitType !== undefined) record.visitType = visitType;
+ if (symptoms !== undefined) record.symptoms = symptoms;
+ if (diagnosis !== undefined) record.diagnosis = diagnosis;
+ if (treatment !== undefined) record.treatment = treatment;
+ if (medications !== undefined) record.medications = medications;
+ if (labResults !== undefined) record.labResults = labResults;
+ if (images !== undefined) record.images = images;
+ if (cost !== undefined) record.cost = cost;
+ if (nextVisitDate !== undefined) record.nextVisitDate = nextVisitDate;
+ if (notes !== undefined) record.notes = notes;
+
+ await record.save();
+
+ res.json({
+ success: true,
+ message: '就医记录更新成功',
+ data: {
+ record
+ }
+ });
+ } catch (error) {
+ console.error('更新就医记录错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.delete('/:id', auth, [
+ param('id').isMongoId().withMessage('无效的记录ID')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const record = await MedicalRecord.findOneAndDelete({
+ _id: req.params.id,
+ userId: req.user
+ });
+
+ if (!record) {
+ return res.status(404).json({
+ success: false,
+ message: '就医记录不存在'
+ });
+ }
+
+ res.json({
+ success: true,
+ message: '就医记录删除成功'
+ });
+ } catch (error) {
+ console.error('删除就医记录错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+module.exports = router;
\ No newline at end of file
diff --git a/server/routes/pets.js b/server/routes/pets.js
new file mode 100644
index 0000000..74d0cbe
--- /dev/null
+++ b/server/routes/pets.js
@@ -0,0 +1,204 @@
+const express = require('express');
+const router = express.Router();
+const { body, validationResult, param } = require('express-validator');
+const Pet = require('../models/Pet');
+const auth = require('../middleware/auth');
+
+router.get('/', auth, async (req, res) => {
+ try {
+ const pets = await Pet.find({ userId: req.user }).sort({ createdAt: -1 });
+
+ res.json({
+ success: true,
+ data: {
+ pets
+ }
+ });
+ } catch (error) {
+ console.error('获取宠物列表错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.get('/:id', auth, [
+ param('id').isMongoId().withMessage('无效的宠物ID')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const pet = await Pet.findOne({
+ _id: req.params.id,
+ userId: req.user
+ });
+
+ if (!pet) {
+ return res.status(404).json({
+ success: false,
+ message: '宠物不存在'
+ });
+ }
+
+ res.json({
+ success: true,
+ data: {
+ pet
+ }
+ });
+ } catch (error) {
+ console.error('获取宠物信息错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.post('/', auth, [
+ body('name').notEmpty().withMessage('宠物名称不能为空'),
+ body('species').isIn(['狗', '猫', '鸟', '兔子', '仓鼠', '其他']).withMessage('请选择有效的宠物种类')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const { name, species, breed, gender, birthDate, weight, avatar, description, isNeutered } = req.body;
+
+ const pet = new Pet({
+ userId: req.user,
+ name,
+ species,
+ breed,
+ gender,
+ birthDate,
+ weight,
+ avatar,
+ description,
+ isNeutered
+ });
+
+ await pet.save();
+
+ res.status(201).json({
+ success: true,
+ message: '宠物添加成功',
+ data: {
+ pet
+ }
+ });
+ } catch (error) {
+ console.error('添加宠物错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.put('/:id', auth, [
+ param('id').isMongoId().withMessage('无效的宠物ID'),
+ body('name').optional().notEmpty().withMessage('宠物名称不能为空'),
+ body('species').optional().isIn(['狗', '猫', '鸟', '兔子', '仓鼠', '其他']).withMessage('请选择有效的宠物种类')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const { name, species, breed, gender, birthDate, weight, avatar, description, isNeutered } = req.body;
+
+ const pet = await Pet.findOne({
+ _id: req.params.id,
+ userId: req.user
+ });
+
+ if (!pet) {
+ return res.status(404).json({
+ success: false,
+ message: '宠物不存在'
+ });
+ }
+
+ if (name !== undefined) pet.name = name;
+ if (species !== undefined) pet.species = species;
+ if (breed !== undefined) pet.breed = breed;
+ if (gender !== undefined) pet.gender = gender;
+ if (birthDate !== undefined) pet.birthDate = birthDate;
+ if (weight !== undefined) pet.weight = weight;
+ if (avatar !== undefined) pet.avatar = avatar;
+ if (description !== undefined) pet.description = description;
+ if (isNeutered !== undefined) pet.isNeutered = isNeutered;
+
+ await pet.save();
+
+ res.json({
+ success: true,
+ message: '宠物信息更新成功',
+ data: {
+ pet
+ }
+ });
+ } catch (error) {
+ console.error('更新宠物信息错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.delete('/:id', auth, [
+ param('id').isMongoId().withMessage('无效的宠物ID')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const pet = await Pet.findOneAndDelete({
+ _id: req.params.id,
+ userId: req.user
+ });
+
+ if (!pet) {
+ return res.status(404).json({
+ success: false,
+ message: '宠物不存在'
+ });
+ }
+
+ res.json({
+ success: true,
+ message: '宠物删除成功'
+ });
+ } catch (error) {
+ console.error('删除宠物错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+module.exports = router;
\ No newline at end of file
diff --git a/server/routes/posts.js b/server/routes/posts.js
new file mode 100644
index 0000000..9b0612c
--- /dev/null
+++ b/server/routes/posts.js
@@ -0,0 +1,333 @@
+const express = require('express');
+const router = express.Router();
+const { body, validationResult, param, query } = require('express-validator');
+const Post = require('../models/Post');
+const auth = require('../middleware/auth');
+
+router.get('/', auth, [
+ query('userId').optional().isMongoId().withMessage('无效的用户ID'),
+ query('page').optional().isInt({ min: 1 }).withMessage('页码必须大于0'),
+ query('limit').optional().isInt({ min: 1, max: 50 }).withMessage('每页数量必须在1-50之间')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const { userId, page = 1, limit = 10 } = req.query;
+ const skip = (page - 1) * limit;
+
+ let query = { visibility: '公开' };
+
+ if (userId) {
+ query.userId = userId;
+ if (userId === req.user) {
+ delete query.visibility;
+ }
+ }
+
+ const total = await Post.countDocuments(query);
+ const posts = await Post.find(query)
+ .populate('userId', 'username avatar nickname')
+ .populate('petId', 'name species avatar')
+ .populate('comments.userId', 'username avatar nickname')
+ .sort({ createdAt: -1 })
+ .skip(skip)
+ .limit(parseInt(limit));
+
+ res.json({
+ success: true,
+ data: {
+ posts,
+ pagination: {
+ page: parseInt(page),
+ limit: parseInt(limit),
+ total,
+ pages: Math.ceil(total / limit)
+ }
+ }
+ });
+ } catch (error) {
+ console.error('获取朋友圈列表错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.get('/my', auth, [
+ query('page').optional().isInt({ min: 1 }).withMessage('页码必须大于0'),
+ query('limit').optional().isInt({ min: 1, max: 50 }).withMessage('每页数量必须在1-50之间')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const { page = 1, limit = 10 } = req.query;
+ const skip = (page - 1) * limit;
+
+ const total = await Post.countDocuments({ userId: req.user });
+ const posts = await Post.find({ userId: req.user })
+ .populate('userId', 'username avatar nickname')
+ .populate('petId', 'name species avatar')
+ .populate('comments.userId', 'username avatar nickname')
+ .sort({ createdAt: -1 })
+ .skip(skip)
+ .limit(parseInt(limit));
+
+ res.json({
+ success: true,
+ data: {
+ posts,
+ pagination: {
+ page: parseInt(page),
+ limit: parseInt(limit),
+ total,
+ pages: Math.ceil(total / limit)
+ }
+ }
+ });
+ } catch (error) {
+ console.error('获取我的朋友圈错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.get('/:id', auth, [
+ param('id').isMongoId().withMessage('无效的帖子ID')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const post = await Post.findById(req.params.id)
+ .populate('userId', 'username avatar nickname')
+ .populate('petId', 'name species avatar')
+ .populate('comments.userId', 'username avatar nickname');
+
+ if (!post) {
+ return res.status(404).json({
+ success: false,
+ message: '帖子不存在'
+ });
+ }
+
+ if (post.visibility !== '公开' && post.userId.toString() !== req.user) {
+ return res.status(403).json({
+ success: false,
+ message: '无权限查看此帖子'
+ });
+ }
+
+ res.json({
+ success: true,
+ data: {
+ post
+ }
+ });
+ } catch (error) {
+ console.error('获取帖子详情错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.post('/', auth, [
+ body('content').notEmpty().withMessage('内容不能为空').isLength({ max: 500 }).withMessage('内容不能超过500个字符')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const { content, petId, images, tags, visibility } = req.body;
+
+ const post = new Post({
+ userId: req.user,
+ petId,
+ content,
+ images: images || [],
+ tags: tags || [],
+ visibility: visibility || '公开'
+ });
+
+ await post.save();
+ await post.populate('userId', 'username avatar nickname');
+ if (post.petId) {
+ await post.populate('petId', 'name species avatar');
+ }
+
+ res.status(201).json({
+ success: true,
+ message: '帖子发布成功',
+ data: {
+ post
+ }
+ });
+ } catch (error) {
+ console.error('发布帖子错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.post('/:id/like', auth, [
+ param('id').isMongoId().withMessage('无效的帖子ID')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const post = await Post.findById(req.params.id);
+
+ if (!post) {
+ return res.status(404).json({
+ success: false,
+ message: '帖子不存在'
+ });
+ }
+
+ const likeIndex = post.likes.indexOf(req.user);
+
+ if (likeIndex > -1) {
+ post.likes.splice(likeIndex, 1);
+ } else {
+ post.likes.push(req.user);
+ }
+
+ await post.save();
+
+ res.json({
+ success: true,
+ message: likeIndex > -1 ? '取消点赞成功' : '点赞成功',
+ data: {
+ isLiked: likeIndex === -1,
+ likeCount: post.likes.length
+ }
+ });
+ } catch (error) {
+ console.error('点赞错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.post('/:id/comment', auth, [
+ param('id').isMongoId().withMessage('无效的帖子ID'),
+ body('content').notEmpty().withMessage('评论内容不能为空').isLength({ max: 200 }).withMessage('评论不能超过200个字符')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const { content } = req.body;
+
+ const post = await Post.findById(req.params.id);
+
+ if (!post) {
+ return res.status(404).json({
+ success: false,
+ message: '帖子不存在'
+ });
+ }
+
+ post.comments.push({
+ userId: req.user,
+ content
+ });
+
+ await post.save();
+ await post.populate('comments.userId', 'username avatar nickname');
+
+ res.json({
+ success: true,
+ message: '评论成功',
+ data: {
+ comments: post.comments
+ }
+ });
+ } catch (error) {
+ console.error('评论错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.delete('/:id', auth, [
+ param('id').isMongoId().withMessage('无效的帖子ID')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const post = await Post.findOneAndDelete({
+ _id: req.params.id,
+ userId: req.user
+ });
+
+ if (!post) {
+ return res.status(404).json({
+ success: false,
+ message: '帖子不存在或无权限删除'
+ });
+ }
+
+ res.json({
+ success: true,
+ message: '帖子删除成功'
+ });
+ } catch (error) {
+ console.error('删除帖子错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+module.exports = router;
\ No newline at end of file
diff --git a/server/routes/reminders.js b/server/routes/reminders.js
new file mode 100644
index 0000000..86c5ff8
--- /dev/null
+++ b/server/routes/reminders.js
@@ -0,0 +1,255 @@
+const express = require('express');
+const router = express.Router();
+const { body, validationResult, param, query } = require('express-validator');
+const Reminder = require('../models/Reminder');
+const auth = require('../middleware/auth');
+
+router.get('/', auth, [
+ query('petId').optional().isMongoId().withMessage('无效的宠物ID'),
+ query('upcoming').optional().isBoolean().withMessage('无效的参数')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const { petId, upcoming } = req.query;
+ let query = { userId: req.user };
+
+ if (petId) {
+ query.petId = petId;
+ }
+
+ if (upcoming === 'true') {
+ const now = new Date();
+ query.reminderDate = { $gte: now };
+ query.isCompleted = false;
+ }
+
+ const reminders = await Reminder.find(query)
+ .populate('petId', 'name species avatar')
+ .sort({ reminderDate: 1 });
+
+ res.json({
+ success: true,
+ data: {
+ reminders
+ }
+ });
+ } catch (error) {
+ console.error('获取提醒列表错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.get('/:id', auth, [
+ param('id').isMongoId().withMessage('无效的提醒ID')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const reminder = await Reminder.findOne({
+ _id: req.params.id,
+ userId: req.user
+ }).populate('petId', 'name species avatar');
+
+ if (!reminder) {
+ return res.status(404).json({
+ success: false,
+ message: '提醒不存在'
+ });
+ }
+
+ res.json({
+ success: true,
+ data: {
+ reminder
+ }
+ });
+ } catch (error) {
+ console.error('获取提醒信息错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.post('/', auth, [
+ body('petId').isMongoId().withMessage('无效的宠物ID'),
+ body('type').isIn(['疫苗', '驱虫', '体检', '其他']).withMessage('请选择有效的提醒类型'),
+ body('title').notEmpty().withMessage('提醒标题不能为空'),
+ body('reminderDate').isISO8601().withMessage('请输入有效的日期')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const {
+ petId,
+ type,
+ title,
+ description,
+ reminderDate,
+ reminderTime,
+ isRepeating,
+ repeatInterval,
+ repeatFrequency
+ } = req.body;
+
+ const reminder = new Reminder({
+ userId: req.user,
+ petId,
+ type,
+ title,
+ description,
+ reminderDate,
+ reminderTime: reminderTime || '09:00',
+ isRepeating: isRepeating || false,
+ repeatInterval: repeatInterval || '月',
+ repeatFrequency: repeatFrequency || 1
+ });
+
+ await reminder.save();
+
+ res.status(201).json({
+ success: true,
+ message: '提醒创建成功',
+ data: {
+ reminder
+ }
+ });
+ } catch (error) {
+ console.error('创建提醒错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.put('/:id', auth, [
+ param('id').isMongoId().withMessage('无效的提醒ID')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const {
+ type,
+ title,
+ description,
+ reminderDate,
+ reminderTime,
+ isRepeating,
+ repeatInterval,
+ repeatFrequency,
+ isCompleted
+ } = req.body;
+
+ const reminder = await Reminder.findOne({
+ _id: req.params.id,
+ userId: req.user
+ });
+
+ if (!reminder) {
+ return res.status(404).json({
+ success: false,
+ message: '提醒不存在'
+ });
+ }
+
+ if (type !== undefined) reminder.type = type;
+ if (title !== undefined) reminder.title = title;
+ if (description !== undefined) reminder.description = description;
+ if (reminderDate !== undefined) reminder.reminderDate = reminderDate;
+ if (reminderTime !== undefined) reminder.reminderTime = reminderTime;
+ if (isRepeating !== undefined) reminder.isRepeating = isRepeating;
+ if (repeatInterval !== undefined) reminder.repeatInterval = repeatInterval;
+ if (repeatFrequency !== undefined) reminder.repeatFrequency = repeatFrequency;
+ if (isCompleted !== undefined) {
+ reminder.isCompleted = isCompleted;
+ if (isCompleted) {
+ reminder.completedDate = Date.now();
+ }
+ }
+
+ await reminder.save();
+
+ res.json({
+ success: true,
+ message: '提醒更新成功',
+ data: {
+ reminder
+ }
+ });
+ } catch (error) {
+ console.error('更新提醒错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+router.delete('/:id', auth, [
+ param('id').isMongoId().withMessage('无效的提醒ID')
+], async (req, res) => {
+ try {
+ const errors = validationResult(req);
+ if (!errors.isEmpty()) {
+ return res.status(400).json({
+ success: false,
+ errors: errors.array()
+ });
+ }
+
+ const reminder = await Reminder.findOneAndDelete({
+ _id: req.params.id,
+ userId: req.user
+ });
+
+ if (!reminder) {
+ return res.status(404).json({
+ success: false,
+ message: '提醒不存在'
+ });
+ }
+
+ res.json({
+ success: true,
+ message: '提醒删除成功'
+ });
+ } catch (error) {
+ console.error('删除提醒错误:', error);
+ res.status(500).json({
+ success: false,
+ message: '服务器错误,请稍后重试'
+ });
+ }
+});
+
+module.exports = router;
\ No newline at end of file
diff --git a/server/server.js b/server/server.js
new file mode 100644
index 0000000..6ba2a24
--- /dev/null
+++ b/server/server.js
@@ -0,0 +1,57 @@
+require('dotenv').config();
+const express = require('express');
+const cors = require('cors');
+const connectDB = require('./config/db');
+
+const authRoutes = require('./routes/auth');
+const petRoutes = require('./routes/pets');
+const dailyRecordRoutes = require('./routes/dailyRecords');
+const reminderRoutes = require('./routes/reminders');
+const medicalRecordRoutes = require('./routes/medicalRecords');
+const postRoutes = require('./routes/posts');
+
+const app = express();
+
+connectDB();
+
+app.use(cors());
+app.use(express.json({ limit: '10mb' }));
+app.use(express.urlencoded({ extended: true, limit: '10mb' }));
+
+app.use('/api/auth', authRoutes);
+app.use('/api/pets', petRoutes);
+app.use('/api/daily-records', dailyRecordRoutes);
+app.use('/api/reminders', reminderRoutes);
+app.use('/api/medical-records', medicalRecordRoutes);
+app.use('/api/posts', postRoutes);
+
+app.get('/api/health', (req, res) => {
+ res.json({
+ success: true,
+ message: '宠物管理平台服务运行正常',
+ timestamp: new Date().toISOString()
+ });
+});
+
+app.use('*', (req, res) => {
+ res.status(404).json({
+ success: false,
+ message: '请求的资源不存在'
+ });
+});
+
+app.use((err, req, res, next) => {
+ console.error('服务器错误:', err);
+ res.status(500).json({
+ success: false,
+ message: '服务器内部错误,请稍后重试'
+ });
+});
+
+const PORT = process.env.PORT || 5000;
+
+app.listen(PORT, () => {
+ console.log(`服务器运行在端口 ${PORT} 上`);
+ console.log(`环境: ${process.env.NODE_ENV}`);
+ console.log(`API地址: http://localhost:${PORT}/api`);
+});
\ No newline at end of file