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