From d049c29f930d1a02656ef6dc9d27c3166ffceea5 Mon Sep 17 00:00:00 2001 From: Primakov Alexandr Alexandrovich Date: Tue, 23 Sep 2025 14:23:52 +0300 Subject: [PATCH] =?UTF-8?q?=D1=87=D0=B8=D1=81=D1=82=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 222 ++-- package.json | 19 +- server/index.ts | 10 - server/routers/back-new/.env | 2 - server/routers/back-new/.gitignore | 2 - server/routers/back-new/README.md | 21 - server/routers/back-new/app.js | 22 - server/routers/back-new/features.config.js | 5 - .../back-new/features/auth/auth.controller.js | 104 -- .../back-new/features/auth/auth.routes.js | 11 - .../features/image/image.controller.js | 157 --- .../back-new/features/image/image.routes.js | 7 - .../back-new/features/user/user.controller.js | 12 - .../back-new/features/user/user.routes.js | 7 - server/routers/back-new/middleware/auth.js | 11 - server/routers/back-new/package-lock.json | 1024 ----------------- server/routers/back-new/package.json | 17 - server/routers/back-new/server.js | 41 - server/routers/back-new/shared/hateoas.js | 8 - server/routers/back-new/shared/usersDb.js | 28 - server/routers/kfu-m-24-1/index.js | 1 - .../kfu-m-24-1/sber_mobile/DB_Scheme.txt | 231 ---- .../sber_mobile/additional_services.js | 53 - .../kfu-m-24-1/sber_mobile/apartments.js | 45 - server/routers/kfu-m-24-1/sber_mobile/auth.js | 51 - .../kfu-m-24-1/sber_mobile/buildings.js | 14 - .../routers/kfu-m-24-1/sber_mobile/cameras.js | 28 - .../chat-ai-agent/chat-moderation.ts | 78 -- .../sber_mobile/chat-ai-agent/gigachat.ts | 18 - .../chat-ai-agent/moderation-config.js | 16 - .../routers/kfu-m-24-1/sber_mobile/chats.js | 218 ---- .../kfu-m-24-1/sber_mobile/get-constants.js | 90 -- .../routers/kfu-m-24-1/sber_mobile/index.js | 41 - .../sber_mobile/initiatives-ai-agents/llm.ts | 22 - .../initiatives-ai-agents/moderation.ts | 56 - .../initiatives-ai-agents/picture.ts | 38 - .../kfu-m-24-1/sber_mobile/initiatives.js | 101 -- .../routers/kfu-m-24-1/sber_mobile/media.js | 15 - .../kfu-m-24-1/sber_mobile/messages.js | 235 ---- .../kfu-m-24-1/sber_mobile/moderate.js | 162 --- .../kfu-m-24-1/sber_mobile/moderation.js | 53 - .../kfu-m-24-1/sber_mobile/polling-chat.js | 982 ---------------- .../routers/kfu-m-24-1/sber_mobile/profile.js | 119 -- .../kfu-m-24-1/sber_mobile/supabaseClient.js | 79 -- .../support-ai-agent/create-ticket-tool.ts | 66 -- .../sber_mobile/support-ai-agent/gigachat.ts | 20 - .../support-ai-agent/knowledge-base-tool.ts | 41 - .../support-ai-agent/support-agent.ts | 167 --- .../support-ai-agent/support-context-tool.ts | 56 - .../support-ai-agent/vector-store.ts | 33 - .../kfu-m-24-1/sber_mobile/supportApi.js | 151 --- .../routers/kfu-m-24-1/sber_mobile/tickets.js | 31 - .../kfu-m-24-1/sber_mobile/user_apartments.js | 18 - .../sber_mobile/utility_payments.js | 50 - .../routers/kfu-m-24-1/sber_mobile/votes.js | 105 -- 55 files changed, 144 insertions(+), 5070 deletions(-) delete mode 100644 server/routers/back-new/.env delete mode 100644 server/routers/back-new/.gitignore delete mode 100644 server/routers/back-new/README.md delete mode 100644 server/routers/back-new/app.js delete mode 100644 server/routers/back-new/features.config.js delete mode 100644 server/routers/back-new/features/auth/auth.controller.js delete mode 100644 server/routers/back-new/features/auth/auth.routes.js delete mode 100644 server/routers/back-new/features/image/image.controller.js delete mode 100644 server/routers/back-new/features/image/image.routes.js delete mode 100644 server/routers/back-new/features/user/user.controller.js delete mode 100644 server/routers/back-new/features/user/user.routes.js delete mode 100644 server/routers/back-new/middleware/auth.js delete mode 100644 server/routers/back-new/package-lock.json delete mode 100644 server/routers/back-new/package.json delete mode 100644 server/routers/back-new/server.js delete mode 100644 server/routers/back-new/shared/hateoas.js delete mode 100644 server/routers/back-new/shared/usersDb.js delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/DB_Scheme.txt delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/additional_services.js delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/apartments.js delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/auth.js delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/buildings.js delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/cameras.js delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/chat-moderation.ts delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/gigachat.ts delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/moderation-config.js delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/chats.js delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/get-constants.js delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/index.js delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/initiatives-ai-agents/llm.ts delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/initiatives-ai-agents/moderation.ts delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/initiatives-ai-agents/picture.ts delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/initiatives.js delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/media.js delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/messages.js delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/moderate.js delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/moderation.js delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/polling-chat.js delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/profile.js delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/supabaseClient.js delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/create-ticket-tool.ts delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/gigachat.ts delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/knowledge-base-tool.ts delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-context-tool.ts delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/vector-store.ts delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/supportApi.js delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/tickets.js delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/user_apartments.js delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/utility_payments.js delete mode 100644 server/routers/kfu-m-24-1/sber_mobile/votes.js diff --git a/package-lock.json b/package-lock.json index 7313728..540a4cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,9 @@ "version": "2.0.0", "license": "MIT", "dependencies": { - "@langchain/community": "^0.3.41", - "@langchain/core": "^0.3.46", - "@langchain/langgraph": "^0.2.65", - "@supabase/supabase-js": "^2.49.4", + "@langchain/community": "^0.3.56", + "@langchain/core": "^0.3.77", + "@langchain/langgraph": "^0.4.9", "ai": "^4.1.13", "axios": "^1.7.7", "bcrypt": "^5.1.0", @@ -25,15 +24,15 @@ "express": "5.0.1", "express-jwt": "^8.5.1", "express-session": "^1.18.1", - "gigachat": "^0.0.14", + "gigachat": "^0.0.16", "jsdom": "^25.0.1", "jsonwebtoken": "^9.0.2", - "langchain": "^0.3.7", - "langchain-gigachat": "^0.0.11", - "mongodb": "^6.12.0", - "mongoose": "^8.9.2", + "langchain": "^0.3.34", + "langchain-gigachat": "^0.0.14", + "mongodb": "^6.20.0", + "mongoose": "^8.18.2", "mongoose-sequence": "^6.0.1", - "morgan": "^1.10.0", + "morgan": "^1.10.1", "multer": "^1.4.5-lts.1", "pbkdf2-password": "^1.2.1", "rotating-file-stream": "^3.2.5", @@ -1928,19 +1927,19 @@ } }, "node_modules/@langchain/community": { - "version": "0.3.46", - "resolved": "https://registry.npmjs.org/@langchain/community/-/community-0.3.46.tgz", - "integrity": "sha512-loix9LkoNcn1gQlVCopmrJW9TmgZb+YpZw7nkFzXT6ozR8ZDh1XlFq1ymR5gTFtdNzF0neK2oJtE9iEl1lm7Dw==", + "version": "0.3.56", + "resolved": "https://registry.npmjs.org/@langchain/community/-/community-0.3.56.tgz", + "integrity": "sha512-lDjUnRfHAX7aMXyEB2EWbe5qOmdQdz8n+0CNQ4ExpLy3NOFQhEVkWclhsucaX04zh0r/VH5Pkk9djpnhPBDH7g==", "license": "MIT", "dependencies": { - "@langchain/openai": ">=0.2.0 <0.6.0", + "@langchain/openai": ">=0.2.0 <0.7.0", "@langchain/weaviate": "^0.2.0", "binary-extensions": "^2.2.0", "expr-eval": "^2.0.2", "flat": "^5.0.2", "js-yaml": "^4.1.0", "langchain": ">=0.2.3 <0.3.0 || >=0.3.4 <0.4.0", - "langsmith": "^0.3.29", + "langsmith": "^0.3.67", "uuid": "^10.0.0", "zod": "^3.25.32" }, @@ -1975,10 +1974,10 @@ "@google-ai/generativelanguage": "*", "@google-cloud/storage": "^6.10.1 || ^7.7.0", "@gradientai/nodejs-sdk": "^1.2.0", - "@huggingface/inference": "^2.6.4", - "@huggingface/transformers": "^3.2.3", + "@huggingface/inference": "^4.0.5", + "@huggingface/transformers": "^3.5.2", "@ibm-cloud/watsonx-ai": "*", - "@lancedb/lancedb": "^0.12.0", + "@lancedb/lancedb": "^0.19.1", "@langchain/core": ">=0.3.58 <0.4.0", "@layerup/layerup-security": "^1.5.12", "@libsql/client": "^0.14.0", @@ -1991,7 +1990,7 @@ "@pinecone-database/pinecone": "*", "@planetscale/database": "^1.8.0", "@premai/prem-sdk": "^0.3.25", - "@qdrant/js-client-rest": "^1.8.2", + "@qdrant/js-client-rest": "^1.15.0", "@raycast/api": "^1.55.2", "@rockset/client": "^0.9.1", "@smithy/eventstream-codec": "^2.0.5", @@ -2027,11 +2026,10 @@ "crypto-js": "^4.2.0", "d3-dsv": "^2.0.0", "discord.js": "^14.14.1", - "dria": "^0.0.3", "duck-duck-scrape": "^2.2.5", "epub2": "^3.0.1", "fast-xml-parser": "*", - "firebase-admin": "^11.9.0 || ^12.0.0", + "firebase-admin": "^11.9.0 || ^12.0.0 || ^13.0.0", "google-auth-library": "*", "googleapis": "*", "hnswlib-node": "^3.0.0", @@ -2049,7 +2047,7 @@ "mammoth": "^1.6.0", "mariadb": "^3.4.0", "mem0ai": "^2.1.8", - "mongodb": ">=5.2.0", + "mongodb": "^6.17.0", "mysql2": "^3.9.8", "neo4j-driver": "*", "notion-to-md": "^3.1.0", @@ -2309,9 +2307,6 @@ "discord.js": { "optional": true }, - "dria": { - "optional": true - }, "duck-duck-scrape": { "optional": true }, @@ -2466,9 +2461,9 @@ } }, "node_modules/@langchain/core": { - "version": "0.3.58", - "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.3.58.tgz", - "integrity": "sha512-HLkOtVofgBHefaUae/+2fLNkpMLzEjHSavTmUF0YC7bDa5NPIZGlP80CGrSFXAeJ+WCPd8rIK8K/p6AW94inUQ==", + "version": "0.3.77", + "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.3.77.tgz", + "integrity": "sha512-aqXHea9xfpVn6VoCq9pjujwFqrh3vw3Fgm9KFUZJ1cF7Bx5HI62DvQPw8LlRB3NB4dhwBBA1ldAVkkkd1du8nA==", "license": "MIT", "dependencies": { "@cfworker/json-schema": "^4.0.2", @@ -2476,7 +2471,7 @@ "camelcase": "6", "decamelize": "1.2.0", "js-tiktoken": "^1.0.12", - "langsmith": "^0.3.29", + "langsmith": "^0.3.67", "mustache": "^4.2.0", "p-queue": "^6.6.2", "p-retry": "4", @@ -2526,21 +2521,21 @@ } }, "node_modules/@langchain/langgraph": { - "version": "0.2.74", - "resolved": "https://registry.npmjs.org/@langchain/langgraph/-/langgraph-0.2.74.tgz", - "integrity": "sha512-oHpEi5sTZTPaeZX1UnzfM2OAJ21QGQrwReTV6+QnX7h8nDCBzhtipAw1cK616S+X8zpcVOjgOtJuaJhXa4mN8w==", + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@langchain/langgraph/-/langgraph-0.4.9.tgz", + "integrity": "sha512-+rcdTGi4Ium4X/VtIX3Zw4RhxEkYWpwUyz806V6rffjHOAMamg6/WZDxpJbrP33RV/wJG1GH12Z29oX3Pqq3Aw==", "license": "MIT", "dependencies": { - "@langchain/langgraph-checkpoint": "~0.0.17", - "@langchain/langgraph-sdk": "~0.0.32", + "@langchain/langgraph-checkpoint": "^0.1.1", + "@langchain/langgraph-sdk": "~0.1.0", "uuid": "^10.0.0", - "zod": "^3.23.8" + "zod": "^3.25.32" }, "engines": { "node": ">=18" }, "peerDependencies": { - "@langchain/core": ">=0.2.36 <0.3.0 || >=0.3.40 < 0.4.0", + "@langchain/core": ">=0.3.58 < 0.4.0", "zod-to-json-schema": "^3.x" }, "peerDependenciesMeta": { @@ -2550,9 +2545,9 @@ } }, "node_modules/@langchain/langgraph-checkpoint": { - "version": "0.0.18", - "resolved": "https://registry.npmjs.org/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-0.0.18.tgz", - "integrity": "sha512-IS7zJj36VgY+4pf8ZjsVuUWef7oTwt1y9ylvwu0aLuOn1d0fg05Om9DLm3v2GZ2Df6bhLV1kfWAM0IAl9O5rQQ==", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-0.1.1.tgz", + "integrity": "sha512-h2bP0RUikQZu0Um1ZUPErQLXyhzroJqKRbRcxYRTAh49oNlsfeq4A3K4YEDRbGGuyPZI/Jiqwhks1wZwY73AZw==", "license": "MIT", "dependencies": { "uuid": "^10.0.0" @@ -2561,7 +2556,7 @@ "node": ">=18" }, "peerDependencies": { - "@langchain/core": ">=0.2.31 <0.4.0" + "@langchain/core": ">=0.2.31 <0.4.0 || ^1.0.0-alpha" } }, "node_modules/@langchain/langgraph-checkpoint/node_modules/uuid": { @@ -2578,9 +2573,9 @@ } }, "node_modules/@langchain/langgraph-sdk": { - "version": "0.0.84", - "resolved": "https://registry.npmjs.org/@langchain/langgraph-sdk/-/langgraph-sdk-0.0.84.tgz", - "integrity": "sha512-l0PFQyJ+6m6aclORNPPWlcRwgKcXVXsPaJCbCUYFABR3yf4cOpsjhUNR0cJ7+2cS400oieHjGRdGGyO/hbSjhg==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@langchain/langgraph-sdk/-/langgraph-sdk-0.1.6.tgz", + "integrity": "sha512-PeXxfo4ls8yql6YdW8qjnZgp1giy7oqJiGjy4j2OSJ7lpkir8n62YpvADDByEh9sPzGLJYh92ZUAh0GNfQ18vA==", "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.15", @@ -2589,8 +2584,9 @@ "uuid": "^9.0.0" }, "peerDependencies": { - "@langchain/core": ">=0.2.31 <0.4.0", - "react": "^18 || ^19" + "@langchain/core": ">=0.2.31 <0.4.0 || ^1.0.0-alpha", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" }, "peerDependenciesMeta": { "@langchain/core": { @@ -2598,6 +2594,9 @@ }, "react": { "optional": true + }, + "react-dom": { + "optional": true } } }, @@ -2737,9 +2736,9 @@ } }, "node_modules/@mongodb-js/saslprep": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz", - "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.0.tgz", + "integrity": "sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==", "license": "MIT", "dependencies": { "sparse-bitfield": "^3.0.3" @@ -2871,6 +2870,8 @@ "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.69.1.tgz", "integrity": "sha512-FILtt5WjCNzmReeRLq5wRs3iShwmnWgBvxHfqapC/VoljJl+W8hDAyFmf1NVw3zH+ZjZ05AKxiKxVeb0HNWRMQ==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@supabase/node-fetch": "^2.6.14" } @@ -2880,6 +2881,8 @@ "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.4.tgz", "integrity": "sha512-WL2p6r4AXNGwop7iwvul2BvOtuJ1YQy8EbOd0dhG1oN1q8el/BIRSFCFnWAMM/vJJlHWLi4ad22sKbKr9mvjoA==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@supabase/node-fetch": "^2.6.14" } @@ -2889,6 +2892,8 @@ "resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz", "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "whatwg-url": "^5.0.0" }, @@ -2901,6 +2906,8 @@ "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.19.4.tgz", "integrity": "sha512-O4soKqKtZIW3olqmbXXbKugUtByD2jPa8kL2m2c1oozAO11uCcGrRhkZL0kVxjBLrXHE0mdSkFsMj7jDSfyNpw==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@supabase/node-fetch": "^2.6.14" } @@ -2910,6 +2917,8 @@ "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.11.2.tgz", "integrity": "sha512-u/XeuL2Y0QEhXSoIPZZwR6wMXgB+RQbJzG9VErA3VghVt7uRfSVsjeqd7m5GhX3JR6dM/WRmLbVR8URpDWG4+w==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@supabase/node-fetch": "^2.6.14", "@types/phoenix": "^1.5.4", @@ -2922,6 +2931,8 @@ "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.7.1.tgz", "integrity": "sha512-asYHcyDR1fKqrMpytAS1zjyEfvxuOIp1CIXX7ji4lHHcJKqyk+sLl/Vxgm4sN6u8zvuUtae9e4kDxQP2qrwWBA==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@supabase/node-fetch": "^2.6.14" } @@ -2931,6 +2942,8 @@ "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.49.4.tgz", "integrity": "sha512-jUF0uRUmS8BKt37t01qaZ88H9yV1mbGYnqLeuFWLcdV+x1P4fl0yP9DGtaEhFPZcwSom7u16GkLEH9QJZOqOkw==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@supabase/auth-js": "2.69.1", "@supabase/functions-js": "2.4.4", @@ -3133,7 +3146,9 @@ "version": "1.6.6", "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz", "integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==", - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@types/retry": { "version": "0.12.0", @@ -3195,6 +3210,8 @@ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@types/node": "*" } @@ -3802,9 +3819,9 @@ } }, "node_modules/bson": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.2.tgz", - "integrity": "sha512-5afhLTjqDSA3akH56E+/2J6kTDuSIlBxyXPdQslj9hcIgOUE378xdOfZvC/9q3LifJNI6KR/juZ+d0NRNYBwXg==", + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", "license": "Apache-2.0", "engines": { "node": ">=16.20.1" @@ -5760,9 +5777,9 @@ } }, "node_modules/gigachat": { - "version": "0.0.14", - "resolved": "https://registry.npmjs.org/gigachat/-/gigachat-0.0.14.tgz", - "integrity": "sha512-BwXDecDxF6aKJT+juuoATrBnFLDBg5Vho1dxYRsgM18zgZ55q5SwNiOgC05/J7rhGY66Pj6Wsnvk3FC6K4IMQw==", + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/gigachat/-/gigachat-0.0.16.tgz", + "integrity": "sha512-37MFTFltKGDE1EDW6y87BxzU5orIU3fpLDqAMHCNdV8JUL2oNbHMe6CACWWqUh7HLaztwkysRP8nJxBYBms1gg==", "license": "ISC", "dependencies": { "axios": "^1.8.2", @@ -7389,17 +7406,17 @@ } }, "node_modules/langchain": { - "version": "0.3.28", - "resolved": "https://registry.npmjs.org/langchain/-/langchain-0.3.28.tgz", - "integrity": "sha512-h4GGlBJNGU/Sj2PipW9kL+ewj7To3c+SnnNKH3HZaVHEqGPMHVB96T1lLjtCLcZCyUfabMr/zFIkLNI4War+Xg==", + "version": "0.3.34", + "resolved": "https://registry.npmjs.org/langchain/-/langchain-0.3.34.tgz", + "integrity": "sha512-OADHLQYRX+36EqQBxIoryCdMKfHex32cJBSWveadIIeRhygqivacIIDNwVjX51Y++c80JIdR0jaQHWn2r3H1iA==", "license": "MIT", "dependencies": { - "@langchain/openai": ">=0.1.0 <0.6.0", + "@langchain/openai": ">=0.1.0 <0.7.0", "@langchain/textsplitters": ">=0.0.0 <0.2.0", "js-tiktoken": "^1.0.12", "js-yaml": "^4.1.0", "jsonpointer": "^5.0.1", - "langsmith": "^0.3.29", + "langsmith": "^0.3.67", "openapi-types": "^12.1.3", "p-retry": "4", "uuid": "^10.0.0", @@ -7484,12 +7501,12 @@ } }, "node_modules/langchain-gigachat": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/langchain-gigachat/-/langchain-gigachat-0.0.11.tgz", - "integrity": "sha512-2hYES1Dt0U/p/h+F+/1lDfmaYTWQyuHG5KAAIQGYygursAUGDDoyKQlGywbJ4JgmENy4u5fv7keVC9+k0X8tbQ==", + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/langchain-gigachat/-/langchain-gigachat-0.0.14.tgz", + "integrity": "sha512-8jnHMZI1QqAs98iTdldouT1chiFRTtEnxXHFiQl8th7u/B6Eot0OJfMT5iviCFO6/pMNxYgq0Fzzr29ndaJyEQ==", "license": "MIT", "dependencies": { - "gigachat": "^0.0.14", + "gigachat": "^0.0.15", "uuid": "^11.0.5", "zod": "^3.23.8", "zod-to-json-schema": "^3.23.5" @@ -7501,6 +7518,16 @@ "@langchain/core": ">=0.2.21 <0.4.0" } }, + "node_modules/langchain-gigachat/node_modules/gigachat": { + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/gigachat/-/gigachat-0.0.15.tgz", + "integrity": "sha512-4hAf/obnzwW4xp+AOP6Zv81F3Dr9QcsEjVOGTdY4aRWphzgV8YVZ134huqQfA/LQCuoD9UMmlt3nfix6exgjYg==", + "license": "ISC", + "dependencies": { + "axios": "^1.8.2", + "uuid": "^11.0.3" + } + }, "node_modules/langchain/node_modules/uuid": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", @@ -7515,9 +7542,9 @@ } }, "node_modules/langsmith": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.3.31.tgz", - "integrity": "sha512-9lwuLZuN3tXFYQ6eMg0rmbBw7oxQo4bu1NYeylbjz27bOdG1XB9XNoxaiIArkK4ciLdOIOhPMBXP4bkvZOgHRw==", + "version": "0.3.69", + "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.3.69.tgz", + "integrity": "sha512-YKzu92YAP2o+d+1VmR38xqFX0RIRLKYj1IqdflVEY83X0FoiVlrWO3xDLXgnu7vhZ2N2M6jx8VO9fVF8yy9gHA==", "license": "MIT", "dependencies": { "@types/uuid": "^10.0.0", @@ -7529,9 +7556,21 @@ "uuid": "^10.0.0" }, "peerDependencies": { + "@opentelemetry/api": "*", + "@opentelemetry/exporter-trace-otlp-proto": "*", + "@opentelemetry/sdk-trace-base": "*", "openai": "*" }, "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@opentelemetry/exporter-trace-otlp-proto": { + "optional": true + }, + "@opentelemetry/sdk-trace-base": { + "optional": true + }, "openai": { "optional": true } @@ -7862,14 +7901,14 @@ } }, "node_modules/mongodb": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.13.0.tgz", - "integrity": "sha512-KeESYR5TEaFxOuwRqkOm3XOsMqCSkdeDMjaW5u2nuKfX7rqaofp7JQGoi7sVqQcNJTKuveNbzZtWMstb8ABP6Q==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz", + "integrity": "sha512-Tl6MEIU3K4Rq3TSHd+sZQqRBoGlFsOgNrH5ltAcFBV62Re3Fd+FcaVf8uSEQFOJ51SDowDVttBTONMfoYWrWlQ==", "license": "Apache-2.0", "dependencies": { - "@mongodb-js/saslprep": "^1.1.9", - "bson": "^6.10.1", - "mongodb-connection-string-url": "^3.0.0" + "@mongodb-js/saslprep": "^1.3.0", + "bson": "^6.10.4", + "mongodb-connection-string-url": "^3.0.2" }, "engines": { "node": ">=16.20.1" @@ -7880,7 +7919,7 @@ "gcp-metadata": "^5.2.0", "kerberos": "^2.0.1", "mongodb-client-encryption": ">=6.0.0 <7", - "snappy": "^7.2.2", + "snappy": "^7.3.2", "socks": "^2.7.1" }, "peerDependenciesMeta": { @@ -7952,14 +7991,14 @@ } }, "node_modules/mongoose": { - "version": "8.9.5", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.9.5.tgz", - "integrity": "sha512-SPhOrgBm0nKV3b+IIHGqpUTOmgVL5Z3OO9AwkFEmvOZznXTvplbomstCnPOGAyungtRXE5pJTgKpKcZTdjeESg==", + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.18.2.tgz", + "integrity": "sha512-gA6GFlshOHUdNyw9OQTmMLSGzVOPbcbjaSZ1dvR5iMp668N2UUznTuzgTY6V6Q41VtBc4kmL/qqML1RNgXB5Fg==", "license": "MIT", "dependencies": { - "bson": "^6.10.1", + "bson": "^6.10.4", "kareem": "2.6.3", - "mongodb": "~6.12.0", + "mongodb": "~6.18.0", "mpath": "0.9.0", "mquery": "5.0.0", "ms": "2.1.3", @@ -7987,13 +8026,13 @@ } }, "node_modules/mongoose/node_modules/mongodb": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.12.0.tgz", - "integrity": "sha512-RM7AHlvYfS7jv7+BXund/kR64DryVI+cHbVAy9P61fnb1RcWZqOW1/Wj2YhqMCx+MuYhqTRGv7AwHBzmsCKBfA==", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.18.0.tgz", + "integrity": "sha512-fO5ttN9VC8P0F5fqtQmclAkgXZxbIkYRTUi1j8JO6IYwvamkhtYDilJr35jOPELR49zqCJgXZWwCtW7B+TM8vQ==", "license": "Apache-2.0", "dependencies": { "@mongodb-js/saslprep": "^1.1.9", - "bson": "^6.10.1", + "bson": "^6.10.4", "mongodb-connection-string-url": "^3.0.0" }, "engines": { @@ -8039,16 +8078,16 @@ "license": "MIT" }, "node_modules/morgan": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", - "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz", + "integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==", "license": "MIT", "dependencies": { "basic-auth": "~2.0.1", "debug": "2.6.9", "depd": "~2.0.0", "on-finished": "~2.3.0", - "on-headers": "~1.0.2" + "on-headers": "~1.1.0" }, "engines": { "node": ">= 0.8.0" @@ -8066,6 +8105,15 @@ "node": ">= 0.8" } }, + "node_modules/morgan/node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/mpath": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", diff --git a/package.json b/package.json index a43b5aa..948f3ea 100644 --- a/package.json +++ b/package.json @@ -21,10 +21,9 @@ "license": "MIT", "homepage": "https://bitbucket.org/online-mentor/multi-stub#readme", "dependencies": { - "@supabase/supabase-js": "^2.49.4", - "@langchain/community": "^0.3.41", - "@langchain/core": "^0.3.46", - "@langchain/langgraph": "^0.2.65", + "@langchain/community": "^0.3.56", + "@langchain/core": "^0.3.77", + "@langchain/langgraph": "^0.4.9", "ai": "^4.1.13", "axios": "^1.7.7", "bcrypt": "^5.1.0", @@ -37,15 +36,15 @@ "express": "5.0.1", "express-jwt": "^8.5.1", "express-session": "^1.18.1", - "gigachat": "^0.0.14", + "gigachat": "^0.0.16", "jsdom": "^25.0.1", "jsonwebtoken": "^9.0.2", - "langchain": "^0.3.7", - "langchain-gigachat": "^0.0.11", - "mongodb": "^6.12.0", - "mongoose": "^8.9.2", + "langchain": "^0.3.34", + "langchain-gigachat": "^0.0.14", + "mongodb": "^6.20.0", + "mongoose": "^8.18.2", "mongoose-sequence": "^6.0.1", - "morgan": "^1.10.0", + "morgan": "^1.10.1", "multer": "^1.4.5-lts.1", "pbkdf2-password": "^1.2.1", "rotating-file-stream": "^3.2.5", diff --git a/server/index.ts b/server/index.ts index 40861ae..fe23c60 100644 --- a/server/index.ts +++ b/server/index.ts @@ -20,9 +20,7 @@ import gamehubRouter from './routers/gamehub' import escRouter from './routers/esc' import connectmeRouter from './routers/connectme' import questioneerRouter from './routers/questioneer' -import backNewRouter from './routers/back-new/app' import { setIo } from './io' -const { createChatPollingRouter } = require('./routers/kfu-m-24-1/sber_mobile/polling-chat') export const app = express() @@ -90,18 +88,11 @@ const initServer = async () => { ) app.use(root) - // Инициализация Polling для чата (после настройки middleware) - const { router: chatPollingRouter, chatHandler } = createChatPollingRouter(express) - /** * Добавляйте сюда свои routers. */ app.use("/kfu-m-24-1", kfuM241Router) - - // Добавляем Polling роутер для чата - app.use("/kfu-m-24-1/sber_mobile", chatPollingRouter) - app.use("/epja-2024-1", epja20241Router) app.use("/v1/todo", todoRouter) app.use("/dogsitters-finder", dogsittersFinderRouter) @@ -114,7 +105,6 @@ const initServer = async () => { app.use("/esc", escRouter) app.use('/connectme', connectmeRouter) app.use('/questioneer', questioneerRouter) - app.use('/back-new', backNewRouter) app.use(errorHandler) diff --git a/server/routers/back-new/.env b/server/routers/back-new/.env deleted file mode 100644 index 2261982..0000000 --- a/server/routers/back-new/.env +++ /dev/null @@ -1,2 +0,0 @@ -GIGACHAT_API_KEY=78359123-4447-481a-9028-861f53b24ed1:04a4f1e9-1349-4a84-85f9-0c6c687c0974 -GIGACHAT_SCOPE=GIGACHAT_API_PERS \ No newline at end of file diff --git a/server/routers/back-new/.gitignore b/server/routers/back-new/.gitignore deleted file mode 100644 index 9439df7..0000000 --- a/server/routers/back-new/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules/ -.env \ No newline at end of file diff --git a/server/routers/back-new/README.md b/server/routers/back-new/README.md deleted file mode 100644 index 97811d1..0000000 --- a/server/routers/back-new/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# back-new - -非Python实现的后端(Node.js + Express) - -## 启动方法 - -1. 安装依赖: - ```bash - npm install - ``` -2. 启动服务: - ```bash - npm start - ``` - -默认端口:`3002` - -## 支持接口 -- POST `/api/auth/login` 用户登录 -- POST `/api/auth/register` 用户注册 -- GET `/gigachat/prompt?prompt=xxx` 生成图片(返回模拟图片链接) \ No newline at end of file diff --git a/server/routers/back-new/app.js b/server/routers/back-new/app.js deleted file mode 100644 index d9743a8..0000000 --- a/server/routers/back-new/app.js +++ /dev/null @@ -1,22 +0,0 @@ -const express = require('express'); -const featuresConfig = require('./features.config'); -const imageRoutes = require('./features/image/image.routes'); - -const router = express.Router(); - -// 动态加载路由 -if (featuresConfig.auth) { - router.use('/auth', require('./features/auth/auth.routes')); -} -if (featuresConfig.user) { - router.use('/user', require('./features/user/user.routes')); -} -if (featuresConfig.image) { - router.use('/image', imageRoutes); -} - -router.get('/', (req, res) => { - res.json({ message: 'API root' }); -}); - -module.exports = router; \ No newline at end of file diff --git a/server/routers/back-new/features.config.js b/server/routers/back-new/features.config.js deleted file mode 100644 index 7a69e24..0000000 --- a/server/routers/back-new/features.config.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - auth: true, - user: true, - image: true, // 关闭为 false -}; \ No newline at end of file diff --git a/server/routers/back-new/features/auth/auth.controller.js b/server/routers/back-new/features/auth/auth.controller.js deleted file mode 100644 index 8f57b3a..0000000 --- a/server/routers/back-new/features/auth/auth.controller.js +++ /dev/null @@ -1,104 +0,0 @@ -const usersDb = require('../../shared/usersDb'); -const makeLinks = require('../../shared/hateoas'); - -exports.login = (req, res) => { - const { username, password, email } = req.body; - const user = usersDb.findUser(username, email, password); - if (user) { - res.json({ - data: { - user: { - id: user.id, - username: user.username, - email: user.email, - firstName: user.firstName, - lastName: user.lastName - }, - token: 'token-' + user.id, - message: 'Login successful' - }, - _links: makeLinks('/api/auth', { - self: '/login', - profile: '/profile/', - logout: '/logout' - }), - _meta: {} - }); - } else { - res.status(401).json({ error: 'Invalid credentials' }); - } -}; - -exports.register = (req, res) => { - const { username, password, email, firstName, lastName } = req.body; - if (usersDb.exists(username, email)) { - return res.status(409).json({ error: 'User already exists' }); - } - const newUser = usersDb.addUser({ username, password, email, firstName, lastName }); - res.json({ - data: { - user: { - id: newUser.id, - username, - email, - firstName, - lastName - }, - token: 'token-' + newUser.id, - message: 'Register successful' - }, - _links: makeLinks('/api/auth', { - self: '/register', - login: '/login', - profile: '/profile/' - }), - _meta: {} - }); -}; - -exports.profile = (req, res) => { - const auth = req.headers.authorization; - if (!auth || !auth.startsWith('Bearer ')) { - return res.status(401).json({ error: 'No token provided' }); - } - const token = auth.replace('Bearer ', ''); - const id = parseInt(token.replace('token-', '')); - const user = usersDb.findById(id); - if (!user) { - return res.status(401).json({ error: 'Invalid token' }); - } - res.json({ - data: { - id: user.id, - username: user.username, - email: user.email, - firstName: user.firstName, - lastName: user.lastName - }, - _links: makeLinks('/api/auth', { - self: '/profile/', - logout: '/logout' - }), - _meta: {} - }); -}; - -exports.logout = (req, res) => { - res.json({ - message: 'Logout successful', - _links: makeLinks('/api/auth', { - self: '/logout', - login: '/login' - }), - _meta: {} - }); -}; - -exports.updateProfile = (req, res) => { - const userId = req.user?.id || req.body.id; // 这里假设有用户认证中间件,否则用body.id - if (!userId) return res.status(401).json({ error: 'Unauthorized' }); - const { firstName, lastName, bio, location, website, email, username, password } = req.body; - const updated = require('../../shared/usersDb').updateUser(userId, { firstName, lastName, bio, location, website, email, username, password }); - if (!updated) return res.status(404).json({ error: 'User not found' }); - res.json({ success: true, user: updated }); -}; \ No newline at end of file diff --git a/server/routers/back-new/features/auth/auth.routes.js b/server/routers/back-new/features/auth/auth.routes.js deleted file mode 100644 index 0262123..0000000 --- a/server/routers/back-new/features/auth/auth.routes.js +++ /dev/null @@ -1,11 +0,0 @@ -const express = require('express'); -const router = express.Router(); -const ctrl = require('./auth.controller'); - -router.post('/login', ctrl.login); -router.post('/register', ctrl.register); -router.get('/profile/', ctrl.profile); -router.post('/logout', ctrl.logout); -router.put('/profile/', ctrl.updateProfile); - -module.exports = router; \ No newline at end of file diff --git a/server/routers/back-new/features/image/image.controller.js b/server/routers/back-new/features/image/image.controller.js deleted file mode 100644 index ada3233..0000000 --- a/server/routers/back-new/features/image/image.controller.js +++ /dev/null @@ -1,157 +0,0 @@ -const axios = require('axios'); -const makeLinks = require('../../shared/hateoas'); -const { v4: uuidv4 } = require('uuid'); -const qs = require('qs'); -require('dotenv').config(); -process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; - -// 获取GigaChat access_token,严格按官方文档 -async function getGigaChatToken() { - const apiKey = '78359123-4447-481a-9028-861f53b24ed1:04a4f1e9-1349-4a84-85f9-0c6c687c0974'; - const scope = process.env.GIGACHAT_SCOPE || 'GIGACHAT_API_PERS'; - if (!apiKey) throw new Error('GIGACHAT_API_KEY 未配置'); - - const rqUID = uuidv4(); - const auth = Buffer.from(apiKey.trim()).toString('base64'); - - try { - const resp = await axios.post( - 'https://ngw.devices.sberbank.ru:9443/api/v2/oauth', - new URLSearchParams({ scope }), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Accept': 'application/json', - 'RqUID': rqUID, - 'Authorization': `Basic ${auth}`, - }, - timeout: 10000, - } - ); - if (!resp.data.access_token) { - console.error('GigaChat token响应异常:', resp.data); - throw new Error('GigaChat token响应异常'); - } - return resp.data.access_token; - } catch (err) { - if (err.response) { - console.error('获取access_token失败:', err.response.status, err.response.data); - } else { - console.error('获取access_token异常:', err.message); - } - throw new Error('获取access_token失败'); - } -} - -// 调用chat生成图片描述 -async function fetchChatContent(accessToken, prompt) { - try { - const chatResp = await axios.post( - 'https://gigachat.devices.sberbank.ru/api/v1/chat/completions', - { - model: "GigaChat", - messages: [ - { role: "system", content: "Ты — Василий Кандинский" }, - { role: "user", content: prompt } - ], - stream: false, - function_call: 'auto' - }, - { - headers: { - 'Authorization': `Bearer ${accessToken}`, - 'Content-Type': 'application/json', - 'RqUID': uuidv4(), - } - } - ); - const content = chatResp.data.choices[0].message.content; - console.log('GigaChat返回内容:', content); - return content; - } catch (err) { - console.error('AI生成图片出错: chat接口失败'); - if (err.response) { - console.error('status:', err.response.status); - console.error('headers:', err.response.headers); - console.error('data:', err.response.data); - console.error('config:', err.config); - } else { - console.error('AI生成图片出错:', err.message); - } - throw new Error('chat接口失败: ' + err.message); - } -} - -// 获取图片内容 -async function fetchImageContent(accessToken, imageId) { - try { - const imageResp = await axios.get( - `https://gigachat.devices.sberbank.ru/api/v1/files/${imageId}/content`, - { - headers: { - 'Authorization': `Bearer ${accessToken}`, - 'RqUID': uuidv4(), - }, - responseType: 'arraybuffer' - } - ); - return imageResp.data; - } catch (err) { - console.error('AI生成图片出错: 获取图片内容失败'); - if (err.response) { - console.error('status:', err.response.status); - console.error('headers:', err.response.headers); - console.error('data:', err.response.data); - console.error('config:', err.config); - } else { - console.error('AI生成图片出错:', err.message); - } - throw new Error('获取图片内容失败: ' + err.message); - } -} - -// 工具函数:异步重试 -async function retryAsync(fn, times = 3, delay = 800) { - let lastErr; - for (let i = 0; i < times; i++) { - try { - return await fn(); - } catch (err) { - lastErr = err; - if (i < times - 1 && delay) await new Promise(r => setTimeout(r, delay)); - } - } - throw lastErr; -} - -exports.generate = async (req, res) => { - const { prompt } = req.query; - if (!prompt) { - return res.status(400).json({ error: 'Prompt parameter is required' }); - } - let accessToken; - try { - accessToken = await getGigaChatToken(); - } catch (e) { - return res.status(500).json({ error: e.message }); - } - try { - // 1. 重试获取图片描述内容 - const content = await retryAsync(() => fetchChatContent(accessToken, prompt), 3, 800); - // 升级正则,兼容更多图片标签格式 - const match = content.match(/]+src=['"]([^'"]+)['"]/); - if (!match) { - console.error('AI生成图片出错: GigaChat未返回图片标签'); - return res.status(500).json({ error: 'No image generated' }); - } - const imageId = match[1]; - // 2. 重试获取图片内容 - const imageData = await retryAsync(() => fetchImageContent(accessToken, imageId), 3, 800); - res.set('Content-Type', 'image/jpeg'); - res.set('X-HATEOAS', JSON.stringify(makeLinks('/gigachat', { self: '/prompt' }))); - res.send(imageData); - } catch (err) { - console.error('AI生成图片出错: 未知错误', err); - res.status(500).json({ error: err.message }); - } -}; \ No newline at end of file diff --git a/server/routers/back-new/features/image/image.routes.js b/server/routers/back-new/features/image/image.routes.js deleted file mode 100644 index 7dbebf8..0000000 --- a/server/routers/back-new/features/image/image.routes.js +++ /dev/null @@ -1,7 +0,0 @@ -const express = require('express'); -const router = express.Router(); -const ctrl = require('./image.controller'); - -router.get('/prompt', ctrl.generate); - -module.exports = router; \ No newline at end of file diff --git a/server/routers/back-new/features/user/user.controller.js b/server/routers/back-new/features/user/user.controller.js deleted file mode 100644 index 313ff47..0000000 --- a/server/routers/back-new/features/user/user.controller.js +++ /dev/null @@ -1,12 +0,0 @@ -const usersDb = require('../../shared/usersDb'); -const makeLinks = require('../../shared/hateoas'); - -exports.list = (req, res) => { - res.json({ - data: usersDb.getAll(), - _links: makeLinks('/api/user', { - self: '/list', - }), - _meta: {} - }); -}; \ No newline at end of file diff --git a/server/routers/back-new/features/user/user.routes.js b/server/routers/back-new/features/user/user.routes.js deleted file mode 100644 index f444cad..0000000 --- a/server/routers/back-new/features/user/user.routes.js +++ /dev/null @@ -1,7 +0,0 @@ -const express = require('express'); -const router = express.Router(); -const ctrl = require('./user.controller'); - -router.get('/list', ctrl.list); - -module.exports = router; \ No newline at end of file diff --git a/server/routers/back-new/middleware/auth.js b/server/routers/back-new/middleware/auth.js deleted file mode 100644 index d1cc427..0000000 --- a/server/routers/back-new/middleware/auth.js +++ /dev/null @@ -1,11 +0,0 @@ -// 简单token认证中间件,支持token-3格式 -module.exports = function (req, res, next) { - const auth = req.headers.authorization; - if (auth && auth.startsWith('Bearer token-')) { - const id = parseInt(auth.replace('Bearer token-', '')); - if (!isNaN(id)) { - req.user = { id }; - } - } - next(); -}; \ No newline at end of file diff --git a/server/routers/back-new/package-lock.json b/server/routers/back-new/package-lock.json deleted file mode 100644 index c4d644d..0000000 --- a/server/routers/back-new/package-lock.json +++ /dev/null @@ -1,1024 +0,0 @@ -{ - "name": "back-new", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "back-new", - "version": "1.0.0", - "dependencies": { - "axios": "^1.10.0", - "cors": "^2.8.5", - "dotenv": "^17.0.0", - "express": "^4.21.2", - "qs": "^6.14.0", - "uuid": "^11.1.0" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmmirror.com/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/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/axios": { - "version": "1.10.0", - "resolved": "https://registry.npmmirror.com/axios/-/axios-1.10.0.tgz", - "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "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.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "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.13.0", - "resolved": "https://registry.npmmirror.com/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmmirror.com/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/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.1", - "resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "license": "MIT" - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmmirror.com/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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": "17.0.0", - "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-17.0.0.tgz", - "integrity": "sha512-A0BJ5lrpJVSfnMMXjmeO0xUnoxqsBHWCoqqTnGwGYVdnctqXXUEhJOO7LxmgxJon9tEZFGpe0xPRX0h2v3AANQ==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/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/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmmirror.com/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "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.13.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/node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmmirror.com/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmmirror.com/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "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.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "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.3", - "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.3.tgz", - "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", - "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/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.2", - "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmmirror.com/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/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.0.0", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.12", - "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "license": "MIT" - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmmirror.com/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/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmmirror.com/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "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.npmmirror.com/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.2", - "resolved": "https://registry.npmmirror.com/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmmirror.com/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/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.0", - "resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmmirror.com/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/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmmirror.com/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - } - } -} diff --git a/server/routers/back-new/package.json b/server/routers/back-new/package.json deleted file mode 100644 index 8663f06..0000000 --- a/server/routers/back-new/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "back-new", - "version": "1.0.0", - "description": "非Python实现的后端,兼容前端接口", - "main": "server.js", - "scripts": { - "start": "node server.js" - }, - "dependencies": { - "axios": "^1.10.0", - "cors": "^2.8.5", - "dotenv": "^17.0.0", - "express": "^4.21.2", - "qs": "^6.14.0", - "uuid": "^11.1.0" - } -} diff --git a/server/routers/back-new/server.js b/server/routers/back-new/server.js deleted file mode 100644 index 977f24d..0000000 --- a/server/routers/back-new/server.js +++ /dev/null @@ -1,41 +0,0 @@ -process.env.GIGACHAT_API_KEY = '78359123-4447-481a-9028-861f53b24ed1:04a4f1e9-1349-4a84-85f9-0c6c687c0974'; -process.env.GIGACHAT_SCOPE = 'GIGACHAT_API_PERS'; -require('dotenv').config(); -console.log('GIGACHAT_API_KEY:', process.env.GIGACHAT_API_KEY); -const express = require('express'); -const cors = require('cors'); - -const app = express(); -const router = require('./app'); - -app.use(cors()); -app.use(express.json());process.env.GIGACHAT_API_KEY = '78359123-4447-481a-9028-861f53b24ed1:04a4f1e9-1349-4a84-85f9-0c6c687c0974'; -process.env.GIGACHAT_SCOPE = 'GIGACHAT_API_PERS'; -require('dotenv').config(); -console.log('GIGACHAT_API_KEY:', process.env.GIGACHAT_API_KEY); -const express = require('express'); -const cors = require('cors'); -const authMiddleware = require('./middleware/auth'); - -const app = express(); -const router = require('./app'); - -app.use(cors()); -app.use(express.json()); -app.use(authMiddleware); - -// 路由前缀要和前端请求一致 -app.use('/ms/back-new', router); - -const PORT = process.env.PORT || 3000; -app.listen(PORT, () => { - console.log(`Server running on port ${PORT}`); -}); - -// 路由前缀要和前端请求一致 -app.use('/ms/back-new', router); - -const PORT = process.env.PORT || 3000; -app.listen(PORT, () => { - console.log(`Server running on port ${PORT}`); -}); \ No newline at end of file diff --git a/server/routers/back-new/shared/hateoas.js b/server/routers/back-new/shared/hateoas.js deleted file mode 100644 index b12ec23..0000000 --- a/server/routers/back-new/shared/hateoas.js +++ /dev/null @@ -1,8 +0,0 @@ -function makeLinks(base, links) { - const result = {}; - for (const [rel, path] of Object.entries(links)) { - result[rel] = { href: base + path }; - } - return result; -} -module.exports = makeLinks; \ No newline at end of file diff --git a/server/routers/back-new/shared/usersDb.js b/server/routers/back-new/shared/usersDb.js deleted file mode 100644 index fcc21db..0000000 --- a/server/routers/back-new/shared/usersDb.js +++ /dev/null @@ -1,28 +0,0 @@ -let users = [ - { id: 1, username: 'test', password: '123456', email: 'test@example.com', firstName: 'Test', lastName: 'User', bio: '', location: '', website: '' } -]; -let nextId = 2; - -exports.findUser = (username, email, password) => - users.find(u => (u.username === username || u.email === email) && u.password === password); - -exports.findById = (id) => users.find(u => u.id === id); - -exports.addUser = ({ username, password, email, firstName, lastName, bio = '', location = '', website = '' }) => { - const newUser = { id: nextId++, username, password, email, firstName, lastName, bio, location, website }; - users.push(newUser); - return newUser; -}; - -exports.exists = (username, email) => - users.some(u => u.username === username || u.email === email); - -exports.getAll = () => users; - -// 新增:更新用户信息 -exports.updateUser = (id, update) => { - const user = users.find(u => u.id === id); - if (!user) return null; - Object.assign(user, update); - return user; -}; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/index.js b/server/routers/kfu-m-24-1/index.js index c65cfc8..609da3e 100644 --- a/server/routers/kfu-m-24-1/index.js +++ b/server/routers/kfu-m-24-1/index.js @@ -4,7 +4,6 @@ const router = Router() router.use('/eng-it-lean', require('./eng-it-lean/index')) router.use('/sberhubproject', require('./sberhubproject/index')) router.use('/sber_web', require('./sber_web/index')) -router.use('/sber_mobile', require('./sber_mobile/index')) module.exports = router diff --git a/server/routers/kfu-m-24-1/sber_mobile/DB_Scheme.txt b/server/routers/kfu-m-24-1/sber_mobile/DB_Scheme.txt deleted file mode 100644 index 367f42a..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/DB_Scheme.txt +++ /dev/null @@ -1,231 +0,0 @@ --- Расширение для генерации UUID -CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; - --- 1. Управляющие компании -CREATE TABLE management_companies ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - name TEXT NOT NULL, - logo_url TEXT, - contact_phone TEXT NOT NULL, - email TEXT, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW() -); - --- 2. Жилые дома -CREATE TABLE buildings ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - management_company_id UUID NOT NULL REFERENCES management_companies(id), - name TEXT, - address TEXT NOT NULL, - floors INTEGER, - entrances INTEGER, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW() -); - --- 3. Профили пользователей -CREATE TABLE user_profiles ( - id UUID PRIMARY KEY REFERENCES auth.users(id), - full_name TEXT, - avatar_url TEXT, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW() -); - --- 4. Квартиры -CREATE TABLE apartments ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - building_id UUID NOT NULL REFERENCES buildings(id), - number TEXT NOT NULL, - area DECIMAL(10, 2), - floor INTEGER, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW() -); - --- 5. Связь пользователей с квартирами -CREATE TABLE apartment_residents ( - apartment_id UUID NOT NULL REFERENCES apartments(id), - user_id UUID NOT NULL REFERENCES auth.users(id), - is_owner BOOLEAN DEFAULT FALSE, - created_at TIMESTAMPTZ DEFAULT NOW(), - PRIMARY KEY (apartment_id, user_id) -); - --- 6. Сервисы УК -CREATE TABLE management_services ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - management_company_id UUID NOT NULL REFERENCES management_companies(id), - title TEXT NOT NULL, - description TEXT, - category TEXT NOT NULL, - base_price DECIMAL(10, 2), - image_url TEXT, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW() -); - --- 7. Связь сервисов УК с домами -CREATE TABLE building_management_services ( - building_id UUID NOT NULL REFERENCES buildings(id), - service_id UUID NOT NULL REFERENCES management_services(id), - custom_price DECIMAL(10, 2), - is_active BOOLEAN DEFAULT TRUE, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW(), - PRIMARY KEY (building_id, service_id) -); - --- 9. Дополнительные сервисы -CREATE TABLE additional_services ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - title TEXT NOT NULL, - description TEXT, - category TEXT NOT NULL, - price DECIMAL(10, 2), - image_url TEXT, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW() -); - --- 10. Инициативы -CREATE TABLE initiatives ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - building_id UUID NOT NULL REFERENCES buildings(id), - creator_id UUID NOT NULL REFERENCES auth.users(id), - title TEXT NOT NULL, - description TEXT NOT NULL, - status TEXT NOT NULL CHECK ( - status IN ('moderation', 'review', 'fundraising', 'approved', 'rejected') - ), - target_amount DECIMAL(10, 2), - current_amount DECIMAL(10, 2) DEFAULT 0, - image_url TEXT, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW() -); - --- 11. Голосования -CREATE TABLE votes ( - initiative_id UUID NOT NULL REFERENCES initiatives(id), - user_id UUID NOT NULL REFERENCES auth.users(id), - vote_type TEXT NOT NULL CHECK (vote_type IN ('for', 'against')), - created_at TIMESTAMPTZ DEFAULT NOW(), - PRIMARY KEY (initiative_id, user_id) -); - --- 12. Чат -CREATE TABLE chats ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - building_id UUID NOT NULL REFERENCES buildings(id), - name TEXT, - created_at TIMESTAMPTZ DEFAULT NOW() -); - --- 13. Сообщения -CREATE TABLE messages ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - chat_id UUID NOT NULL REFERENCES chats(id), - user_id UUID NOT NULL REFERENCES auth.users(id), - text TEXT NOT NULL, - created_at TIMESTAMPTZ DEFAULT NOW() -); - --- 14. Камеры -CREATE TABLE cameras ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - building_id UUID NOT NULL REFERENCES buildings(id), - location TEXT NOT NULL, - stream_url TEXT NOT NULL, - is_active BOOLEAN DEFAULT TRUE, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW() -); - --- 15. Платежки по квартире (ЖКХ, Интернет и т.д.) -CREATE TABLE payment_services ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - apartment_id UUID NOT NULL REFERENCES apartments(id), - name TEXT NOT NULL, -- Например, "ЖКХ", "Интернет" - icon TEXT, -- Можно хранить название иконки или url - amount DECIMAL(10, 2) NOT NULL, -- Общая сумма по платежке - is_paid BOOLEAN DEFAULT FALSE, -- Оплачен ли весь агрегатор - payment_method TEXT CHECK (payment_method IN ('card', 'sber')), - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW() -); - --- 16. Детализация по платежке (например, отопление, вода и т.д.) -CREATE TABLE payment_service_details ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - payment_service_id UUID NOT NULL REFERENCES payment_services(id) ON DELETE CASCADE, - name TEXT NOT NULL, -- Например, "Отопление" - amount DECIMAL(10, 2) NOT NULL, -- Сумма по детализации - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW() -); - --- 17. Заявки -CREATE TABLE tickets ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - user_id UUID NOT NULL REFERENCES auth.users(id), - apartment_id UUID NOT NULL REFERENCES apartments(id), - title TEXT NOT NULL, - description TEXT NOT NULL, - status TEXT NOT NULL CHECK (status IN ('open', 'in_progress', 'resolved')), - category TEXT NOT NULL, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW() -); - --- 18. Сообщения в службу поддержки -CREATE TABLE support ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - user_id UUID NOT NULL REFERENCES auth.users(id), - message TEXT NOT NULL, - is_from_user BOOLEAN NOT NULL, - created_at TIMESTAMPTZ DEFAULT NOW() -); - --- Индексы -CREATE INDEX idx_buildings_management_company ON buildings(management_company_id); -CREATE INDEX idx_management_services_company ON management_services(management_company_id); -CREATE INDEX idx_building_services_building ON building_management_services(building_id); -CREATE INDEX idx_initiatives_building ON initiatives(building_id); -CREATE INDEX idx_votes_initiative ON votes(initiative_id); -CREATE INDEX idx_messages_chat ON messages(chat_id); -CREATE INDEX idx_cameras_building ON cameras(building_id); -CREATE INDEX idx_tickets_user ON tickets(user_id); -CREATE INDEX idx_tickets_apartment ON tickets(apartment_id); -CREATE INDEX idx_apartments_building ON apartments(building_id); -CREATE INDEX idx_apartment_residents_apartment ON apartment_residents(apartment_id); -CREATE INDEX idx_apartment_residents_user ON apartment_residents(user_id); - --- Триггеры для обновления updated_at -CREATE OR REPLACE FUNCTION update_updated_at() -RETURNS TRIGGER AS $$ -BEGIN - NEW.updated_at = NOW(); - RETURN NEW; -END; -$$ LANGUAGE plpgsql; - --- Применяем триггеры ко всем таблицам с updated_at -DO $$ -DECLARE - t record; -BEGIN - FOR t IN - SELECT table_name - FROM information_schema.columns - WHERE column_name = 'updated_at' - AND table_schema = 'public' - LOOP - EXECUTE format('CREATE TRIGGER trigger_%s_updated_at - BEFORE UPDATE ON %I - FOR EACH ROW EXECUTE FUNCTION update_updated_at()', - t.table_name, t.table_name); - END LOOP; -END; -$$ LANGUAGE plpgsql; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/additional_services.js b/server/routers/kfu-m-24-1/sber_mobile/additional_services.js deleted file mode 100644 index 70236e4..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/additional_services.js +++ /dev/null @@ -1,53 +0,0 @@ -const router = require('express').Router(); -const { getSupabaseClient } = require('./supabaseClient'); - -// Получить все дополнительные сервисы -router.get('/additional-services', async (req, res) => { - const supabase = getSupabaseClient(); - const { data, error } = await supabase.from('additional_services').select('*').order('created_at', { ascending: false }); - if (error) return res.status(400).json({ error: error.message }); - res.json(data); -}); - -// Получить сервис по id -router.get('/additional-services/:id', async (req, res) => { - const supabase = getSupabaseClient(); - const { id } = req.params; - const { data, error } = await supabase.from('additional_services').select('*').eq('id', id).single(); - if (error) return res.status(400).json({ error: error.message }); - res.json(data); -}); - -// Создать сервис -router.post('/additional-services', async (req, res) => { - const supabase = getSupabaseClient(); - const { title, description, category, price, image_url } = req.body; - const { data, error } = await supabase.from('additional_services').insert([ - { title, description, category, price, image_url } - ]).select().single(); - if (error) return res.status(400).json({ error: error.message }); - res.json(data); -}); - -// Обновить сервис -router.put('/additional-services/:id', async (req, res) => { - const supabase = getSupabaseClient(); - const { id } = req.params; - const { title, description, category, price, image_url } = req.body; - const { data, error } = await supabase.from('additional_services').update({ - title, description, category, price, image_url - }).eq('id', id).select().single(); - if (error) return res.status(400).json({ error: error.message }); - res.json(data); -}); - -// Удалить сервис -router.delete('/additional-services/:id', async (req, res) => { - const supabase = getSupabaseClient(); - const { id } = req.params; - const { error } = await supabase.from('additional_services').delete().eq('id', id); - if (error) return res.status(400).json({ error: error.message }); - res.json({ success: true }); -}); - -module.exports = router; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/apartments.js b/server/routers/kfu-m-24-1/sber_mobile/apartments.js deleted file mode 100644 index fd360ef..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/apartments.js +++ /dev/null @@ -1,45 +0,0 @@ -const router = require('express').Router(); -const { getSupabaseClient } = require('./supabaseClient'); - -// Получить все квартиры по дому -router.get('/apartments', async (req, res) => { - const supabase = getSupabaseClient(); - const { building_id } = req.query; - if (!building_id) return res.status(400).json({ error: 'building_id required' }); - const { data, error } = await supabase.from('apartments').select('*').eq('building_id', building_id); - if (error) return res.status(400).json({ error: error.message }); - res.json(data); -}); - -// Получить адрес квартиры и название дома по id квартиры -router.get('/apartment-info', async (req, res) => { - const supabase = getSupabaseClient(); - const { apartment_id } = req.query; - if (!apartment_id) return res.status(400).json({ error: 'apartment_id required' }); - - // Получаем квартиру с building_id и номером - const { data: apartment, error: err1 } = await supabase - .from('apartments') - .select('id, number, building_id') - .eq('id', apartment_id) - .single(); - if (err1) return res.status(400).json({ error: err1.message }); - - // Получаем дом по building_id - const { data: building, error: err2 } = await supabase - .from('buildings') - .select('id, name, address') - .eq('id', apartment.building_id) - .single(); - if (err2) return res.status(400).json({ error: err2.message }); - - res.json({ - apartment_id: apartment.id, - apartment_number: apartment.number, - building_id: building.id, - building_name: building.name, - building_address: building.address - }); -}); - -module.exports = router; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/auth.js b/server/routers/kfu-m-24-1/sber_mobile/auth.js deleted file mode 100644 index 39ef7bd..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/auth.js +++ /dev/null @@ -1,51 +0,0 @@ -const router = require('express').Router(); -const { getSupabaseClient } = require('./supabaseClient'); - -// POST /sign-in -router.post('/sign-in', async (req, res) => { - const { email, password } = req.body; - const supabase = getSupabaseClient(); - const { data, error } = await supabase.auth.signInWithPassword({ email, password }); - if (error) return res.status(400).json({ error: error.message }); - res.json(data); -}); - -// POST /sign-up -router.post('/sign-up', async (req, res) => { - const { email, password } = req.body; - const supabase = getSupabaseClient(); - const { data, error } = await supabase.auth.signUp({ email, password }); - if (error) return res.status(400).json({ error: error.message }); - res.json(data); -}); - -// POST /sign-out -router.post('/sign-out', async (req, res) => { - const { access_token } = req.body; - const supabase = getSupabaseClient(); - supabase.auth.setSession({ access_token, refresh_token: '' }); - const { error } = await supabase.auth.signOut(); - if (error) return res.status(400).json({ error: error.message }); - res.json({ success: true }); -}); - -// POST /reset-password -router.post('/reset-password', async (req, res) => { - const { email } = req.body; - const supabase = getSupabaseClient(); - const { data, error } = await supabase.auth.resetPasswordForEmail(email); - if (error) return res.status(400).json({ error: error.message }); - res.json(data); -}); - -// POST /update-password -router.post('/update-password', async (req, res) => { - const { access_token, newPassword } = req.body; - const supabase = getSupabaseClient(); - supabase.auth.setSession({ access_token, refresh_token: '' }); - const { data, error } = await supabase.auth.updateUser({ password: newPassword }); - if (error) return res.status(400).json({ error: error.message }); - res.json(data); -}); - -module.exports = router; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/buildings.js b/server/routers/kfu-m-24-1/sber_mobile/buildings.js deleted file mode 100644 index 8230923..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/buildings.js +++ /dev/null @@ -1,14 +0,0 @@ -const router = require('express').Router(); -const { getSupabaseClient } = require('./supabaseClient'); - -// Получить все дома по УК -router.get('/buildings', async (req, res) => { - const supabase = getSupabaseClient(); - const { management_company_id } = req.query; - if (!management_company_id) return res.status(400).json({ error: 'management_company_id required' }); - const { data, error } = await supabase.from('buildings').select('*').eq('management_company_id', management_company_id); - if (error) return res.status(400).json({ error: error.message }); - res.json(data); -}); - -module.exports = router; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/cameras.js b/server/routers/kfu-m-24-1/sber_mobile/cameras.js deleted file mode 100644 index 1425b9f..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/cameras.js +++ /dev/null @@ -1,28 +0,0 @@ -const router = require('express').Router(); -const { getSupabaseClient } = require('./supabaseClient'); - -// Получить все камеры по дому -router.get('/cameras', async (req, res) => { - const supabase = getSupabaseClient(); - const { building_id } = req.query; - if (!building_id) return res.status(400).json({ error: 'building_id required' }); - const { data, error } = await supabase.from('cameras').select('*').eq('building_id', building_id); - if (error) return res.status(400).json({ error: error.message }); - res.json(data); -}); - -// Получить все камеры по квартире (через building_id) -router.get('/cameras/by-apartment', async (req, res) => { - const supabase = getSupabaseClient(); - const { apartment_id } = req.query; - if (!apartment_id) return res.status(400).json({ error: 'apartment_id required' }); - // Получаем building_id квартиры и сразу камеры этого дома - const { data, error } = await supabase - .from('cameras') - .select('*, apartments!inner(id, building_id)') - .eq('apartments.id', apartment_id); - if (error) return res.status(400).json({ error: error.message }); - res.json(data); -}); - -module.exports = router; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/chat-moderation.ts b/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/chat-moderation.ts deleted file mode 100644 index b89f8d5..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/chat-moderation.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { z } from "zod"; -import gigachat from './gigachat'; - -export interface ModerationResult { - comment: string; - isApproved: boolean; - success: boolean; - error?: string; -} - -export class ChatModerationAgent { - private moderationLlm: any; - - constructor(GIGA_AUTH) { - // Создаем структурированный вывод для модерации - this.moderationLlm = gigachat(GIGA_AUTH).withStructuredOutput(z.object({ - comment: z.string(), - isApproved: z.boolean(), - }) as any); - } - - private getSystemPrompt(): string { - return `Ты модерируешь сообщения в чате. Твоя задача - проверить сообщение на нецензурную лексику, брань и неприемлемый контент. - -Твои задачи: -1. Проверь сообщение на наличие нецензурной лексики, мата, ругательств и брани. -2. Проверь на оскорбления, угрозы и агрессивное поведение. -3. Проверь на спам и рекламу. -4. Проверь на неприемлемый контент (дискриминация, экстремизм и т.д.). - -- Если сообщение не содержит запрещенного контента, оно одобряется (isApproved: true). -- Если сообщение содержит запрещенный контент, оно отклоняется (isApproved: false). - -Правила написания комментария: -- Если сообщение одобряется, оставь поле comment пустым. -- Если сообщение отклоняется, пиши комментарий со следующей формулировкой: - "Сообщение удалено. Причина: (укажи конкретную причину: нецензурная лексика, оскорбления, спам и т.д.)"`; - } - - public async moderateMessage(message: string): Promise { - try { - const prompt = `${this.getSystemPrompt()} - -Сообщение: ${message}`; - - const result = await this.moderationLlm.invoke(prompt); - - // Дополнительная проверка - if (!result.isApproved && result.comment.trim() === '') { - result.comment = 'Сообщение удалено. Причина: нарушение правил чата.'; - } - - return { - comment: result.comment, - isApproved: result.isApproved, - success: true - }; - - } catch (error) { - console.error('❌ [Chat Moderation] Ошибка при модерации:', error); - - // В случае ошибки одобряем сообщение - return { - comment: '', - isApproved: true, - success: false, - error: error instanceof Error ? error.message : 'Неизвестная ошибка' - }; - } - } -} - -// Экспортируем функцию для обратной совместимости -export const moderationText = async (title: string, body: string, GIGA_AUTH): Promise<[string, boolean, string]> => { - const agent = new ChatModerationAgent(GIGA_AUTH); - const result = await agent.moderateMessage(body); - return [result.comment, result.isApproved, body]; -}; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/gigachat.ts b/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/gigachat.ts deleted file mode 100644 index 609020a..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/gigachat.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Agent } from 'node:https'; -import { GigaChat } from 'langchain-gigachat'; - -const httpsAgent = new Agent({ - rejectUnauthorized: false, -}); - -// Получаем GIGA_AUTH из переменной окружения (устанавливается в get-constants.js) -export const gigachat = (GIGA_AUTH) => new - GigaChat({ - model: 'GigaChat-2', - scope: 'GIGACHAT_API_PERS', - streaming: false, - credentials: GIGA_AUTH, - httpsAgent -}); - -export default gigachat; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/moderation-config.js b/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/moderation-config.js deleted file mode 100644 index c4efec6..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/chat-ai-agent/moderation-config.js +++ /dev/null @@ -1,16 +0,0 @@ -// Конфигурация системы модерации -const MODERATION_CONFIG = { - // Задержка перед запуском модерации (в миллисекундах) - MODERATION_DELAY: 1500, // 1.5 секунды - - // Включена ли система модерации - MODERATION_ENABLED: true, - - // Текст для замены заблокированных сообщений - BLOCKED_MESSAGE_TEXT: '[Удалено модератором]', - - // Логировать ли процесс модерации - ENABLE_MODERATION_LOGS: true -}; - -module.exports = MODERATION_CONFIG; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/chats.js b/server/routers/kfu-m-24-1/sber_mobile/chats.js deleted file mode 100644 index 047ac37..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/chats.js +++ /dev/null @@ -1,218 +0,0 @@ -const router = require('express').Router(); -const { getSupabaseClient } = require('./supabaseClient'); - -// Получить все чаты по дому -router.get('/chats', async (req, res) => { - const supabase = getSupabaseClient(); - const { building_id } = req.query; - - if (!building_id) { - return res.status(400).json({ error: 'building_id required' }); - } - - try { - const { data, error } = await supabase.from('chats').select('*').eq('building_id', building_id); - - if (error) { - return res.status(400).json({ error: error.message }); - } - - res.json(data || []); - } catch (err) { - res.status(500).json({ error: 'Internal server error' }); - } -}); - -// Получить все чаты по квартире (через building_id) -router.get('/chats/by-apartment', async (req, res) => { - const supabase = getSupabaseClient(); - const { apartment_id } = req.query; - if (!apartment_id) return res.status(400).json({ error: 'apartment_id required' }); - // Получаем building_id квартиры и сразу чаты этого дома - const { data, error } = await supabase - .from('chats') - .select('*, apartments!inner(id, building_id)') - .eq('apartments.id', apartment_id); - if (error) return res.status(400).json({ error: error.message }); - res.json(data); -}); - -// Создать новый чат -router.post('/chats', async (req, res) => { - const supabase = getSupabaseClient(); - const { building_id, name } = req.body; - - if (!building_id) { - return res.status(400).json({ error: 'building_id is required' }); - } - - const { data, error } = await supabase - .from('chats') - .insert({ building_id, name }) - .select() - .single(); - - if (error) return res.status(400).json({ error: error.message }); - res.json(data); -}); - -// Получить конкретный чат по ID -router.get('/chats/:chat_id', async (req, res) => { - const supabase = getSupabaseClient(); - const { chat_id } = req.params; - - const { data, error } = await supabase - .from('chats') - .select('*') - .eq('id', chat_id) - .single(); - - if (error) return res.status(400).json({ error: error.message }); - res.json(data); -}); - -// Обновить чат -router.put('/chats/:chat_id', async (req, res) => { - const supabase = getSupabaseClient(); - const { chat_id } = req.params; - const { name } = req.body; - - const { data, error } = await supabase - .from('chats') - .update({ name }) - .eq('id', chat_id) - .select() - .single(); - - if (error) return res.status(400).json({ error: error.message }); - res.json(data); -}); - -// Удалить чат -router.delete('/chats/:chat_id', async (req, res) => { - const supabase = getSupabaseClient(); - const { chat_id } = req.params; - - const { error } = await supabase - .from('chats') - .delete() - .eq('id', chat_id); - - if (error) return res.status(400).json({ error: error.message }); - res.json({ success: true, message: 'Chat deleted successfully' }); -}); - -// Получить статистику чата (количество сообщений, участников и т.д.) -router.get('/chats/:chat_id/stats', async (req, res) => { - const supabase = getSupabaseClient(); - const { chat_id } = req.params; - - try { - // Получаем количество сообщений - const { count: messageCount, error: messageError } = await supabase - .from('messages') - .select('*', { count: 'exact', head: true }) - .eq('chat_id', chat_id); - - if (messageError) throw messageError; - - // Получаем информацию о чате с домом - const { data: chatInfo, error: chatError } = await supabase - .from('chats') - .select(` - *, - buildings ( - id, - name, - address, - apartments ( - apartment_residents ( - user_id - ) - ) - ) - `) - .eq('id', chat_id) - .single(); - - if (chatError) throw chatError; - - // Собираем уникальные user_id жителей дома - const userIds = new Set(); - chatInfo.buildings.apartments.forEach(apartment => { - apartment.apartment_residents.forEach(resident => { - userIds.add(resident.user_id); - }); - }); - - // Получаем профили всех жителей - let uniqueResidents = []; - if (userIds.size > 0) { - const { data: profiles } = await supabase - .from('user_profiles') - .select('id, full_name, avatar_url') - .in('id', Array.from(userIds)); - - uniqueResidents = profiles || []; - } - - res.json({ - chat_id, - chat_name: chatInfo.name, - building: { - id: chatInfo.buildings.id, - name: chatInfo.buildings.name, - address: chatInfo.buildings.address - }, - message_count: messageCount || 0, - total_residents: uniqueResidents.length, - residents: uniqueResidents - }); - } catch (error) { - res.status(400).json({ error: error.message }); - } -}); - -// Получить последнее сообщение в чате -router.get('/chats/:chat_id/last-message', async (req, res) => { - const supabase = getSupabaseClient(); - const { chat_id } = req.params; - - try { - // Получаем последнее сообщение - const { data: lastMessage, error } = await supabase - .from('messages') - .select('*') - .eq('chat_id', chat_id) - .order('created_at', { ascending: false }) - .limit(1) - .single(); - - let data = null; - - if (error && error.code === 'PGRST116') { - data = null; - } else if (error) { - return res.status(400).json({ error: error.message }); - } else if (lastMessage) { - // Получаем профиль пользователя для сообщения - const { data: userProfile } = await supabase - .from('user_profiles') - .select('id, full_name, avatar_url') - .eq('id', lastMessage.user_id) - .single(); - - // Объединяем сообщение с профилем - data = { - ...lastMessage, - user_profiles: userProfile || null - }; - } - - res.json(data); - } catch (err) { - res.status(500).json({ error: 'Internal server error' }); - } -}); - -module.exports = router; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/get-constants.js b/server/routers/kfu-m-24-1/sber_mobile/get-constants.js deleted file mode 100644 index 47f34e8..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/get-constants.js +++ /dev/null @@ -1,90 +0,0 @@ -const getSupabaseUrl = async () => { - const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev'); - const data = await response.json(); - return data.features['sber_mobile'].SUPABASE_URL.value; -}; - -const getSupabaseKey = async () => { - const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev'); - const data = await response.json(); - return data.features['sber_mobile'].SUPABASE_KEY.value; -}; - -const getSupabaseServiceKey = async () => { - const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev'); - const data = await response.json(); - return data.features['sber_mobile'].SUPABASE_SERVICE_KEY.value; -}; - -const getGigaAuth = async () => { - const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev'); - const data = await response.json(); - return data.features['sber_mobile'].GIGA_AUTH.value; -}; - -const getLangsmithApiKey = async () => { - const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev'); - const data = await response.json(); - return data.features['sber_mobile'].LANGSMITH_API_KEY.value; -}; - -const getLangsmithEndpoint = async () => { - const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev'); - const data = await response.json(); - return data.features['sber_mobile'].LANGSMITH_ENDPOINT.value; -}; - -const getLangsmithTracing = async () => { - const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev'); - const data = await response.json(); - return data.features['sber_mobile'].LANGSMITH_TRACING.value; -}; - -const getLangsmithProject = async () => { - const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev'); - const data = await response.json(); - return data.features['sber_mobile'].LANGSMITH_PROJECT.value; -}; - -const getTavilyApiKey = async () => { - const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev'); - const data = await response.json(); - return data.features['sber_mobile'].TAVILY_API_KEY.value; -}; - -const getRagSupabaseServiceRoleKey = async () => { - const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev'); - const data = await response.json(); - return data.features['sber_mobile'].RAG_SUPABASE_SERVICE_ROLE_KEY.value; -}; - -const getRagSupabaseUrl = async () => { - const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev'); - const data = await response.json(); - return data.features['sber_mobile'].RAG_SUPABASE_URL.value; -}; - -module.exports = { - getSupabaseUrl, - getSupabaseKey, - getSupabaseServiceKey, - getGigaAuth -}; - -// IIFE для установки переменных окружения -(async () => { - try { - process.env.GIGA_AUTH = await getGigaAuth(); - process.env.LANGSMITH_API_KEY = await getLangsmithApiKey(); - process.env.LANGSMITH_ENDPOINT = await getLangsmithEndpoint(); - process.env.LANGSMITH_TRACING = await getLangsmithTracing(); - process.env.LANGSMITH_PROJECT = await getLangsmithProject(); - process.env.TAVILY_API_KEY = await getTavilyApiKey(); - process.env.RAG_SUPABASE_SERVICE_ROLE_KEY = await getRagSupabaseServiceRoleKey(); - process.env.RAG_SUPABASE_URL = await getRagSupabaseUrl(); - - console.log('Environment variables loaded successfully'); - } catch (error) { - console.error('Error loading environment variables:', error); - } -})(); \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/index.js b/server/routers/kfu-m-24-1/sber_mobile/index.js deleted file mode 100644 index 9ef7bc9..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/index.js +++ /dev/null @@ -1,41 +0,0 @@ -const router = require('express').Router(); -const authRouter = require('./auth'); -const { supabaseRouter } = require('./supabaseClient'); -const profileRouter = require('./profile'); -const initiativesRouter = require('./initiatives'); -const votesRouter = require('./votes'); -const additionalServicesRouter = require('./additional_services'); -const chatsRouter = require('./chats'); -const camerasRouter = require('./cameras'); -const ticketsRouter = require('./tickets'); -const messagesRouter = require('./messages'); -const moderationRouter = require('./moderation'); -const utilityPaymentsRouter = require('./utility_payments'); -const apartmentsRouter = require('./apartments'); -const buildingsRouter = require('./buildings'); -const userApartmentsRouter = require('./user_apartments'); -const avatarRouter = require('./media'); -const supportRouter = require('./supportApi'); -const moderateRouter = require('./moderate.js'); - - -module.exports = router; - -router.use('/auth', authRouter); -router.use('/supabase', supabaseRouter); -router.use('', profileRouter); -router.use('', initiativesRouter); -router.use('', votesRouter); -router.use('', additionalServicesRouter); -router.use('', chatsRouter); -router.use('', camerasRouter); -router.use('', ticketsRouter); -router.use('', messagesRouter); -router.use('', moderationRouter); -router.use('', utilityPaymentsRouter); -router.use('', apartmentsRouter); -router.use('', buildingsRouter); -router.use('', userApartmentsRouter); -router.use('', avatarRouter); -router.use('', supportRouter); -router.use('', moderateRouter); \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/initiatives-ai-agents/llm.ts b/server/routers/kfu-m-24-1/sber_mobile/initiatives-ai-agents/llm.ts deleted file mode 100644 index a5a0e23..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/initiatives-ai-agents/llm.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { GigaChat as GigaChatLang} from 'langchain-gigachat'; -import { GigaChat } from 'gigachat'; -import { Agent } from 'node:https'; - -const httpsAgent = new Agent({ - rejectUnauthorized: false, -}); - -export const llm_mod = (GIGA_AUTH) => - new GigaChatLang({ - credentials: GIGA_AUTH, - temperature: 0.2, - model: 'GigaChat-2-Max', - httpsAgent, -}); - -export const llm_gen = (GIGA_AUTH) => - new GigaChat({ - credentials: GIGA_AUTH, - model: 'GigaChat-2', - httpsAgent, -}); \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/initiatives-ai-agents/moderation.ts b/server/routers/kfu-m-24-1/sber_mobile/initiatives-ai-agents/moderation.ts deleted file mode 100644 index 48162c0..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/initiatives-ai-agents/moderation.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { llm_mod } from './llm' -import { z } from "zod"; - - -// возвращаю комментарий + исправленное предложение + булево значение - -export const moderationText = async (title: string, description: string, GIGA_AUTH): Promise<[string, string | undefined, boolean]> => { - - const moderationLlm = llm_mod(GIGA_AUTH).withStructuredOutput(z.object({ - comment: z.string(), - fixedText: z.string().optional(), - isApproved: z.boolean(), - }) as any) - - const prompt = ` - Представь, что ты модерируешь предложения от жильцов многоквартирного дома (это личная инициатива по улучшения, - не имеющая отношения к Управляющей компании). - - Заголовок: ${title} - Основной текст: ${description} - - Твои задачи: - 1. Проверь предложение и заголовок на спам. - 2. Проверь, чтобы заголовок и текст были на одну тему. - 3. Проверь само предложение пользователя на отсутствие грубой лексики и пошлостей. - 4. Проверь грамматику. - 5. Проверь на бессмысленность предложения. Оно не должно содержать только случайные символы. - 6. Не должно быть рекламы, ссылок и т.д. - 7. Проверь предложение на информативность, предложение не может быть коротким, оно должно ясно отражжать суть инициативы. - 8. Предложение должно быть в вежливой форме. - - - Если все правила соблюдены, то предложение принимается! - - - Если предложение отклонено, всегда пиши комментарий и fixedText! - - Правила написания комментария: - - Если предложение отклоняется, пиши комментарий со следующей формулировкой: - "Предложение отклонено. Причина: (укажи проблему)" - - Правила написания fixedText: - - Если предложение отклонено, то верни в поле "fixedText" измененный текст, который будет соответствовать правилам. - - Если предложение отклонено и содержит запрещённый контент (рекламу, личные данные), удали всю информацию, - которая противоречит правилам, и верни в только подходящий фрагмент, сохраняя общий смысл. - - Если текст не представляет никакой ценности, возврати в поле "fixedText" правило, - по которому оно не прошло. - -Если предложение принимается, то ничего не возвращай в поле fixedText. - ` - - const result = await moderationLlm.invoke(prompt); - if(!result.isApproved && result.comment.trim() === '' && (!result.fixedText || result.fixedText.trim() === '')) { - result.comment = 'Предложение отклонено. Причина: несоблюдение требований к оформлению или содержанию.', - result.fixedText = description - } - - return [result.comment, result.fixedText, result.isApproved]; -}; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/initiatives-ai-agents/picture.ts b/server/routers/kfu-m-24-1/sber_mobile/initiatives-ai-agents/picture.ts deleted file mode 100644 index d216c5d..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/initiatives-ai-agents/picture.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { llm_gen } from './llm' -import { detectImage } from 'gigachat'; - -export const generatePicture = async (prompt: string, GIGA_AUTH) => { - const resp = await llm_gen(GIGA_AUTH).chat({ - messages: [ - { - "role": "system", - "content": "Ты — Василий Кандинский для жильцов многоквартирного дома" - }, - { - role: "user", - content: `Старайся передать атмосферу уюта и безопасности. - Нарисуй картинку подходящую для такого события: ${prompt} - В картинке не должно быть текста, только изображение.`, - }, - ], - function_call: 'auto', - }); - - // Получение изображения по идентификатору - const detectedImage = detectImage(resp.choices[0]?.message.content ?? ''); - - if (!detectedImage?.uuid) { - throw new Error('Не удалось получить UUID изображения из ответа GigaChat'); - } - - const image = await llm_gen(GIGA_AUTH).getImage(detectedImage.uuid); - - // Возвращаем содержимое изображения, убеждаясь что это Buffer - if (Buffer.isBuffer(image.content)) { - return image.content; - } else if (typeof image.content === 'string') { - return Buffer.from(image.content, 'binary'); - } else { - throw new Error('Unexpected image content type: ' + typeof image.content); - } -} diff --git a/server/routers/kfu-m-24-1/sber_mobile/initiatives.js b/server/routers/kfu-m-24-1/sber_mobile/initiatives.js deleted file mode 100644 index 42f82e4..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/initiatives.js +++ /dev/null @@ -1,101 +0,0 @@ -const router = require('express').Router(); -const { getSupabaseClient } = require('./supabaseClient'); - -// Получить все предложения, инициативы status=review (по дому) -router.get('/initiatives-review', async (req, res) => { - const supabase = getSupabaseClient(); - const { building_id } = req.query; - let query = supabase.from('initiatives').select('*').eq('status', 'review'); - if (building_id) query = query.eq('building_id', building_id); - const { data, error } = await query; - if (error) return res.status(400).json({ error: error.message }); - res.json(data); -}); - -// Получить все сборы, инициативы status=fundraising (по дому) -router.get('/initiatives-fundraising', async (req, res) => { - const supabase = getSupabaseClient(); - const { building_id } = req.query; - let query = supabase.from('initiatives').select('*').eq('status', 'fundraising'); - if (building_id) query = query.eq('building_id', building_id); - const { data, error } = await query; - if (error) return res.status(400).json({ error: error.message }); - res.json(data); -}); - -// Получить инициативу по id (и optionally building_id) -router.get('/initiatives/:id', async (req, res) => { - const supabase = getSupabaseClient(); - const { id } = req.params; - const { building_id } = req.query; - let query = supabase.from('initiatives').select('*').eq('id', id); - if (building_id) query = query.eq('building_id', building_id); - const { data, error } = await query.single(); - if (error) return res.status(400).json({ error: error.message }); - res.json(data); -}); - -// Создать инициативу -router.post('/initiatives', async (req, res) => { - const supabase = getSupabaseClient(); - const { building_id, creator_id, title, description, status, target_amount, current_amount, image_url } = req.body; - const { data, error } = await supabase.from('initiatives').insert([ - { building_id, creator_id, title, description, status, target_amount, current_amount: current_amount || 0, image_url } - ]).select().single(); - if (error) return res.status(400).json({ error: error.message }); - res.json(data); -}); - -// Обновить инициативу -router.put('/initiatives/:id', async (req, res) => { - const supabase = getSupabaseClient(); - const { id } = req.params; - const { title, description, status, target_amount, current_amount, image_url } = req.body; - const { data, error } = await supabase.from('initiatives').update({ - title, description, status, target_amount, current_amount, image_url - }).eq('id', id).select().single(); - if (error) return res.status(400).json({ error: error.message }); - res.json(data); -}); - -// Удалить инициативу -router.delete('/initiatives/:id', async (req, res) => { - const supabase = getSupabaseClient(); - const { id } = req.params; - const { error } = await supabase.from('initiatives').delete().eq('id', id); - if (error) return res.status(400).json({ error: error.message }); - res.json({ success: true }); -}); - -// Получить все инициативы по квартире с голосами пользователя -router.get('/initiatives/by-apartment', async (req, res) => { - const supabase = getSupabaseClient(); - const { apartment_id, user_id } = req.query; - if (!apartment_id) return res.status(400).json({ error: 'apartment_id required' }); - // Получаем building_id квартиры - const { data: apartments, error: err1 } = await supabase - .from('apartments') - .select('building_id') - .eq('id', apartment_id) - .single(); - if (err1) return res.status(400).json({ error: err1.message }); - const building_id = apartments.building_id; - // Получаем инициативы этого дома с голосами пользователя (если user_id передан) - let selectStr = '*, votes:initiatives(id, votes!left(user_id, vote_type))'; - if (!user_id) selectStr = '*'; - const { data, error } = await supabase - .from('initiatives') - .select(selectStr) - .eq('building_id', building_id); - if (error) return res.status(400).json({ error: error.message }); - // Если user_id передан, фильтруем только голос текущего пользователя - if (user_id && data) { - data.forEach(initiative => { - initiative.user_vote = (initiative.votes || []).find(v => v.user_id === user_id) || null; - delete initiative.votes; - }); - } - res.json(data); -}); - -module.exports = router; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/media.js b/server/routers/kfu-m-24-1/sber_mobile/media.js deleted file mode 100644 index e2a8df9..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/media.js +++ /dev/null @@ -1,15 +0,0 @@ -const router = require('express').Router(); -const { supabaseRouter } = require('./supabaseClient'); - - -// GET /avatar -router.get('/avatar', async (req, res) => { - const supabase = getSupabaseClient(); - const { user_id } = req.query; - if (!user_id) return res.status(400).json({ error: 'user_id required' }); - const { data, error } = await supabase.storage.from('avatars').download(`avatar_${user_id}.png`); - if (error) return res.status(400).json({ error: error.message }); - res.blob(data); - }); - -module.exports = router; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/messages.js b/server/routers/kfu-m-24-1/sber_mobile/messages.js deleted file mode 100644 index a495e33..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/messages.js +++ /dev/null @@ -1,235 +0,0 @@ -const router = require('express').Router(); -const { getSupabaseClient } = require('./supabaseClient'); -const { moderationText } = require('./chat-ai-agent/chat-moderation'); // Импортируем функцию модерации -const MODERATION_CONFIG = require('./chat-ai-agent/moderation-config'); // Импортируем конфигурацию модерации - - -// Добавляем middleware для логирования всех запросов к messages роутеру - -// Тестовый эндпоинт для проверки работы роутера -router.get('/messages/test', (req, res) => { - res.json({ - status: 'OK', - message: 'Messages router работает', - timestamp: new Date().toISOString(), - moderation_config: MODERATION_CONFIG - }); -}); - -// Получить все сообщения в чате с информацией о пользователе -router.get('/messages', async (req, res) => { - try { - const { chat_id, limit = 50, offset = 0 } = req.query; - - if (!chat_id) { - return res.status(400).json({ error: 'chat_id is required' }); - } - - const supabase = getSupabaseClient(); - - const { data, error } = await supabase - .from('messages') - .select(` - *, - user_profiles ( - id, - full_name, - avatar_url - ) - `) - .eq('chat_id', chat_id) - .order('created_at', { ascending: true }) - .range(offset, offset + limit - 1); - - if (error) { - return res.status(500).json({ error: 'Failed to fetch messages' }); - } - - // Получаем уникальные ID пользователей из сообщений, у которых нет профиля - const messagesWithoutProfiles = data.filter(msg => !msg.user_profiles); - const userIds = [...new Set(messagesWithoutProfiles.map(msg => msg.user_id))]; - - if (userIds.length > 0) { - const { data: profiles, error: profilesError } = await supabase - .from('user_profiles') - .select('id, full_name, avatar_url') - .in('id', userIds); - - if (!profilesError && profiles) { - // Добавляем профили к сообщениям - data.forEach(message => { - if (!message.user_profiles) { - message.user_profiles = profiles.find(profile => profile.id === message.user_id) || null; - } - }); - } - } - - res.json(data); - } catch (err) { - res.status(500).json({ error: 'Unexpected error occurred' }); - } -}); - -// Создать новое сообщение -router.post('/messages', async (req, res) => { - - let supabase; - try { - supabase = getSupabaseClient(); - } catch (error) { - console.error(`❌ [Message Send] Ошибка получения Supabase клиента:`, error); - return res.status(500).json({ error: 'Database connection error' }); - } - - const { chat_id, user_id, text } = req.body; - - - if (!chat_id || !user_id || !text) { - console.log(`❌ [Message Send] Отклонен: отсутствуют обязательные поля`); - console.log(`❌ [Message Send] chat_id: ${chat_id}, user_id: ${user_id}, text: ${text}`); - return res.status(400).json({ - error: 'chat_id, user_id, and text are required' - }); - } - - // Создаем сообщение - const { data: newMessage, error } = await supabase - .from('messages') - .insert({ chat_id, user_id, text }) - .select('*') - .single(); - - if (error) { - console.error(`❌ [Message Send] Ошибка сохранения в Supabase:`, error); - return res.status(400).json({ error: error.message }); - } - - // Получаем профиль пользователя - const { data: userProfile, error: profileError } = await supabase - .from('user_profiles') - .select('id, full_name, avatar_url') - .eq('id', user_id) - .single(); - - if (profileError) { - console.log(`⚠️ [Message Send] Профиль пользователя не найден:`, profileError); - } - - // Объединяем сообщение с профилем - const data = { - ...newMessage, - user_profiles: userProfile || null - }; - - res.json(data); -}); - -// Получить конкретное сообщение -router.get('/messages/:message_id', async (req, res) => { - const supabase = getSupabaseClient(); - const { message_id } = req.params; - - // Получаем сообщение - const { data: message, error } = await supabase - .from('messages') - .select('*') - .eq('id', message_id) - .single(); - - if (error) return res.status(400).json({ error: error.message }); - - // Получаем профиль пользователя - const { data: userProfile } = await supabase - .from('user_profiles') - .select('id, full_name, avatar_url') - .eq('id', message.user_id) - .single(); - - // Объединяем сообщение с профилем - const data = { - ...message, - user_profiles: userProfile || null - }; - - res.json(data); -}); - -// Получить последние сообщения для каждого чата (для списка чатов) -router.get('/chats/last-messages', async (req, res) => { - const supabase = getSupabaseClient(); - const { building_id } = req.query; - - if (!building_id) { - return res.status(400).json({ error: 'building_id required' }); - } - - // Получаем чаты и их последние сообщения через обычные запросы - const { data: chats, error: chatsError } = await supabase - .from('chats') - .select('*') - .eq('building_id', building_id); - - if (chatsError) return res.status(400).json({ error: chatsError.message }); - - // Для каждого чата получаем последнее сообщение - const chatsWithMessages = await Promise.all( - chats.map(async (chat) => { - const { data: lastMessage } = await supabase - .from('messages') - .select(` - *, - user_profiles:user_id ( - id, - full_name, - avatar_url - ) - `) - .eq('chat_id', chat.id) - .order('created_at', { ascending: false }) - .limit(1) - .single(); - - return { - ...chat, - last_message: lastMessage || null - }; - }) - ); - - res.json(chatsWithMessages); -}); - -// Удалить сообщение (только для автора) -router.delete('/messages/:message_id', async (req, res) => { - const supabase = getSupabaseClient(); - const { message_id } = req.params; - const { user_id } = req.body; - - if (!user_id) { - return res.status(400).json({ error: 'user_id required' }); - } - - // Проверяем, что пользователь является автором сообщения - const { data: message, error: fetchError } = await supabase - .from('messages') - .select('user_id') - .eq('id', message_id) - .single(); - - if (fetchError) return res.status(400).json({ error: fetchError.message }); - - if (message.user_id !== user_id) { - return res.status(403).json({ error: 'You can only delete your own messages' }); - } - - const { error } = await supabase - .from('messages') - .delete() - .eq('id', message_id); - - if (error) return res.status(400).json({ error: error.message }); - res.json({ success: true, message: 'Message deleted successfully' }); -}); - -module.exports = router; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/moderate.js b/server/routers/kfu-m-24-1/sber_mobile/moderate.js deleted file mode 100644 index 61c0692..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/moderate.js +++ /dev/null @@ -1,162 +0,0 @@ -const router = require('express').Router(); -const { moderationText } = require('./initiatives-ai-agents/moderation'); -const { generatePicture } = require('./initiatives-ai-agents/picture'); -const { getSupabaseClient } = require('./supabaseClient'); -const { getGigaAuth } = require('./get-constants'); - -async function getGigaKey() { - const GIGA_AUTH = await getGigaAuth(); - return GIGA_AUTH; - } - -// Обработчик для модерации и создания инициативы -router.post('/moderate', async (req, res) => { - - const GIGA_AUTH = await getGigaKey(); - - try { - const { title, description, building_id, creator_id, target_amount, status } = req.body; - - if (!title || !description) { - res.status(400).json({ error: 'Заголовок и описание обязательны' }); - return; - } - - if (!building_id || !creator_id) { - res.status(400).json({ error: 'ID дома и создателя обязательны' }); - return; - } - - // Валидация статуса, если передан - const validStatuses = ['moderation', 'review', 'fundraising', 'approved', 'rejected']; - if (status && !validStatuses.includes(status)) { - res.status(400).json({ error: `Недопустимый статус. Допустимые значения: ${validStatuses.join(', ')}` }); - return; - } - - console.log('Запрос на модерацию:', { title: title.substring(0, 50), description: description.substring(0, 100) }); - - // Модерация текста (передаем title и description как body) - const [comment, fixedText, isApproved] = await moderationText(title, description, GIGA_AUTH); - - // Если модерация не прошла, возвращаем undefined - if (!isApproved) { - if (!comment || comment.trim() === '') { - console.warn('Обнаружен некорректный результат модерации - пустой комментарий при отклонении'); - } - - res.json({ - comment, - fixedText, - isApproved, - initiative: undefined - }); - return; - } - - // Модерация прошла, генерируем изображение используя заголовок как промпт - console.log('Модерация прошла, генерируем изображение с промптом:', title); - - const imageBuffer = await generatePicture(title, GIGA_AUTH); - - if (!imageBuffer || imageBuffer.length === 0) { - res.status(500).json({ error: 'Получен пустой буфер изображения' }); - return; - } - - // Получаем Supabase клиент и создаем имя файла - const supabase = getSupabaseClient(); - const timestamp = Date.now(); - const filename = `image_${creator_id}_${timestamp}.jpg`; - - // Загружаем изображение в Supabase Storage - let uploadResult; - let retries = 0; - const maxRetries = 5; - - while (retries < maxRetries) { - try { - uploadResult = await supabase.storage - .from('images') - .upload(filename, imageBuffer, { - contentType: 'image/jpeg', - upsert: true - }); - - if (!uploadResult.error) { - break; // Успешная загрузка - } - - retries++; - - if (retries < maxRetries) { - // Ждем перед повторной попыткой - await new Promise(resolve => setTimeout(resolve, 1000 * retries)); - } - } catch (error) { - console.warn(`Попытка загрузки ${retries + 1} неудачна (исключение):`, error.message); - retries++; - - if (retries < maxRetries) { - // Ждем перед повторной попыткой - await new Promise(resolve => setTimeout(resolve, 1000 * retries)); - } else { - throw error; // Перебрасываем ошибку после всех попыток - } - } - } - - if (uploadResult?.error) { - console.error('Supabase storage error after all retries:', uploadResult.error); - res.status(500).json({ error: 'Ошибка при сохранении изображения после нескольких попыток' }); - return; - } - - console.log('Изображение успешно загружено в Supabase Storage:', filename); - - // Получаем публичный URL - const { data: urlData } = supabase.storage - .from('images') - .getPublicUrl(filename); - - // Определяем статус: если передан в запросе, используем его, иначе 'review' - const finalStatus = status || 'review'; - - // Создаем инициативу в базе данных - const { data: initiative, error: initiativeError } = await supabase - .from('initiatives') - .insert([{ - building_id, - creator_id, - title: fixedText || title, - description, - status: finalStatus, - target_amount: target_amount || null, - current_amount: 0, - image_url: urlData.publicUrl - }]) - .select() - .single(); - - if (initiativeError) { - console.error('Ошибка создания инициативы:', initiativeError); - res.status(500).json({ error: 'Ошибка при создании инициативы', details: initiativeError.message }); - return; - } - - console.log('Инициатива успешно создана:', initiative.id); - - res.json({ - comment, - fixedText, - isApproved, - initiative - }); - - } catch (error) { - console.error('Error in moderation and initiative creation:', error); - res.status(500).json({ error: 'Внутренняя ошибка сервера', details: error.message }); - } -}); - -module.exports = router; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/moderation.js b/server/routers/kfu-m-24-1/sber_mobile/moderation.js deleted file mode 100644 index 7f54af9..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/moderation.js +++ /dev/null @@ -1,53 +0,0 @@ -const router = require('express').Router(); -const MODERATION_CONFIG = require('./chat-ai-agent/moderation-config'); -const { moderationText } = require('./chat-ai-agent/chat-moderation'); - -// Получить текущие настройки модерации -router.get('/moderation/config', (req, res) => { - res.json(MODERATION_CONFIG); -}); - -// Обновить настройки модерации -router.post('/moderation/config', (req, res) => { - - const oldConfig = { ...MODERATION_CONFIG }; - const { MODERATION_DELAY, MODERATION_ENABLED, BLOCKED_MESSAGE_TEXT, ENABLE_MODERATION_LOGS } = req.body; - - const changes = []; - - if (MODERATION_DELAY !== undefined) { - const newValue = parseInt(MODERATION_DELAY); - MODERATION_CONFIG.MODERATION_DELAY = newValue; - changes.push(`MODERATION_DELAY: ${oldConfig.MODERATION_DELAY} -> ${newValue}`); - } - if (MODERATION_ENABLED !== undefined) { - const newValue = Boolean(MODERATION_ENABLED); - MODERATION_CONFIG.MODERATION_ENABLED = newValue; - changes.push(`MODERATION_ENABLED: ${oldConfig.MODERATION_ENABLED} -> ${newValue}`); - } - if (BLOCKED_MESSAGE_TEXT !== undefined) { - const newValue = String(BLOCKED_MESSAGE_TEXT); - MODERATION_CONFIG.BLOCKED_MESSAGE_TEXT = newValue; - changes.push(`BLOCKED_MESSAGE_TEXT: "${oldConfig.BLOCKED_MESSAGE_TEXT}" -> "${newValue}"`); - } - if (ENABLE_MODERATION_LOGS !== undefined) { - const newValue = Boolean(ENABLE_MODERATION_LOGS) - MODERATION_CONFIG.ENABLE_MODERATION_LOGS = newValue; - changes.push(`ENABLE_MODERATION_LOGS: ${oldConfig.ENABLE_MODERATION_LOGS} -> ${newValue}`); - } - - if (changes.length > 0) { - changes.forEach((change, index) => { - }); - } else { - } - - res.json({ - success: true, - message: 'Настройки модерации обновлены', - changes: changes, - config: MODERATION_CONFIG - }); -}); - -module.exports = router; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/polling-chat.js b/server/routers/kfu-m-24-1/sber_mobile/polling-chat.js deleted file mode 100644 index d33ead2..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/polling-chat.js +++ /dev/null @@ -1,982 +0,0 @@ -const { getSupabaseClient, initializationPromise } = require('./supabaseClient'); -const MODERATION_CONFIG = require('./chat-ai-agent/moderation-config'); -const { getGigaAuth } = require('./get-constants'); -const { moderationText } = require('./chat-ai-agent/chat-moderation'); - -async function getGigaKey() { - const GIGA_AUTH = await getGigaAuth(); - return GIGA_AUTH; -} - -class ChatPollingHandler { - constructor() { - this.connectedClients = new Map(); // user_id -> { user_info, chats: Set(), lastActivity: Date } - this.chatParticipants = new Map(); // chat_id -> Set(user_id) - this.userEventQueues = new Map(); // user_id -> [{id, event, data, timestamp}] - this.eventIdCounter = 0; - this.realtimeSubscription = null; - - // Инициализируем Supabase подписку с задержкой и проверками - this.initializeWithRetry(); - - // Очистка старых событий каждые 5 минут - setInterval(() => { - this.cleanupOldEvents(); - }, 5 * 60 * 1000); - } - - // Инициализация с повторными попытками - async initializeWithRetry() { - try { - // Сначала ждем завершения основной инициализации - await initializationPromise; - - this.setupRealtimeSubscription(); - this.testRealtimeConnection(); - return; - - } catch (error) { - console.log('❌ [Supabase] Основная инициализация неудачна, пробуем альтернативный подход'); - } - - // Если основная инициализация не удалась, используем повторные попытки - let attempts = 0; - const maxAttempts = 10; - const baseDelay = 2000; // 2 секунды - - while (attempts < maxAttempts) { - try { - attempts++; - - // Ждем перед попыткой - await new Promise(resolve => setTimeout(resolve, baseDelay * attempts)); - - // Проверяем готовность Supabase клиента - const supabase = getSupabaseClient(); - if (supabase) { - this.setupRealtimeSubscription(); - this.testRealtimeConnection(); - return; // Успех, выходим - } - } catch (error) { - console.log(`❌ [Supabase] Попытка #${attempts} неудачна:`, error.message); - - if (attempts === maxAttempts) { - console.error('❌ [Supabase] Все попытки инициализации исчерпаны'); - console.error('❌ [Supabase] Realtime подписка будет недоступна'); - return; - } - } - } - } - - // Аутентификация пользователя - async handleAuthentication(req, res) { - const { user_id, token } = req.body; - - if (!user_id) { - res.status(400).json({ error: 'user_id is required' }); - return; - } - - try { - // Проверяем пользователя в базе данных - const supabase = getSupabaseClient(); - const { data: userProfile, error } = await supabase - .from('user_profiles') - .select('*') - .eq('id', user_id) - .single(); - - if (error) { - console.log('❌ [Polling Server] Пользователь не найден:', error); - res.status(401).json({ error: 'User not found' }); - return; - } - - // Регистрируем пользователя - this.connectedClients.set(user_id, { - user_info: { - user_id, - profile: userProfile, - last_seen: new Date() - }, - chats: new Set(), - lastActivity: new Date() - }); - - // Создаем очередь событий для пользователя - if (!this.userEventQueues.has(user_id)) { - this.userEventQueues.set(user_id, []); - } - - // Добавляем событие аутентификации в очередь - this.addEventToQueue(user_id, 'authenticated', { - message: 'Successfully authenticated', - user: userProfile - }); - - res.json({ - success: true, - message: 'Successfully authenticated', - user: userProfile - }); - - } catch (error) { - console.error('❌ [Polling Server] Ошибка аутентификации:', error); - res.status(500).json({ error: 'Authentication failed' }); - } - } - - // Эндпоинт для получения событий (polling) - async handleGetEvents(req, res) { - try { - const { user_id, last_event_id } = req.query; - - if (!user_id) { - res.status(400).json({ error: 'user_id is required' }); - return; - } - - const client = this.connectedClients.get(user_id); - if (!client) { - res.status(401).json({ error: 'Not authenticated' }); - return; - } - - // Обновляем время последней активности - client.lastActivity = new Date(); - - // Получаем очередь событий пользователя - const eventQueue = this.userEventQueues.get(user_id) || []; - - // Фильтруем события после last_event_id - const lastEventId = parseInt(last_event_id) || 0; - const newEvents = eventQueue.filter(event => event.id > lastEventId); - - // Логируем отправку событий клиенту - if (newEvents.length > 0) { - console.log(`📨 [Polling Server] Отправляем ${newEvents.length} событий клиенту ${user_id}`); - newEvents.forEach(event => { - if (event.event === 'message_updated') { - console.log(`📨 [Polling Server] → Событие: ${event.event}, Сообщение ID: ${event.data?.message?.id}, Текст: "${event.data?.message?.text?.substring(0, 50)}${(event.data?.message?.text?.length || 0) > 50 ? '...' : ''}"`); - } - }); - } - - res.json({ - success: true, - events: newEvents, - last_event_id: eventQueue.length > 0 ? Math.max(...eventQueue.map(e => e.id)) : lastEventId - }); - - } catch (error) { - console.error('❌ [Polling Server] Ошибка получения событий:', error); - res.status(500).json({ error: 'Failed to get events' }); - } - } - - // HTTP эндпоинт для присоединения к чату - async handleJoinChat(req, res) { - try { - const { user_id, chat_id } = req.body; - - if (!user_id || !chat_id) { - res.status(400).json({ error: 'user_id and chat_id are required' }); - return; - } - - const client = this.connectedClients.get(user_id); - if (!client) { - res.status(401).json({ error: 'Not authenticated' }); - return; - } - - // Проверяем, что чат существует и пользователь имеет доступ к нему - const supabase = getSupabaseClient(); - const { data: chat, error } = await supabase - .from('chats') - .select(` - *, - buildings ( - management_company_id, - apartments ( - apartment_residents ( - user_id - ) - ) - ) - `) - .eq('id', chat_id) - .single(); - - if (error || !chat) { - res.status(404).json({ error: 'Chat not found' }); - return; - } - - // Проверяем доступ пользователя к чату через квартиры в доме - const hasAccess = chat.buildings.apartments.some(apartment => - apartment.apartment_residents.some(resident => - resident.user_id === user_id - ) - ); - - if (!hasAccess) { - res.status(403).json({ error: 'Access denied to this chat' }); - return; - } - - // Добавляем пользователя в чат - client.chats.add(chat_id); - - if (!this.chatParticipants.has(chat_id)) { - this.chatParticipants.set(chat_id, new Set()); - } - this.chatParticipants.get(chat_id).add(user_id); - - // Добавляем событие присоединения в очередь пользователя - this.addEventToQueue(user_id, 'joined_chat', { - chat_id, - chat: chat, - message: 'Successfully joined chat' - }); - - // Уведомляем других участников о подключении - this.broadcastToChatExcludeUser(chat_id, user_id, 'user_joined', { - chat_id, - user: client.user_info.profile, - timestamp: new Date() - }); - - res.json({ success: true, message: 'Joined chat successfully' }); - - } catch (error) { - res.status(500).json({ error: 'Failed to join chat' }); - } - } - - // HTTP эндпоинт для покидания чата - async handleLeaveChat(req, res) { - try { - const { user_id, chat_id } = req.body; - - if (!user_id || !chat_id) { - res.status(400).json({ error: 'user_id and chat_id are required' }); - return; - } - - const client = this.connectedClients.get(user_id); - if (!client) { - res.status(401).json({ error: 'Not authenticated' }); - return; - } - - // Удаляем пользователя из чата - client.chats.delete(chat_id); - - if (this.chatParticipants.has(chat_id)) { - this.chatParticipants.get(chat_id).delete(user_id); - - // Если чат пуст, удаляем его - if (this.chatParticipants.get(chat_id).size === 0) { - this.chatParticipants.delete(chat_id); - } - } - - // Уведомляем других участников об отключении - this.broadcastToChatExcludeUser(chat_id, user_id, 'user_left', { - chat_id, - user: client.user_info.profile, - timestamp: new Date() - }); - - res.json({ success: true, message: 'Left chat successfully' }); - - } catch (error) { - res.status(500).json({ error: 'Failed to leave chat' }); - } - } - - // HTTP эндпоинт для отправки сообщения - async handleSendMessage(req, res) { - try { - const { user_id, chat_id, text } = req.body; - - if (!user_id || !chat_id || !text) { - res.status(400).json({ error: 'user_id, chat_id and text are required' }); - return; - } - - const client = this.connectedClients.get(user_id); - if (!client) { - res.status(401).json({ error: 'Not authenticated' }); - return; - } - - if (!client.chats.has(chat_id)) { - res.status(403).json({ error: 'Not joined to this chat' }); - return; - } - - // Сохраняем сообщение в базу данных - const supabase = getSupabaseClient(); - const { data: message, error } = await supabase - .from('messages') - .insert({ - chat_id, - user_id, - text - }) - .select(` - *, - user_profiles ( - id, - full_name, - avatar_url - ) - `) - .single(); - - if (error) { - res.status(500).json({ error: 'Failed to save message' }); - return; - } - - // Отправляем сообщение всем участникам чата - this.broadcastToChat(chat_id, 'new_message', { - message, - timestamp: new Date() - }); - - res.json({ success: true, message: 'Message sent successfully' }); - - } catch (error) { - res.status(500).json({ error: 'Failed to send message' }); - } - } - - // HTTP эндпоинт для индикации печатания - async handleTypingStart(req, res) { - try { - const { user_id, chat_id } = req.body; - - if (!user_id || !chat_id) { - res.status(400).json({ error: 'user_id and chat_id are required' }); - return; - } - - const client = this.connectedClients.get(user_id); - if (!client) { - res.status(401).json({ error: 'Not authenticated' }); - return; - } - - if (!client.chats.has(chat_id)) { - res.status(403).json({ error: 'Not joined to this chat' }); - return; - } - - this.broadcastToChatExcludeUser(chat_id, user_id, 'user_typing_start', { - chat_id, - user: client.user_info.profile, - timestamp: new Date() - }); - - res.json({ success: true }); - - } catch (error) { - res.status(500).json({ error: 'Failed to send typing indicator' }); - } - } - - // HTTP эндпоинт для остановки индикации печатания - async handleTypingStop(req, res) { - try { - const { user_id, chat_id } = req.body; - - if (!user_id || !chat_id) { - res.status(400).json({ error: 'user_id and chat_id are required' }); - return; - } - - const client = this.connectedClients.get(user_id); - if (!client) { - res.status(401).json({ error: 'Not authenticated' }); - return; - } - - if (!client.chats.has(chat_id)) { - res.status(403).json({ error: 'Not joined to this chat' }); - return; - } - - this.broadcastToChatExcludeUser(chat_id, user_id, 'user_typing_stop', { - chat_id, - user: client.user_info.profile, - timestamp: new Date() - }); - - res.json({ success: true }); - - } catch (error) { - res.status(500).json({ error: 'Failed to send typing indicator' }); - } - } - - // Обработка отключения клиента - handleClientDisconnect(user_id) { - const client = this.connectedClients.get(user_id); - if (!client) return; - - // Удаляем пользователя из всех чатов - client.chats.forEach(chat_id => { - if (this.chatParticipants.has(chat_id)) { - this.chatParticipants.get(chat_id).delete(user_id); - - // Уведомляем других участников об отключении - this.broadcastToChatExcludeUser(chat_id, user_id, 'user_left', { - chat_id, - user: client.user_info.profile, - timestamp: new Date() - }); - - // Если чат пуст, удаляем его - if (this.chatParticipants.get(chat_id).size === 0) { - this.chatParticipants.delete(chat_id); - } - } - }); - - // Удаляем клиента - this.connectedClients.delete(user_id); - } - - // Добавление события в очередь пользователя - addEventToQueue(user_id, event, data) { - if (!this.userEventQueues.has(user_id)) { - this.userEventQueues.set(user_id, []); - } - - const eventQueue = this.userEventQueues.get(user_id); - const eventId = ++this.eventIdCounter; - - eventQueue.push({ - id: eventId, - event, - data, - timestamp: new Date() - }); - - // Ограничиваем размер очереди (последние 100 событий) - if (eventQueue.length > 100) { - eventQueue.splice(0, eventQueue.length - 100); - } - } - - // Рассылка события всем участникам чата - broadcastToChat(chat_id, event, data) { - const participants = this.chatParticipants.get(chat_id); - if (!participants) return; - - participants.forEach(user_id => { - this.addEventToQueue(user_id, event, data); - }); - } - - // Рассылка события всем участникам чата кроме отправителя - broadcastToChatExcludeUser(chat_id, exclude_user_id, event, data) { - const participants = this.chatParticipants.get(chat_id); - if (!participants) return; - - participants.forEach(user_id => { - if (user_id !== exclude_user_id) { - this.addEventToQueue(user_id, event, data); - } - }); - } - - // Получение списка онлайн пользователей в чате - getOnlineUsersInChat(chat_id) { - const participants = this.chatParticipants.get(chat_id) || new Set(); - const onlineUsers = []; - const now = new Date(); - const ONLINE_THRESHOLD = 2 * 60 * 1000; // 2 минуты - - participants.forEach(user_id => { - const client = this.connectedClients.get(user_id); - if (client && (now - client.lastActivity) < ONLINE_THRESHOLD) { - onlineUsers.push(client.user_info.profile); - } - }); - - return onlineUsers; - } - - // Отправка системного сообщения в чат - async sendSystemMessage(chat_id, text) { - this.broadcastToChat(chat_id, 'system_message', { - chat_id, - text, - timestamp: new Date() - }); - } - - // Очистка старых событий - cleanupOldEvents() { - const now = new Date(); - const MAX_EVENT_AGE = 1 * 60 * 60 * 1000; // 1 час - const INACTIVE_USER_THRESHOLD = 30 * 60 * 1000; // 30 минут - - // Очищаем старые события - this.userEventQueues.forEach((eventQueue, user_id) => { - const filteredEvents = eventQueue.filter(event => - (now - event.timestamp) < MAX_EVENT_AGE - ); - - if (filteredEvents.length !== eventQueue.length) { - this.userEventQueues.set(user_id, filteredEvents); - } - }); - - // Удаляем неактивных пользователей - this.connectedClients.forEach((client, user_id) => { - if ((now - client.lastActivity) > INACTIVE_USER_THRESHOLD) { - this.handleClientDisconnect(user_id); - this.userEventQueues.delete(user_id); - } - }); - } - - // Тестирование Real-time подписки - async testRealtimeConnection() { - try { - const supabase = getSupabaseClient(); - if (!supabase) { - return false; - } - - // Создаем тестовый канал для проверки подключения - const testChannel = supabase - .channel('test_connection') - .subscribe((status, error) => { - if (error) { - console.error('❌ [Supabase] Тестовый канал - ошибка:', error); - } - - if (status === 'SUBSCRIBED') { - // Отписываемся от тестового канала - setTimeout(() => { - testChannel.unsubscribe(); - }, 2000); - } - }); - - return true; - } catch (error) { - console.error('❌ [Supabase] Ошибка тестирования Realtime:', error); - return false; - } - } - - // Проверка статуса подписки - checkSubscriptionStatus() { - if (this.realtimeSubscription) { - return true; - } else { - return false; - } - } - - setupRealtimeSubscription() { - // Убираем setTimeout, вызываем сразу - this._doSetupRealtimeSubscription(); - } - - _doSetupRealtimeSubscription() { - try { - const supabase = getSupabaseClient(); - - if (!supabase) { - console.log('❌ [Supabase] Supabase клиент не найден'); - throw new Error('Supabase client not available'); - } - - // Подписываемся на изменения в таблице messages - const subscription = supabase - .channel('messages_changes') - .on( - 'postgres_changes', - { - event: 'INSERT', - schema: 'public', - table: 'messages' - }, - async (payload) => { - try { - const newMessage = payload.new; - if (!newMessage) { - return; - } - - if (!newMessage.chat_id) { - return; - } - - // Получаем профиль пользователя - const { data: userProfile, error: profileError } = await supabase - .from('user_profiles') - .select('id, full_name, avatar_url') - .eq('id', newMessage.user_id) - .single(); - - if (profileError) { - console.error('❌ [Supabase] Ошибка получения профиля пользователя:', profileError); - } - - // Объединяем сообщение с профилем - const messageWithProfile = { - ...newMessage, - user_profiles: userProfile || null - }; - - // Отправляем сообщение всем участникам чат - this.broadcastToChat(newMessage.chat_id, 'new_message', { - message: messageWithProfile, - timestamp: new Date() - }); - - // === ЗАПУСК МОДЕРАЦИИ === - if (MODERATION_CONFIG.MODERATION_ENABLED) { - - if (MODERATION_CONFIG.MODERATION_DELAY === 0) { - setImmediate(() => { - this.moderateMessage(newMessage.id, newMessage.text, newMessage.chat_id); - }); - } else { - const timeoutId = setTimeout(() => { - this.moderateMessage(newMessage.id, newMessage.text, newMessage.chat_id); - }, MODERATION_CONFIG.MODERATION_DELAY); - - } - - } - - } catch (callbackError) { - console.error('❌ [Supabase] Ошибка в обработчике сообщения:', callbackError); - console.error('❌ [Supabase] Stack trace:', callbackError.stack); - } - } - ) - .on( - 'postgres_changes', - { - event: 'UPDATE', - schema: 'public', - table: 'messages' - }, - async (payload) => { - try { - const updatedMessage = payload.new; - if (!updatedMessage) { - return; - } - - if (!updatedMessage.chat_id) { - return; - } - - // Получаем профиль пользователя - const { data: userProfile, error: profileError } = await supabase - .from('user_profiles') - .select('id, full_name, avatar_url') - .eq('id', updatedMessage.user_id) - .single(); - - if (profileError) { - console.error('❌ [Supabase] Ошибка получения профиля пользователя:', profileError); - } - - // Объединяем сообщение с профилем - const messageWithProfile = { - ...updatedMessage, - user_profiles: userProfile || null - }; - - // Отправляем обновление всем участникам чат - this.broadcastToChat(updatedMessage.chat_id, 'message_updated', { - message: messageWithProfile, - timestamp: new Date() - }); - - } catch (callbackError) { - console.error('❌ [Supabase] Ошибка в обработчике обновления сообщения:', callbackError); - } - } - ) - .subscribe((status, error) => { - if (error) { - console.error('❌ [Supabase] Ошибка подписки:', error); - } - - if (status === 'CHANNEL_ERROR') { - console.error('❌ [Supabase] Ошибка канала'); - } else if (status === 'TIMED_OUT') { - console.error('❌ [Supabase] Таймаут подписки'); - } - }); - - // Сохраняем ссылку на подписку для возможности отписки - this.realtimeSubscription = subscription; - - } catch (error) { - console.error('❌ [Supabase] Критическая ошибка при настройке подписки:', error); - throw error; // Пробрасываем ошибку для обработки в initializeWithRetry - } - } - - // Функция отложенной модерации сообщения - async moderateMessage(messageId, messageText, chatId) { - const moderationStartTime = Date.now(); - - try { - - // Вызываем функцию модерации - - let comment, isApproved, finalMessage; - const GIGA_AUTH = await getGigaKey(); - console.log(GIGA_AUTH) - try { - const result = await moderationText('', messageText, GIGA_AUTH); - [comment, isApproved, finalMessage] = result; - } catch (moderationError) { - console.error(`❌ [Moderation] Ошибка при вызове AI агента:`, moderationError); - console.error(`❌ [Moderation] Stack trace:`, moderationError.stack); - // В случае ошибки одобряем сообщение - comment = ''; - isApproved = true; - finalMessage = messageText; - console.log(`⚠️ [Moderation] Используем fallback значения из-за ошибки`); - } - - const moderationTime = Date.now() - moderationStartTime; - - if (isApproved) { - console.log(`📝 [Moderation] Действие: сообщение остается без изменений`); - } else { - console.log(`📝 [Moderation] Действие: сообщение будет заменено в базе данных`); - } - - // Если сообщение не прошло модерацию, обновляем его в базе данных - if (!isApproved) { - console.log(`💾 [Moderation] Начинаем обновление сообщения в базе данных...`); - - const supabase = getSupabaseClient(); - - // Сначала получаем информацию о сообщении для получения chat_id - console.log(`💾 [Moderation] Получаем данные сообщения из базы...`); - const { data: messageData, error: fetchError } = await supabase - .from('messages') - .select('chat_id, user_id') - .eq('id', messageId) - .single(); - - if (fetchError) { - console.error(`❌ [Moderation] Ошибка получения данных сообщения ${messageId}:`, fetchError); - return; - } - - console.log(`💾 [Moderation] Данные получены. Chat ID: ${messageData.chat_id}, User ID: ${messageData.user_id}`); - - // Обновляем текст сообщения - console.log(`💾 [Moderation] Обновляем текст сообщения на: "${MODERATION_CONFIG.BLOCKED_MESSAGE_TEXT}"`); - const { data: updatedMessage, error } = await supabase - .from('messages') - .update({ text: MODERATION_CONFIG.BLOCKED_MESSAGE_TEXT }) - .eq('id', messageId) - .select('*') - .single(); - - if (error) { - console.error(`❌ [Moderation] Ошибка обновления сообщения ${messageId}:`, error); - console.error(`❌ [Moderation] Детали ошибки:`, error); - } - } - - - } catch (error) { - const totalTime = Date.now() - moderationStartTime; - console.error(`❌ [Moderation] === ОШИБКА МОДЕРАЦИИ СООБЩЕНИЯ ${messageId} ===`); - console.error(`❌ [Moderation] Время до ошибки: ${totalTime}мс`); - console.error(`❌ [Moderation] Тип ошибки: ${error.name || 'Unknown'}`); - console.error(`❌ [Moderation] Сообщение ошибки: ${error.message || 'Unknown error'}`); - console.error(`❌ [Moderation] Stack trace:`, error.stack); - } - } - - // Получение статистики подключений - getConnectionStats() { - return { - connectedClients: this.connectedClients.size, - activeChats: this.chatParticipants.size, - totalChatParticipants: Array.from(this.chatParticipants.values()) - .reduce((total, participants) => total + participants.size, 0), - totalEventQueues: this.userEventQueues.size, - totalEvents: Array.from(this.userEventQueues.values()) - .reduce((total, queue) => total + queue.length, 0) - }; - } -} - -// Функция для создания роутера с polling эндпоинтами -function createChatPollingRouter(express) { - const router = express.Router(); - const chatHandler = new ChatPollingHandler(); - - // CORS middleware для всех запросов - router.use((req, res, next) => { - res.header('Access-Control-Allow-Origin', '*'); - res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); - res.header('Access-Control-Allow-Headers', 'Content-Type, Cache-Control, Authorization'); - res.header('Access-Control-Allow-Credentials', 'true'); - - // Обрабатываем OPTIONS запросы - if (req.method === 'OPTIONS') { - res.status(200).end(); - return; - } - - next(); - }); - - // Эндпоинт для аутентификации - router.post('/auth', (req, res) => { - chatHandler.handleAuthentication(req, res); - }); - - // Эндпоинт для получения событий (polling) - router.get('/events', (req, res) => { - chatHandler.handleGetEvents(req, res); - }); - - // HTTP эндпоинты для действий - router.post('/join-chat', (req, res) => { - chatHandler.handleJoinChat(req, res); - }); - - router.post('/leave-chat', (req, res) => { - chatHandler.handleLeaveChat(req, res); - }); - - router.post('/send-message', (req, res) => { - chatHandler.handleSendMessage(req, res); - }); - - router.post('/typing-start', (req, res) => { - chatHandler.handleTypingStart(req, res); - }); - - router.post('/typing-stop', (req, res) => { - chatHandler.handleTypingStop(req, res); - }); - - // Эндпоинт для получения онлайн пользователей в чате - router.get('/online-users/:chat_id', (req, res) => { - const { chat_id } = req.params; - const onlineUsers = chatHandler.getOnlineUsersInChat(chat_id); - res.json({ onlineUsers }); - }); - - // Эндпоинт для получения статистики - router.get('/stats', (req, res) => { - const stats = chatHandler.getConnectionStats(); - res.json(stats); - }); - - // Эндпоинт для проверки статуса Supabase подписки - router.get('/supabase-status', (req, res) => { - const isConnected = chatHandler.checkSubscriptionStatus(); - res.json({ - supabaseSubscriptionActive: isConnected, - subscriptionExists: !!chatHandler.realtimeSubscription, - subscriptionInfo: chatHandler.realtimeSubscription ? { - channel: chatHandler.realtimeSubscription.topic, - state: chatHandler.realtimeSubscription.state - } : null - }); - }); - - // Эндпоинт для принудительного переподключения к Supabase - router.post('/reconnect-supabase', (req, res) => { - try { - // Отписываемся от текущей подписки - if (chatHandler.realtimeSubscription) { - chatHandler.realtimeSubscription.unsubscribe(); - chatHandler.realtimeSubscription = null; - } - - // Создаем новую подписку - chatHandler.setupRealtimeSubscription(); - - res.json({ - success: true, - message: 'Reconnection initiated' - }); - } catch (error) { - console.error('❌ [Polling Server] Ошибка переподключения:', error); - res.status(500).json({ - success: false, - error: 'Reconnection failed', - details: error.message - }); - } - }); - - // Тестовый эндпоинт для создания сообщения в обход API - router.post('/test-message', async (req, res) => { - const { chat_id, user_id, text } = req.body; - - if (!chat_id || !user_id || !text) { - res.status(400).json({ error: 'chat_id, user_id и text обязательны' }); - return; - } - - try { - // Создаем тестовое событие напрямую - chatHandler.broadcastToChat(chat_id, 'new_message', { - message: { - id: `test_${Date.now()}`, - chat_id, - user_id, - text, - created_at: new Date().toISOString(), - user_profiles: { - id: user_id, - full_name: 'Test User', - avatar_url: null - } - }, - timestamp: new Date() - }); - - res.json({ - success: true, - message: 'Test message sent to polling clients' - }); - } catch (error) { - console.error('❌ [Polling Server] Ошибка отправки тестового сообщения:', error); - res.status(500).json({ - success: false, - error: 'Failed to send test message', - details: error.message - }); - } - }); - - return { router, chatHandler }; -} - -module.exports = { - ChatPollingHandler, - createChatPollingRouter -}; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/profile.js b/server/routers/kfu-m-24-1/sber_mobile/profile.js deleted file mode 100644 index 529e057..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/profile.js +++ /dev/null @@ -1,119 +0,0 @@ -const router = require('express').Router(); -const { getSupabaseClient } = require('./supabaseClient'); - -// GET /profile -router.get('/profile', async (req, res) => { - const { user_id } = req.query; - const supabase = getSupabaseClient(); - let { data: userData, error: userError } = await supabase.auth.admin.getUserById(user_id); - - if (userError) return res.status(400).json({ error: userError.message }); - - let { data: profileData, error: profileError } = await supabase.from('user_profiles').select(` - id, - full_name, - avatar_url, - updated_at - `).eq('id', user_id).single(); - - if (profileError) return res.status(400).json({ error: profileError.message }); - - // Получаем аватарку из бакета - let avatarUrl = null; - const avatarPath = `avatars/${user_id}.jpg`; - const { data: avatarData } = await supabase.storage.from('sber.mobile').getPublicUrl(avatarPath); - - if (avatarData) { - // Проверяем, существует ли файл - const { data: fileData, error: fileError } = await supabase.storage.from('sber.mobile').list('avatars', { - search: `${user_id}.jpg` - }); - - if (!fileError && fileData && fileData.length > 0) { - avatarUrl = avatarData.publicUrl; - } - } - - res.json({ - id: profileData.id, - username: profileData.full_name, - avatar_url: avatarUrl || profileData.avatar_url, - phone: userData.user.phone, - updated_at: profileData.updated_at - }); -}); - -// POST /profile -router.post('/profile', async (req, res) => { - const { user_id, data } = req.body; - const supabase = getSupabaseClient(); - - const { data: userData, error: userError } = await supabase.auth.admin.updateUserById( - user_id, - { phone: data.phone } - ) - - if (userError) return res.status(400).json({ error: userError.message }); - - let avatarUrl = data.avatar_url; - - // Если передана аватарка в base64, сохраняем в бакет - if (data.avatarBase64) { - try { - // Удаляем старую аватарку - const oldAvatarPath = `avatars/${user_id}.jpg`; - await supabase.storage.from('sber.mobile').remove([oldAvatarPath]); - - // Конвертируем base64 в buffer - const base64Data = data.avatarBase64.replace(/^data:image\/[a-z]+;base64,/, ''); - const buffer = Buffer.from(base64Data, 'base64'); - - // Загружаем новую аватарку - const avatarPath = `avatars/${user_id}.jpg`; - const { error: uploadError } = await supabase.storage - .from('sber.mobile') - .upload(avatarPath, buffer, { - contentType: 'image/jpeg', - upsert: true - }); - - if (uploadError) { - console.error('Ошибка загрузки аватарки:', uploadError); - } else { - // Получаем публичный URL - const { data: urlData } = await supabase.storage - .from('sber.mobile') - .getPublicUrl(avatarPath); - avatarUrl = urlData.publicUrl; - } - } catch (error) { - console.error('Ошибка обработки аватарки:', error); - } - } - - let { error: profileError } = await supabase.from('user_profiles').update({ - full_name: data.username, - avatar_url: avatarUrl, - // apartment: data.apartment - }).eq('id', user_id).single(); - - if (profileError) return res.status(400).json({ error: profileError.message }); - - res.json({ success: true, avatar_url: avatarUrl }); -}); - -// Получить управляющую компанию по квартире -router.get('/management-company', async (req, res) => { - const supabase = getSupabaseClient(); - const { apartment_id } = req.query; - if (!apartment_id) return res.status(400).json({ error: 'apartment_id required' }); - const { data: apartment, error: err1 } = await supabase.from('apartments').select('building_id').eq('id', apartment_id).single(); - if (err1) return res.status(400).json({ error: err1.message }); - const { data: building, error: err2 } = await supabase.from('buildings').select('management_company_id').eq('id', apartment.building_id).single(); - if (err2) return res.status(400).json({ error: err2.message }); - const { data: company, error: err3 } = await supabase.from('management_companies').select('*').eq('id', building.management_company_id).single(); - if (err3) return res.status(400).json({ error: err3.message }); - res.json(company); -}); - -module.exports = router; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/supabaseClient.js b/server/routers/kfu-m-24-1/sber_mobile/supabaseClient.js deleted file mode 100644 index 460d723..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/supabaseClient.js +++ /dev/null @@ -1,79 +0,0 @@ -const router = require('express').Router(); -const { createClient } = require('@supabase/supabase-js'); -const { getSupabaseUrl, getSupabaseKey, getSupabaseServiceKey } = require('./get-constants'); - -let supabase = null; -let initializationPromise = null; - -async function initSupabaseClient() { - - try { - const supabaseUrl = await getSupabaseUrl(); - const supabaseAnonKey = await getSupabaseKey(); - const supabaseServiceRoleKey = await getSupabaseServiceKey(); - - - if (!supabaseUrl || !supabaseServiceRoleKey) { - throw new Error('Missing required Supabase configuration'); - } - - supabase = createClient(supabaseUrl, supabaseServiceRoleKey); - - return supabase; - - } catch (error) { - throw error; - } -} - -function getSupabaseClient() { - if (!supabase) { - throw new Error('Supabase client is not initialized. Call initSupabaseClient first.'); - } - return supabase; -} - -// POST /refresh-supabase-client -router.post('/refresh-supabase-client', async (req, res) => { - try { - await initSupabaseClient(); - res.json({ success: true, message: 'Supabase client refreshed' }); - } catch (error) { - res.status(500).json({ error: error.message }); - } -}); - -// GET /supabase-client-status -router.get('/supabase-client-status', (req, res) => { - - const isInitialized = !!supabase; - - res.json({ - initialized: isInitialized, - clientExists: !!supabase, - timestamp: new Date().toISOString() - }); -}); - -// Инициализация клиента при старте -initializationPromise = (async () => { - try { - await initSupabaseClient(); - } catch (error) { - // Планируем повторную попытку через 5 секунд - setTimeout(async () => { - try { - await initSupabaseClient(); - } catch (retryError) { - console.error('❌ [Supabase Client] Повторная инициализация неудачна:', retryError); - } - }, 5000); - } -})(); - -module.exports = { - getSupabaseClient, - initSupabaseClient, - supabaseRouter: router, - initializationPromise -}; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/create-ticket-tool.ts b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/create-ticket-tool.ts deleted file mode 100644 index ff88787..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/create-ticket-tool.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { StructuredTool, ToolRunnableConfig } from '@langchain/core/tools'; -import { z } from 'zod'; -import { CallbackManagerForToolRun } from '@langchain/core/callbacks/manager'; -import { getSupabaseClient } from '../supabaseClient'; - -export class CreateTicketTool extends StructuredTool { - name = 'create_ticket'; - description = 'Создает заявку в системе. ВАЖНО: используй этот инструмент ТОЛЬКО после получения явного согласия пользователя на создание заявки с конкретным текстом.'; - - schema = z.object({ - title: z.string().describe('Заголовок заявки'), - description: z.string().describe('Подробное описание проблемы'), - category: z.string().describe('Категория заявки (например: ремонт, уборка, техническая_поддержка, жалоба)'), - }); - - private userId: string; - private apartmentId: string; - - constructor(userId: string, apartmentId: string) { - super(); - this.userId = userId; - this.apartmentId = apartmentId; - } - - protected async _call( - arg: z.infer, - runManager?: CallbackManagerForToolRun, - parentConfig?: ToolRunnableConfig> - ): Promise { - try { - if (!this.apartmentId) { - return 'Не удалось определить вашу квартиру. Обратитесь к администратору для создания заявки.'; - } - - const supabase = getSupabaseClient(); - - const { data: ticket, error } = await supabase - .from('tickets') - .insert({ - user_id: this.userId, - apartment_id: this.apartmentId, - title: arg.title, - description: arg.description, - category: arg.category, - status: 'open' - }) - .select() - .single(); - - if (error) { - return 'Произошла ошибка при создании заявки. Попробуйте позже или обратитесь к администратору.'; - } - - return `Заявка успешно создана! -Номер заявки: ${ticket.id} -Заголовок: ${ticket.title} -Статус: Открыта -Дата создания: ${new Date(ticket.created_at).toLocaleString('ru-RU')} - -Ваша заявка принята в работу. Мы свяжемся с вами в ближайшее время.`; - - } catch (error) { - return 'Произошла техническая ошибка при создании заявки. Пожалуйста, попробуйте позже.'; - } - } -} \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/gigachat.ts b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/gigachat.ts deleted file mode 100644 index ab98ebb..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/gigachat.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Agent } from 'node:https'; -import { GigaChat } from 'langchain-gigachat'; -import { getGigaAuth } from '../get-constants'; - -const httpsAgent = new Agent({ - rejectUnauthorized: false, -}); - -// Получаем GIGA_AUTH из переменной окружения (устанавливается в get-constants.js) -export const gigachat = (GIGA_AUTH) => - new GigaChat({ - model: 'GigaChat-2', - temperature: 0.7, - scope: 'GIGACHAT_API_PERS', - streaming: false, - credentials: GIGA_AUTH, - httpsAgent - }); - -export default gigachat; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/knowledge-base-tool.ts b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/knowledge-base-tool.ts deleted file mode 100644 index 84a681d..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/knowledge-base-tool.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { StructuredTool, ToolRunnableConfig } from '@langchain/core/tools'; -import { z } from 'zod'; -import { CallbackManagerForToolRun } from '@langchain/core/callbacks/manager'; -import { getVectorStore } from './vector-store'; - -export class KnowledgeBaseTool extends StructuredTool { - name = 'search_knowledge_base'; - description = 'Ищет информацию в базе знаний компании о процессах, оплатах, подаче заявок, правилах и документах УК. Используй этот инструмент для вопросов, требующих специфических знаний о компании.'; - - schema = z.object({ - query: z.string().describe('Поисковый запрос для поиска в базе знаний'), - }); - - protected async _call( - arg: z.infer, - runManager?: CallbackManagerForToolRun, - parentConfig?: ToolRunnableConfig> - ): Promise { - try { - const vectorStore = getVectorStore(); - const retriever = vectorStore.asRetriever({ - k: 5 - }); - - const relevantDocs = await retriever.getRelevantDocuments(arg.query); - - if (!relevantDocs || relevantDocs.length === 0) { - return 'В базе знаний не найдено информации по данному запросу. Возможно, стоит переформулировать вопрос или обратиться к специалисту.'; - } - - const formattedDocs = relevantDocs.map((doc, index) => { - return `Документ ${index + 1}:\n${doc.pageContent}\n`; - }).join('\n---\n'); - - return `Найдена следующая информация в базе знаний компании:\n\n${formattedDocs}\n\nИспользуй эту информацию для ответа на вопрос пользователя.`; - - } catch (error) { - return 'Произошла ошибка при поиске в базе знаний. Попробуйте переформулировать запрос.'; - } - } -} \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts deleted file mode 100644 index e8c5c68..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { HumanMessage, AIMessage, SystemMessage, BaseMessage } from '@langchain/core/messages'; -import { ChatPromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts'; -import { createReactAgent } from '@langchain/langgraph/prebuilt'; -import { MemorySaver } from '@langchain/langgraph'; -import gigachat from './gigachat'; -import { SupportContextTool } from './support-context-tool'; -import { KnowledgeBaseTool } from './knowledge-base-tool'; -import { CreateTicketTool } from './create-ticket-tool'; - -export interface SupportAgentConfig { - temperature?: number; - threadId?: string; - GIGA_AUTH?: string; -} - -export interface SupportResponse { - content: string; - success: boolean; - error?: string; -} - -export class SupportAgent { - private llm: any; - private memorySaver: MemorySaver; - private agent: any; - private systemPrompt: string; - private threadId: string; - private isFirstMessage: boolean; - private userId: string; - - constructor(config: SupportAgentConfig = {}) { - this.systemPrompt = this.getDefaultSystemPrompt(); - this.threadId = config.threadId || 'default'; - this.userId = this.threadId; - this.memorySaver = new MemorySaver(); - this.isFirstMessage = true; - - this.llm = gigachat(config.GIGA_AUTH); - if (config.temperature !== undefined) { - this.llm.temperature = config.temperature; - } - - const tools = [ - new SupportContextTool(this.userId), - new KnowledgeBaseTool() - ]; - - this.agent = createReactAgent({ - llm: this.llm, - tools: tools, - checkpointSaver: this.memorySaver - }); - } - - private getDefaultSystemPrompt(): string { - return `Ты - профессиональный агент службы поддержки управляющей компании. - -ОСНОВНЫЕ ПРИНЦИПЫ: -- Помогай только с реальными проблемами и вопросами, связанными с ЖКХ, управляющей компанией и приложением -- Будь вежливым, профессиональным и по существу -- Если вопрос неуместен, не связан с твоими обязанностями или является развлекательным - вежливо откажись и перенаправь к основным темам - -ДОСТУПНЫЕ ИНСТРУМЕНТЫ: - -1. get_support_context - получает историю сообщений пользователя - ВСЕГДА используй ПЕРВЫМ при каждом новом сообщении - -2. search_knowledge_base - поиск в базе знаний компании - Используй ТОЛЬКО для серьезных вопросов о: - - Процессах оплаты ЖКХ и тарифах - - Подаче заявок и документообороте - - Правилах и регламентах УК - - Технических вопросах приложения - - Процедурах и инструкциях компании - -3. create_ticket - создание заявки в системе - Используй ТОЛЬКО когда: - - Пользователь сообщает о реальной проблеме (поломка, неисправность, жалоба) - - Проблема требует вмешательства УК или технических служб - - ОБЯЗАТЕЛЬНО сначала покажи пользователю полный текст заявки - - Получи ЯВНОЕ согласие пользователя перед созданием - - НЕ создавай заявки для консультационных вопросов - -ПРАВИЛА ИСПОЛЬЗОВАНИЯ ИНСТРУМЕНТОВ: -- НЕ используй search_knowledge_base и create_ticket для: - * Общих вопросов и болтовни - * Развлекательных запросов - * Вопросов не по теме ЖКХ/УК - * Простых консультаций, которые можно решить обычным ответом - -АЛГОРИТМ РАБОТЫ: -1. Получи контекст истории сообщений -2. Определи, является ли вопрос уместным и серьезным -3. Если нужна специфическая информация - найди в базе знаний -4. Если нужно создать заявку - покажи текст и получи согласие -5. Дай полный и полезный ответ - -Всегда отвечай на русском языке и фокусируйся на помощи с реальными проблемами ЖКХ.`; - } - - public async processMessage(userMessage: string, apartmentId?: string): Promise { - try { - const messages: BaseMessage[] = []; - - if (this.isFirstMessage) { - messages.push(new SystemMessage(this.systemPrompt)); - this.isFirstMessage = false; - } - - messages.push(new HumanMessage(userMessage)); - - // Создаем инструменты с актуальным apartmentId - const tools = [ - new SupportContextTool(this.userId), - new KnowledgeBaseTool(), - new CreateTicketTool(this.userId, apartmentId || '') - ]; - - // Пересоздаем агента с обновленными инструментами - const tempAgent = createReactAgent({ - llm: this.llm, - tools: tools, - checkpointSaver: this.memorySaver - }); - - const response = await tempAgent.invoke({ - messages: messages - }, { - configurable: { - thread_id: this.threadId - } - }); - - const lastMessage = response.messages[response.messages.length - 1]; - - return { - content: typeof lastMessage.content === 'string' ? lastMessage.content : 'Извините, не удалось сформировать ответ.', - success: true - }; - - } catch (error) { - console.error('Ошибка при обработке сообщения:', error); - return { - content: 'Извините, произошла ошибка при обработке вашего запроса. Попробуйте позже.', - success: false, - error: error instanceof Error ? error.message : 'Неизвестная ошибка' - }; - } - } - - public async clearHistory(): Promise { - this.memorySaver = new MemorySaver(); - - const tools = [ - new SupportContextTool(this.userId), - new KnowledgeBaseTool() - ]; - - this.agent = createReactAgent({ - llm: this.llm, - tools: tools, - checkpointSaver: this.memorySaver - }); - - this.isFirstMessage = true; - } -} \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-context-tool.ts b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-context-tool.ts deleted file mode 100644 index 94a573c..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-context-tool.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { StructuredTool, ToolRunnableConfig } from '@langchain/core/tools'; -import { z } from 'zod'; -import { CallbackManagerForToolRun } from '@langchain/core/callbacks/manager'; -import { getSupabaseClient } from '../supabaseClient'; - -export class SupportContextTool extends StructuredTool { - name = 'get_support_context'; - description = 'Получает последние 10 сообщений из истории поддержки для понимания контекста разговора. Используй этот инструмент в начале разговора.'; - - schema = z.object({}); - - private userId: string; - - constructor(userId: string) { - super(); - this.userId = userId; - } - - protected async _call( - arg: z.infer, - runManager?: CallbackManagerForToolRun, - parentConfig?: ToolRunnableConfig> - ): Promise { - try { - const supabase = getSupabaseClient(); - - const { data: messages, error } = await supabase - .from('support') - .select('message, is_from_user, created_at') - .eq('user_id', this.userId) - .order('created_at', { ascending: false }) - .limit(10); - - if (error) { - return 'Не удалось получить историю сообщений.'; - } - - if (!messages || messages.length === 0) { - return 'История сообщений поддержки пуста. Это первое обращение пользователя.'; - } - - const chronologicalMessages = messages.reverse(); - - const contextMessages = chronologicalMessages.map((msg, index) => { - const role = msg.is_from_user ? 'Пользователь' : 'Агент поддержки'; - const time = new Date(msg.created_at).toLocaleString('ru-RU'); - return `${index + 1}. [${time}] ${role}: ${msg.message}`; - }).join('\n'); - - return `Последние сообщения из истории поддержки (${messages.length} сообщений):\n\n${contextMessages}\n\nИспользуй этот контекст для понимания предыдущих обращений пользователя и предоставления более точных ответов.`; - - } catch (error) { - return 'Произошла ошибка при получении истории сообщений.'; - } - } -} \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/vector-store.ts b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/vector-store.ts deleted file mode 100644 index 3841672..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/vector-store.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { createClient } from '@supabase/supabase-js'; -import { SupabaseVectorStore } from '@langchain/community/vectorstores/supabase'; -import { GigaChatEmbeddings } from 'langchain-gigachat'; -import { Agent } from 'node:https'; - -const httpsAgent = new Agent({ - rejectUnauthorized: false, -}); - -let vectorStoreInstance: SupabaseVectorStore | null = null; - -export function getVectorStore(): SupabaseVectorStore { - if (!vectorStoreInstance) { - const client = createClient( - process.env.RAG_SUPABASE_URL!, - process.env.RAG_SUPABASE_SERVICE_ROLE_KEY!, - ); - - vectorStoreInstance = new SupabaseVectorStore( - new GigaChatEmbeddings({ - credentials: process.env.GIGA_AUTH, - httpsAgent, - }), - { - client, - tableName: 'slon', - queryName: 'match_slon' - } - ); - } - - return vectorStoreInstance; -} \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/supportApi.js b/server/routers/kfu-m-24-1/sber_mobile/supportApi.js deleted file mode 100644 index afb4ea8..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/supportApi.js +++ /dev/null @@ -1,151 +0,0 @@ -const router = require('express').Router(); -const { getSupabaseClient } = require('./supabaseClient'); -const { getGigaAuth } = require('./get-constants'); -const { SupportAgent } = require('./support-ai-agent/support-agent'); - -// Хранилище агентов для разных пользователей -const userAgents = new Map(); - -/** - * Получить или создать агента для пользователя - */ -async function getUserAgent(userId) { - if (!userAgents.has(userId)) { - const GIGA_AUTH = await getGigaAuth(); - const config = { - threadId: userId, - temperature: 0.7, - GIGA_AUTH - }; - userAgents.set(userId, new SupportAgent(config)); - } - return userAgents.get(userId); -} - -// GET /api/support - Получить историю сообщений пользователя -router.get('/support', async (req, res) => { - const supabase = getSupabaseClient(); - const { user_id } = req.query; - - if (!user_id) { - return res.status(400).json({ error: 'user_id обязателен' }); - } - - try { - // Получаем все сообщения пользователя из базы данных - const { data: messages, error } = await supabase - .from('support') - .select('*') - .eq('user_id', user_id) - .order('created_at', { ascending: true }); - - if (error) { - return res.status(400).json({ error: error.message }); - } - - res.json({ - messages: messages || [], - success: true - }); - - } catch (error) { - console.error('Ошибка в GET /support:', error); - res.status(500).json({ - error: 'Внутренняя ошибка сервера', - success: false - }); - } -}); - -// POST /api/support -router.post('/support', async (req, res) => { - const supabase = getSupabaseClient(); - const { user_id, message, apartment_id } = req.body; - - if (!user_id || !message) { - return res.status(400).json({ error: 'user_id и message обязательны' }); - } - - try { - // Сохраняем сообщение пользователя в базу данных - const { error: insertError } = await supabase - .from('support') - .insert({ user_id, message, is_from_user: true }); - - if (insertError) { - return res.status(400).json({ error: insertError.message }); - } - - // Получаем агента для пользователя - const agent = await getUserAgent(user_id); - - // Получаем ответ от AI-агента, передавая apartment_id - const aiResponse = await agent.processMessage(message, apartment_id); - - if (!aiResponse.success) { - console.error('Ошибка AI-агента:', aiResponse.error); - return res.status(500).json({ - error: 'Ошибка при генерации ответа', - reply: 'Извините, произошла ошибка. Попробуйте позже.' - }); - } - - // Сохраняем ответ агента в базу данных - const { error: responseError } = await supabase - .from('support') - .insert({ - user_id, - message: aiResponse.content, - is_from_user: false - }); - - if (responseError) { - console.error('Ошибка сохранения ответа:', responseError); - // Не возвращаем ошибку пользователю, так как ответ уже сгенерирован - } - - // Возвращаем ответ пользователю - res.json({ - reply: aiResponse.content, - success: true - }); - - } catch (error) { - console.error('Ошибка в supportApi:', error); - res.status(500).json({ - error: 'Внутренняя ошибка сервера', - reply: 'Извините, произошла ошибка. Попробуйте позже.' - }); - } -}); - -// DELETE /api/support/history/:userId - Очистка истории диалога -router.delete('/support/history/:userId', async (req, res) => { - const { userId } = req.params; - - try { - if (userAgents.has(userId)) { - const agent = userAgents.get(userId); - await agent.clearHistory(); - - res.json({ - message: 'История диалога очищена', - success: true - }); - } else { - res.json({ - message: 'Агент для данного пользователя не найден', - success: true - }); - } - - } catch (error) { - console.error('Ошибка в /support/history:', error); - res.status(500).json({ - error: 'Внутренняя ошибка сервера', - success: false - }); - } -}); - -module.exports = router; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/tickets.js b/server/routers/kfu-m-24-1/sber_mobile/tickets.js deleted file mode 100644 index bfeceb8..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/tickets.js +++ /dev/null @@ -1,31 +0,0 @@ -const router = require('express').Router(); -const { getSupabaseClient } = require('./supabaseClient'); - -// Получить заявки пользователя по квартире -router.get('/tickets', async (req, res) => { - const supabase = getSupabaseClient(); - const { user_id, apartment_id } = req.query; - - if (!user_id || !apartment_id) { - return res.status(400).json({ error: 'Требуется user_id и apartment_id' }); - } - - try { - const { data, error } = await supabase - .from('tickets') - .select('*') - .eq('user_id', user_id) - .eq('apartment_id', apartment_id) - .order('created_at', { ascending: false }); - - if (error) { - return res.status(400).json({ error: error.message }); - } - - res.json(data || []); - } catch (err) { - res.status(500).json({ error: 'Внутренняя ошибка сервера' }); - } -}); - -module.exports = router; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/user_apartments.js b/server/routers/kfu-m-24-1/sber_mobile/user_apartments.js deleted file mode 100644 index e5421ba..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/user_apartments.js +++ /dev/null @@ -1,18 +0,0 @@ -const router = require('express').Router(); -const { getSupabaseClient } = require('./supabaseClient'); - -// Получить все квартиры пользователя -router.get('/user-apartments', async (req, res) => { - const supabase = getSupabaseClient(); - const { user_id } = req.query; - if (!user_id) return res.status(400).json({ error: 'user_id required' }); - const { data: links, error: err1 } = await supabase.from('apartment_residents').select('apartment_id').eq('user_id', user_id); - if (err1) return res.status(400).json({ error: err1.message }); - const apartmentIds = links.map(l => l.apartment_id); - if (!apartmentIds.length) return res.json([]); - const { data, error } = await supabase.from('apartments').select('*').in('id', apartmentIds); - if (error) return res.status(400).json({ error: error.message }); - res.json(data); -}); - -module.exports = router; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/utility_payments.js b/server/routers/kfu-m-24-1/sber_mobile/utility_payments.js deleted file mode 100644 index b5082b6..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/utility_payments.js +++ /dev/null @@ -1,50 +0,0 @@ -const router = require('express').Router(); -const { getSupabaseClient } = require('./supabaseClient'); - -// Получить платежки с деталями для квартиры -router.get('/payment-services', async (req, res) => { - const supabase = getSupabaseClient(); - const { apartment_id } = req.query; - if (!apartment_id) return res.status(400).json({ error: 'apartment_id обязателен' }); - - // Получаем все платежки по квартире - const { data: services, error: servicesError } = await supabase - .from('payment_services') - .select('id, name, icon, amount, is_paid, payment_method') - .eq('apartment_id', apartment_id); - if (servicesError) return res.status(400).json({ error: servicesError.message }); - - // Получаем детализацию по всем платежкам - const serviceIds = services.map(s => s.id); - let details = []; - if (serviceIds.length > 0) { - const { data: detailsData, error: detailsError } = await supabase - .from('payment_service_details') - .select('id, payment_service_id, name, amount') - .in('payment_service_id', serviceIds); - if (detailsError) return res.status(400).json({ error: detailsError.message }); - details = detailsData; - } - - // Формируем структуру для фронта - const result = services.map(service => { - const serviceDetails = details.filter(d => d.payment_service_id === service.id).map(detail => ({ - id: detail.id, - name: detail.name, - amount: detail.amount - })); - return { - id: service.id, - title: service.name, - icon: service.icon, - amount: service.amount, - isPaid: service.is_paid, - paymentMethod: service.payment_method, - details: serviceDetails - }; - }); - - res.json(result); -}); - -module.exports = router; \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/votes.js b/server/routers/kfu-m-24-1/sber_mobile/votes.js deleted file mode 100644 index 9a861da..0000000 --- a/server/routers/kfu-m-24-1/sber_mobile/votes.js +++ /dev/null @@ -1,105 +0,0 @@ -const router = require('express').Router(); -const { getSupabaseClient } = require('./supabaseClient'); - -// Получить все голоса по инициативе -router.get('/votes/:initiative_id', async (req, res) => { - const supabase = getSupabaseClient(); - const { initiative_id } = req.params; - const { data, error } = await supabase.from('votes').select('*').eq('initiative_id', initiative_id); - if (error) - return res.status(400).json({ error: error.message }); - res.json(data); -}); - -// Получить голос пользователя по инициативе -router.get('/votes/:initiative_id/user/:user_id', async (req, res) => { - const supabase = getSupabaseClient(); - const { initiative_id, user_id } = req.params; - const { data, error } = await supabase.from('votes').select('*').eq('initiative_id', initiative_id).eq('user_id', user_id).single(); - if (error) { - console.log(error, '/votes/:initiative_id/:user_id') - console.log(initiative_id, user_id) - return res.status(400).json({ error: error.message }); - } - res.json(data); -}); - -// Получить статистику голосов по инициативе -router.get('/votes/stats/:initiative_id', async (req, res) => { - const supabase = getSupabaseClient(); - const { initiative_id } = req.params; - - const { data, error } = await supabase - .from('votes') - .select('vote_type') - .eq('initiative_id', initiative_id); - console.log(data, error) - if (error) { - console.log('/votes/:initiative_id/stats') - res.status(400).json({ error: error.message }); - } - const stats = { - for: data.filter(vote => vote.vote_type === 'for').length, - against: data.filter(vote => vote.vote_type === 'against').length, - total: data.length - }; - - res.json(stats); -}); - -// Проголосовать (создать, обновить или удалить голос) -router.post('/votes', async (req, res) => { - const supabase = getSupabaseClient(); - const { initiative_id, user_id, vote_type } = req.body; - - // Проверяем существующий голос - const { data: existingVote, error: checkError } = await supabase - .from('votes') - .select('*') - .eq('initiative_id', initiative_id) - .eq('user_id', user_id) - .single(); - - if (checkError && checkError.code !== 'PGRST116') { - console.log('1/votes') - return res.status(400).json({ error: checkError.message }); - } - - if (existingVote) { - if (existingVote.vote_type === vote_type) { - // Если нажали тот же тип голоса - УДАЛЯЕМ (отменяем голос) - const { error: deleteError } = await supabase - .from('votes') - .delete() - .eq('initiative_id', initiative_id) - .eq('user_id', user_id); - - if (deleteError) return res.status(400).json({ error: deleteError.message }); - res.json({ message: 'Vote removed', action: 'removed', previous_vote: existingVote.vote_type }); - } else { - // Если нажали другой тип голоса - ОБНОВЛЯЕМ - const { data, error } = await supabase - .from('votes') - .update({ vote_type }) - .eq('initiative_id', initiative_id) - .eq('user_id', user_id) - .select() - .single(); - - if (error) return res.status(400).json({ error: error.message }); - res.json({ ...data, action: 'updated', previous_vote: existingVote.vote_type }); - } - } else { - // Если голоса нет - СОЗДАЕМ новый - const { data, error } = await supabase - .from('votes') - .insert([{ initiative_id, user_id, vote_type }]) - .select() - .single(); - - if (error) return res.status(400).json({ error: error.message }); - res.json({ ...data, action: 'created' }); - } -}); - -module.exports = router; \ No newline at end of file