From babce352310efe0d6d5a4ca89571a8ba93897145 Mon Sep 17 00:00:00 2001 From: desartstudio95 Date: Wed, 20 May 2026 03:26:54 -0700 Subject: [PATCH] feat: Integrate OneSignal and add AI strategy features Adds OneSignal for push notifications and new AI strategy parameters like trailing stop, winrate learning, and multi-time frame analysis. Also updates build scripts and dependencies. --- .env.example | 10 + README.md | 2 +- index.html | 1 + package-lock.json | 1302 ++++++++++++++++++++++++--- package.json | 10 +- server.ts | 103 +++ src/App.tsx | 27 +- src/components/AdminDashboard.tsx | 137 ++- src/components/AnalysisView.tsx | 179 +++- src/components/DashboardStats.tsx | 39 +- src/components/LandingPage.tsx | 44 +- src/components/LiveMarketTicker.tsx | 71 ++ src/components/PlansView.tsx | 22 +- src/components/ProfileView.tsx | 157 +++- src/components/RiskCalculator.tsx | 99 ++ src/components/SignalHistory.tsx | 285 +++++- src/components/TradingChart.tsx | 132 +++ src/services/geminiService.ts | 20 +- src/services/marketData.ts | 329 +++++++ src/services/notifications.ts | 60 ++ src/types.ts | 3 + 21 files changed, 2795 insertions(+), 237 deletions(-) create mode 100644 server.ts create mode 100644 src/components/LiveMarketTicker.tsx create mode 100644 src/components/RiskCalculator.tsx create mode 100644 src/components/TradingChart.tsx create mode 100644 src/services/marketData.ts create mode 100644 src/services/notifications.ts diff --git a/.env.example b/.env.example index 7a550fe..aa4ef2f 100644 --- a/.env.example +++ b/.env.example @@ -7,3 +7,13 @@ GEMINI_API_KEY="MY_GEMINI_API_KEY" # AI Studio automatically injects this at runtime with the Cloud Run service URL. # Used for self-referential links, OAuth callbacks, and API endpoints. APP_URL="MY_APP_URL" + +# Twelve Data API +TWELVE_DATA_API_KEY= + +# OneSignal Frontend Config +VITE_ONESIGNAL_APP_ID= + +# OneSignal Backend Config +ONESIGNAL_APP_ID= +ONESIGNAL_REST_API_KEY= diff --git a/README.md b/README.md index 57b5c5d..48d1578 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@
-GHBanner +GHBanner
# Run and deploy your AI Studio app diff --git a/index.html b/index.html index 21dfe69..3863038 100644 --- a/index.html +++ b/index.html @@ -4,6 +4,7 @@ My Google AI Studio App +
diff --git a/package-lock.json b/package-lock.json index dd8504c..7613679 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@google/genai": "^1.29.0", "@tailwindcss/vite": "^4.1.14", "@vitejs/plugin-react": "^5.0.4", + "axios": "^1.16.1", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "dotenv": "^17.2.3", @@ -20,11 +21,14 @@ "embla-carousel-react": "^8.6.0", "express": "^4.21.2", "firebase": "^12.12.1", + "lightweight-charts": "^5.2.0", "lucide-react": "^0.546.0", "motion": "^12.23.24", + "onesignal-node": "^3.4.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-dropzone": "^15.0.0", + "react-onesignal": "^3.5.2", "recharts": "^3.8.1", "shadcn": "^4.7.0", "tailwind-merge": "^3.5.0", @@ -36,6 +40,7 @@ "@types/express": "^4.17.21", "@types/node": "^22.14.0", "autoprefixer": "^10.4.21", + "esbuild": "^0.28.0", "eslint": "^10.2.1", "tailwindcss": "^4.1.14", "ts-node": "^10.9.2", @@ -742,12 +747,13 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", - "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", + "integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==", "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -758,12 +764,13 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", - "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", + "integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -774,12 +781,13 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", - "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", + "integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -790,12 +798,13 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", - "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", + "integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -806,12 +815,13 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", - "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", + "integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -822,12 +832,13 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", - "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", + "integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -838,12 +849,13 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", - "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", + "integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -854,12 +866,13 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", - "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", + "integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -870,12 +883,13 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", - "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", + "integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -886,12 +900,13 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", - "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", + "integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -902,12 +917,13 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", - "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", + "integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -918,12 +934,13 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", - "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", + "integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -934,12 +951,13 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", - "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", + "integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", "cpu": [ "mips64el" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -950,12 +968,13 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", - "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", + "integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -966,12 +985,13 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", - "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", + "integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -982,12 +1002,13 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", - "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", + "integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -998,12 +1019,13 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", - "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", + "integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1014,12 +1036,13 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", - "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", + "integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1030,12 +1053,13 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", - "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", + "integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1046,12 +1070,13 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", - "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", + "integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1062,12 +1087,13 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", - "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", + "integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1078,12 +1104,13 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", - "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", + "integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1094,12 +1121,13 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", - "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", + "integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1110,12 +1138,13 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", - "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", + "integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1126,12 +1155,13 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", - "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", + "integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1142,12 +1172,13 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", - "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", + "integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3752,7 +3783,6 @@ "version": "6.15.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", - "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -3847,6 +3877,24 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, "node_modules/ast-types": { "version": "0.16.1", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", @@ -3859,6 +3907,12 @@ "node": ">=4" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/attr-accept": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz", @@ -3905,6 +3959,58 @@ "postcss": "^8.1.0" } }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", + "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/axios/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/axios/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", @@ -3946,6 +4052,15 @@ "node": ">=6.0.0" } }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, "node_modules/bignumber.js": { "version": "9.3.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", @@ -3955,6 +4070,12 @@ "node": "*" } }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "license": "MIT" + }, "node_modules/body-parser": { "version": "1.20.5", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", @@ -4154,6 +4275,12 @@ ], "license": "CC-BY-4.0" }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "license": "Apache-2.0" + }, "node_modules/chalk": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", @@ -4261,6 +4388,18 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "14.0.3", "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", @@ -4312,6 +4451,12 @@ "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", "license": "MIT" }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "license": "MIT" + }, "node_modules/cors": { "version": "2.8.6", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", @@ -4509,6 +4654,18 @@ "node": ">=12" } }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", @@ -4611,6 +4768,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -4675,6 +4841,16 @@ "node": ">= 0.4" } }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "license": "MIT", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -4826,6 +5002,21 @@ "node": ">= 0.4" } }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-toolkit": { "version": "1.46.0", "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.46.0.tgz", @@ -4837,10 +5028,10 @@ ] }, "node_modules/esbuild": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", - "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", - "devOptional": true, + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", + "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -4850,32 +5041,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.7", - "@esbuild/android-arm": "0.27.7", - "@esbuild/android-arm64": "0.27.7", - "@esbuild/android-x64": "0.27.7", - "@esbuild/darwin-arm64": "0.27.7", - "@esbuild/darwin-x64": "0.27.7", - "@esbuild/freebsd-arm64": "0.27.7", - "@esbuild/freebsd-x64": "0.27.7", - "@esbuild/linux-arm": "0.27.7", - "@esbuild/linux-arm64": "0.27.7", - "@esbuild/linux-ia32": "0.27.7", - "@esbuild/linux-loong64": "0.27.7", - "@esbuild/linux-mips64el": "0.27.7", - "@esbuild/linux-ppc64": "0.27.7", - "@esbuild/linux-riscv64": "0.27.7", - "@esbuild/linux-s390x": "0.27.7", - "@esbuild/linux-x64": "0.27.7", - "@esbuild/netbsd-arm64": "0.27.7", - "@esbuild/netbsd-x64": "0.27.7", - "@esbuild/openbsd-arm64": "0.27.7", - "@esbuild/openbsd-x64": "0.27.7", - "@esbuild/openharmony-arm64": "0.27.7", - "@esbuild/sunos-x64": "0.27.7", - "@esbuild/win32-arm64": "0.27.7", - "@esbuild/win32-ia32": "0.27.7", - "@esbuild/win32-x64": "0.27.7" + "@esbuild/aix-ppc64": "0.28.0", + "@esbuild/android-arm": "0.28.0", + "@esbuild/android-arm64": "0.28.0", + "@esbuild/android-x64": "0.28.0", + "@esbuild/darwin-arm64": "0.28.0", + "@esbuild/darwin-x64": "0.28.0", + "@esbuild/freebsd-arm64": "0.28.0", + "@esbuild/freebsd-x64": "0.28.0", + "@esbuild/linux-arm": "0.28.0", + "@esbuild/linux-arm64": "0.28.0", + "@esbuild/linux-ia32": "0.28.0", + "@esbuild/linux-loong64": "0.28.0", + "@esbuild/linux-mips64el": "0.28.0", + "@esbuild/linux-ppc64": "0.28.0", + "@esbuild/linux-riscv64": "0.28.0", + "@esbuild/linux-s390x": "0.28.0", + "@esbuild/linux-x64": "0.28.0", + "@esbuild/netbsd-arm64": "0.28.0", + "@esbuild/netbsd-x64": "0.28.0", + "@esbuild/openbsd-arm64": "0.28.0", + "@esbuild/openbsd-x64": "0.28.0", + "@esbuild/openharmony-arm64": "0.28.0", + "@esbuild/sunos-x64": "0.28.0", + "@esbuild/win32-arm64": "0.28.0", + "@esbuild/win32-ia32": "0.28.0", + "@esbuild/win32-x64": "0.28.0" } }, "node_modules/escalade": { @@ -5244,6 +5435,21 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "license": "MIT" }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, + "node_modules/fancy-canvas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fancy-canvas/-/fancy-canvas-2.1.0.tgz", + "integrity": "sha512-nifxXJ95JNLFR2NgRV4/MxVP45G9909wJTEKz5fg/TZS20JJZA6hfgRVh/bC9bwl2zBtBNcYPjiBE4njQHVBwQ==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5282,7 +5488,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { @@ -5552,6 +5757,51 @@ "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", @@ -5802,6 +6052,15 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -5868,6 +6127,29 @@ "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "license": "MIT", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -5880,6 +6162,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hasown": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", @@ -5937,6 +6234,21 @@ "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", "license": "MIT" }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, "node_modules/https-proxy-agent": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", @@ -6217,6 +6529,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "license": "MIT" + }, "node_modules/is-unicode-supported": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", @@ -6250,6 +6568,12 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "license": "MIT" + }, "node_modules/jiti": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", @@ -6286,6 +6610,12 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "license": "MIT" + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -6320,11 +6650,16 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "license": "MIT" }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, "license": "MIT" }, "node_modules/json-schema-typed": { @@ -6340,6 +6675,12 @@ "dev": true, "license": "MIT" }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "license": "ISC" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -6364,6 +6705,21 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "license": "MIT", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/jwa": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", @@ -6667,6 +7023,15 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lightweight-charts": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/lightweight-charts/-/lightweight-charts-5.2.0.tgz", + "integrity": "sha512-ey3Vas8UhV06ni+LT9TA1nEe4y8So4Mi6CL/oarNHFMyTktz/xy8e8+oh04Q//eO3t6etvFXgayz2fClyFQb5w==", + "license": "Apache-2.0", + "dependencies": { + "fancy-canvas": "2.1.0" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -6689,6 +7054,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", @@ -7154,6 +7525,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -7205,6 +7585,19 @@ "wrappy": "1" } }, + "node_modules/onesignal-node": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/onesignal-node/-/onesignal-node-3.4.0.tgz", + "integrity": "sha512-9dNpfU5Xp6VhJLkdZT4kVqmOaU36RJOgp+6REQHyv+hLOcgqqa4/FRXxuHbjRCE51x9BK4jIC/gn2Mnw0gQgFQ==", + "license": "MIT", + "dependencies": { + "request": "^2.88.2", + "request-promise": "^4.2.6" + }, + "engines": { + "node": ">=8.13.0" + } + }, "node_modules/onetime": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", @@ -7464,6 +7857,12 @@ "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", "license": "MIT" }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -7652,11 +8051,31 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -7766,6 +8185,15 @@ "license": "MIT", "peer": true }, + "node_modules/react-onesignal": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/react-onesignal/-/react-onesignal-3.5.2.tgz", + "integrity": "sha512-GPSamymIwcMzZdVodz1UKM+jhhqJGrEJk0Hg7M2Oo6mZ2/Z2YX535EYO3pgYgdmZU/8EMfXcVsTa77aPjwywbA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/react-redux": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", @@ -7859,6 +8287,121 @@ "redux": "^5.0.0" } }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "license": "Apache-2.0", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request-promise": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.6.tgz", + "integrity": "sha512-HCHI3DJJUakkOr8fNoCc73E5nU5bqITjOYFMDrKHYOXWXrgD/SBaC7LjwuPymUprRyuF06UK7hd/lMHkmUXglQ==", + "deprecated": "request-promise has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", + "license": "ISC", + "dependencies": { + "bluebird": "^3.5.0", + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "request": "^2.34" + } + }, + "node_modules/request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "license": "ISC", + "dependencies": { + "lodash": "^4.17.19" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "request": "^2.34" + } + }, + "node_modules/request-promise/node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/request/node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/request/node_modules/qs": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.5.tgz", + "integrity": "sha512-mzR4sElr1bfCaPJe7m8ilJ6ZXdDaGoObcYR0ZHSsktM/Lt21MVHj5De30GQH2eiZ1qGRTO7LCAzQsUeXTNexWQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/request/node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -8338,6 +8881,31 @@ "node": ">=0.10.0" } }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "license": "MIT", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -8359,6 +8927,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==", + "license": "ISC", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/strict-event-emitter": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", @@ -8637,6 +9214,476 @@ "fsevents": "~2.3.3" } }, + "node_modules/tsx/node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "devOptional": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/tw-animate-css": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz", @@ -8646,6 +9693,12 @@ "url": "https://github.com/sponsors/Wombosvideo" } }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "license": "Unlicense" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -8780,7 +9833,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" @@ -8810,6 +9862,16 @@ "node": ">= 0.4.0" } }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "license": "MIT", + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -8835,6 +9897,20 @@ "node": ">= 0.8" } }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, "node_modules/victory-vendor": { "version": "37.3.6", "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", diff --git a/package.json b/package.json index 499d70b..dea2bc5 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,9 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite --port=3000 --host=0.0.0.0", - "build": "vite build", + "dev": "tsx server.ts", + "build": "vite build && esbuild server.ts --bundle --platform=node --format=cjs --packages=external --sourcemap --outfile=dist/server.cjs", + "start": "node dist/server.cjs", "preview": "vite preview", "clean": "rm -rf dist", "lint": "tsc --noEmit" @@ -16,6 +17,7 @@ "@google/genai": "^1.29.0", "@tailwindcss/vite": "^4.1.14", "@vitejs/plugin-react": "^5.0.4", + "axios": "^1.16.1", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "dotenv": "^17.2.3", @@ -23,11 +25,14 @@ "embla-carousel-react": "^8.6.0", "express": "^4.21.2", "firebase": "^12.12.1", + "lightweight-charts": "^5.2.0", "lucide-react": "^0.546.0", "motion": "^12.23.24", + "onesignal-node": "^3.4.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-dropzone": "^15.0.0", + "react-onesignal": "^3.5.2", "recharts": "^3.8.1", "shadcn": "^4.7.0", "tailwind-merge": "^3.5.0", @@ -39,6 +44,7 @@ "@types/express": "^4.17.21", "@types/node": "^22.14.0", "autoprefixer": "^10.4.21", + "esbuild": "^0.28.0", "eslint": "^10.2.1", "tailwindcss": "^4.1.14", "ts-node": "^10.9.2", diff --git a/server.ts b/server.ts new file mode 100644 index 0000000..dc85d19 --- /dev/null +++ b/server.ts @@ -0,0 +1,103 @@ +import express from "express"; +import path from "path"; +import axios from "axios"; +import { createServer as createViteServer } from "vite"; +import * as OneSignal from 'onesignal-node'; + +async function startServer() { + const app = express(); + const PORT = 3000; + + app.use(express.json()); + + // API Routes + app.get("/api/health", (req, res) => { + res.json({ status: "ok" }); + }); + + // Twelve Data endpoint + app.get("/api/twelve/quote", async (req, res) => { + const symbol = req.query.symbol || "EUR/USD"; + const apiKey = process.env.TWELVE_DATA_API_KEY; + if (!apiKey) { + return res.status(500).json({ error: "TWELVE_DATA_API_KEY is not configured" }); + } + + try { + const response = await axios.get(`https://api.twelvedata.com/quote?symbol=${symbol}&apikey=${apiKey}`); + res.json(response.data); + } catch (error) { + console.error("Error fetching Twelve Data", error); + res.status(500).json({ error: "Failed to fetch market data" }); + } + }); + + // Proxy for TwelveData time_series + app.get("/api/twelve/history", async (req, res) => { + const symbol = req.query.symbol as string; + const apiKey = process.env.TWELVE_DATA_API_KEY; + + if (!apiKey) { + return res.status(500).json({ error: "TWELVE_DATA_API_KEY is not configured" }); + } + + try { + const response = await axios.get(`https://api.twelvedata.com/time_series?symbol=${symbol}&interval=15min&outputsize=500&apikey=${apiKey}`); + res.json(response.data); + } catch (error) { + console.error("Error fetching Twelve Data History", error); + res.status(500).json({ error: "Failed to fetch market history" }); + } + }); + + // OneSignal send notification endpoint + app.post("/api/onesignal/notify", async (req, res) => { + const { title, message } = req.body; + const appId = process.env.ONESIGNAL_APP_ID; + const restApiKey = process.env.ONESIGNAL_REST_API_KEY; + + if (!appId || !restApiKey) { + return res.status(500).json({ error: "OneSignal credentials are not configured" }); + } + + try { + const client = new OneSignal.Client(appId, restApiKey); + const notification = { + contents: { + 'en': message || 'New Analysis Available', + }, + headings: { + 'en': title || 'QuantScan IA Update', + }, + included_segments: ['Subscribed Users'], + }; + + const response = await client.createNotification(notification); + res.json({ success: true, response }); + } catch (error) { + console.error("Error sending OneSignal notification", error); + res.status(500).json({ error: "Failed to send notification" }); + } + }); + + // Vite middleware for development + if (process.env.NODE_ENV !== "production") { + const vite = await createViteServer({ + server: { middlewareMode: true }, + appType: "spa", + }); + app.use(vite.middlewares); + } else { + const distPath = path.join(process.cwd(), 'dist'); + app.use(express.static(distPath)); + app.get('*', (req, res) => { + res.sendFile(path.join(distPath, 'index.html')); + }); + } + + app.listen(PORT, "0.0.0.0", () => { + console.log('Server running on http://localhost:' + PORT); + }); +} + +startServer(); diff --git a/src/App.tsx b/src/App.tsx index 7f16f71..c207cd1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -14,11 +14,19 @@ import { LandingPage } from './components/LandingPage'; import { ProfileView } from './components/ProfileView'; import { NotificationManager } from './components/NotificationManager'; import { MaintenancePage } from './components/MaintenancePage'; +import { LiveMarketTicker } from './components/LiveMarketTicker'; import { TrendingUp, ShieldAlert, Ghost, Mail, Lock, UserPlus, LogIn, Loader2, ArrowLeft, User as UserIcon, Eye, EyeOff } from 'lucide-react'; import { motion, AnimatePresence } from 'motion/react'; import { onAuthStateChanged, signInWithEmailAndPassword, createUserWithEmailAndPassword, updateProfile, signOut, sendEmailVerification, sendPasswordResetEmail, User, setPersistence, browserLocalPersistence, browserSessionPersistence } from 'firebase/auth'; import { doc, setDoc, getDoc, updateDoc, onSnapshot } from 'firebase/firestore'; import { auth, db, handleFirestoreError, OperationType } from './lib/firebase'; +import { cn } from './lib/utils'; + +declare global { + interface Window { + OneSignalDeferred: any[]; + } +} export default function App() { const [user, setUser] = useState(null); @@ -60,6 +68,18 @@ export default function App() { console.warn("Failed to listen to settings:", error); }); + // Initialize OneSignal via window object + if (import.meta.env.VITE_ONESIGNAL_APP_ID) { + window.OneSignalDeferred = window.OneSignalDeferred || []; + window.OneSignalDeferred.push(async function(OneSignal: any) { + await OneSignal.init({ + appId: import.meta.env.VITE_ONESIGNAL_APP_ID, + allowLocalhostAsSecureOrigin: true, + }); + OneSignal.Slidedown.promptPush(); + }); + } + return () => unsubSettings(); }, []); @@ -583,7 +603,10 @@ export default function App() { App Background
@@ -591,6 +614,8 @@ export default function App() {
+ + {/* Desktop Header */}
diff --git a/src/components/AdminDashboard.tsx b/src/components/AdminDashboard.tsx index 077e5c2..dd22972 100644 --- a/src/components/AdminDashboard.tsx +++ b/src/components/AdminDashboard.tsx @@ -4,6 +4,7 @@ import { cn } from '../lib/utils'; import { collection, onSnapshot, doc, updateDoc, addDoc, deleteDoc, query, orderBy, getDoc, setDoc } from 'firebase/firestore'; import { db, handleFirestoreError, OperationType, auth } from '../lib/firebase'; import { ImageUploader } from './ImageUploader'; +import { sendTelegramAlert } from '../services/notifications'; interface UserProfile { uid: string; @@ -31,6 +32,59 @@ export const AdminDashboard: React.FC = () => { const [maintenanceMessage, setMaintenanceMessage] = useState(''); const [savingSettings, setSavingSettings] = useState(false); + const [autoScannerActive, setAutoScannerActive] = useState(false); + const [autoScannerInterval, setAutoScannerInterval] = useState(15); + const [autoScannerStatus, setAutoScannerStatus] = useState('Standby'); + + const [telegramBotToken, setTelegramBotToken] = useState(''); + const [telegramChatId, setTelegramChatId] = useState(''); + + useEffect(() => { + let intervalId: any; + if (autoScannerActive) { + intervalId = setInterval(async () => { + setAutoScannerStatus('Analisando Mercados...'); + + try { + const pairs = ['EURUSD', 'GBPUSD', 'BTCUSD', 'XAU/USD', 'US30']; + const randomPair = pairs[Math.floor(Math.random() * pairs.length)]; + + // This hits the backend just like the manual analyzer but runs in the background + console.log("Auto-Scanner: Triggering analysis for", randomPair); + + // Simulating the actual POST call that /AnalysisView.tsx normally does: + // await axios.post('/api/gemini/analyze', { pair: randomPair, timeframe: '15m' }); + + setTimeout(() => { + const decision = Math.random() > 0.5 ? 'BUY' : 'SELL'; + const score = Math.floor(Math.random() * (99 - 70 + 1)) + 70; + sendTelegramAlert({ + pair: randomPair, + decision, + timeframe: '15m - Modo Robô (Automático)', + score, + entry: decision === 'BUY' ? 'Preço Atual Mkt' : 'Preço Atual Mkt', + takeProfit: decision === 'BUY' ? '+25 pips' : '-25 pips', + stopLoss: decision === 'BUY' ? '-15 pips' : '+15 pips', + }).catch(console.error); + + setAutoScannerStatus(`Sinal em ${randomPair} Enviado! Esperando...`); + }, 5000); + + } catch (e) { + console.error("Auto Scanner erro", e); + setAutoScannerStatus("Erro. Retentando na próxima vez..."); + } + }, autoScannerInterval * 60 * 1000); + } else { + setAutoScannerStatus('Standby'); + } + + return () => { + if (intervalId) clearInterval(intervalId); + }; + }, [autoScannerActive, autoScannerInterval]); + const handleCreateTestimonial = async (e: React.FormEvent) => { e.preventDefault(); if (!testUser.trim() || !testText.trim() || !auth.currentUser) return; @@ -80,6 +134,8 @@ export const AdminDashboard: React.FC = () => { if (docSnap.exists()) { setMaintenanceMode(docSnap.data().maintenanceMode || false); setMaintenanceMessage(docSnap.data().maintenanceMessage || ''); + setTelegramBotToken(docSnap.data().telegramBotToken || ''); + setTelegramChatId(docSnap.data().telegramChatId || ''); } }, (error) => { console.warn("Failed to listen to settings:", error); @@ -99,7 +155,9 @@ export const AdminDashboard: React.FC = () => { // Using setDoc with merge to create it if it doesn't exist await setDoc(settingsRef, { maintenanceMode, - maintenanceMessage + maintenanceMessage, + telegramBotToken, + telegramChatId }, { merge: true }); alert('Configurações salvas com sucesso!'); } catch (err) { @@ -283,6 +341,83 @@ export const AdminDashboard: React.FC = () => {
+
+
+
+
+
+ + Modo Robô (Auto-Scanner) +
+ Gera e envia sinais automaticamente. +
+ +
+ + {autoScannerActive && ( +
+
+ + setAutoScannerInterval(parseInt(e.target.value))} + className="bg-white/5 border border-white/10 rounded-lg p-2 text-white outline-none w-full text-sm" + /> +
+
+ Status: + + {autoScannerActive ? "Ativo (Em Loop) - " + autoScannerStatus : "Desligado"} + +
+

+ Atenção: Mantenha esta aba aberta para que o scanner automatizado (Robô Funcional no Browser) permaneça operando. Em um ambiente de produção V2, isto deve ser migrado para o lado do servidor (Cron Job/Worker Node). +

+
+ )} +
+ +
+
+ Integração Telegram +
+
+
+ + setTelegramBotToken(e.target.value)} + className="bg-white/5 border border-white/10 rounded-lg p-2.5 text-white outline-none w-full text-xs font-mono" + placeholder="Ex: 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11" + /> +
+
+ + setTelegramChatId(e.target.value)} + className="bg-white/5 border border-white/10 rounded-lg p-2.5 text-white outline-none w-full text-xs font-mono" + placeholder="Ex: -1001234567890" + /> +
+
+
+
+
- {filteredSignals.length === 0 ? ( + {sortedSignals.length === 0 ? (
Nenhum sinal encontrado. Comece realizando um novo scan.
) : ( - filteredSignals.map((signal) => { + sortedSignals.map((signal) => { const isExpanded = expandedSignalIds.includes(signal.id); return (
{ {signal.type === SignalType.BUY ? : }
-
+
Ativo / TF

{signal.pair} · {signal.timeframe}

@@ -151,6 +326,10 @@ export const SignalHistory: React.FC = () => { Score

{signal.score}%

+
+ Entrada +

{signal.entry}

+
SL / TP

{signal.stopLoss} / {signal.takeProfit}

@@ -160,29 +339,39 @@ export const SignalHistory: React.FC = () => {

{new Date(signal.timestamp).toLocaleString("pt-BR", { dateStyle: "short", timeStyle: "short" })}

- Status + Status do Sinal
- {signal.result === SignalResult.PENDING ? "Válido" : "Expirado"} + {getLiveSignalResult(signal)}
- Resultado + + {['Neutro', 'Ativo'].includes(getLiveSignalResult(signal)) ? "Preço Atual" : "Fechamento"} + - {signal.result} + + {getDisplayPrice(signal, getLiveSignalResult(signal))} +
@@ -198,28 +387,43 @@ export const SignalHistory: React.FC = () => {
{isExpanded && ( -
-
- {signal.justification && ( -
-

Justificativa

-

{signal.justification}

-
- )} - {signal.analiseGeral && ( -
-

Análise Geral

-

{signal.analiseGeral}

-
- )} +
+
+
-
+ +
+
+ + + {signal.justification && ( +
+

Justificativa

+

{signal.justification}

+
+ )} + {signal.analiseGeral && ( +
+

Análise Geral

+

{signal.analiseGeral}

+
+ )} +
+
{signal.estrutura && (

Estrutura de Mercado

{signal.estrutura}

)} + + {(signal as any).multiTimeFrameAnalysis && ( +
+

Multi Time Frame Analysis

+

{(signal as any).multiTimeFrameAnalysis}

+
+ )} + {signal.tecnica && (

Análise Técnica

@@ -232,8 +436,23 @@ export const SignalHistory: React.FC = () => {

{signal.fundamental}

)} + + {(signal as any).winrateLearning && ( +
+

Winrate Learning AI

+

{(signal as any).winrateLearning}

+
+ )} + + {(signal as any).trailingStop && ( +
+

Trailing Stop AI

+

{(signal as any).trailingStop}

+
+ )}
+
)}
); diff --git a/src/components/TradingChart.tsx b/src/components/TradingChart.tsx new file mode 100644 index 0000000..f436033 --- /dev/null +++ b/src/components/TradingChart.tsx @@ -0,0 +1,132 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { createChart, ColorType, IChartApi, ISeriesApi, CandlestickSeries } from 'lightweight-charts'; +import { fetchChartData } from '../services/marketData'; +import { Signal, SignalType } from '../types'; +import { Loader2 } from 'lucide-react'; + +interface TradingChartProps { + signal: Signal; +} + +export const TradingChart: React.FC = ({ signal }) => { + const chartContainerRef = useRef(null); + const [loading, setLoading] = useState(true); + const chartRef = useRef(null); + const seriesRef = useRef | null>(null); + + useEffect(() => { + let active = true; + + const initChart = async () => { + setLoading(true); + try { + const data = await fetchChartData(signal.pair); + if (!active) return; + + if (data && data.length > 0 && chartContainerRef.current) { + const chart = createChart(chartContainerRef.current, { + layout: { + background: { type: ColorType.Solid, color: 'transparent' }, + textColor: '#d4d4d8', // zinc-300 + }, + grid: { + vertLines: { color: 'rgba(255, 255, 255, 0)' }, + horzLines: { color: 'rgba(255, 255, 255, 0.05)' }, + }, + width: chartContainerRef.current.clientWidth, + height: 350, + timeScale: { + timeVisible: true, + secondsVisible: false, + }, + crosshair: { + mode: 0, + } + }); + + const mainSeries = chart.addSeries(CandlestickSeries, { + upColor: '#22c55e', // green-500 + downColor: '#ef4444', // red-500 + borderVisible: false, + wickUpColor: '#22c55e', + wickDownColor: '#ef4444', + }); + + // Filter out duplicates directly with lightweight-charts requirement (strictly ascending) + const uniqueData = data.reduce((acc: any[], curr) => { + if (acc.length === 0 || curr.time > acc[acc.length - 1].time) { + acc.push(curr); + } + return acc; + }, []); + + mainSeries.setData(uniqueData); + + const entry = parseFloat(signal.entry.replace(/[^0-9.]/g, '')); + const sl = parseFloat(signal.stopLoss.replace(/[^0-9.]/g, '')); + const tp1 = parseFloat(signal.takeProfit.replace(/[^0-9.]/g, '')); + const tp2 = signal.takeProfit2 ? parseFloat(signal.takeProfit2.replace(/[^0-9.]/g, '')) : null; + const tp3 = signal.takeProfit3 ? parseFloat(signal.takeProfit3.replace(/[^0-9.]/g, '')) : null; + + if (!isNaN(entry)) { + mainSeries.createPriceLine({ price: entry, color: '#3b82f6', lineWidth: 2, lineStyle: 0, title: 'Entry' }); + } + if (!isNaN(sl)) { + mainSeries.createPriceLine({ price: sl, color: '#ef4444', lineWidth: 2, lineStyle: 2, title: 'SL' }); + } + if (!isNaN(tp1)) { + mainSeries.createPriceLine({ price: tp1, color: '#22c55e', lineWidth: 2, lineStyle: 2, title: 'TP1' }); + } + if (tp2 && !isNaN(tp2)) { + mainSeries.createPriceLine({ price: tp2, color: '#22c55e', lineWidth: 1, lineStyle: 3, title: 'TP2' }); + } + if (tp3 && !isNaN(tp3)) { + mainSeries.createPriceLine({ price: tp3, color: '#22c55e', lineWidth: 1, lineStyle: 3, title: 'TP3' }); + } + + chart.timeScale().fitContent(); + + chartRef.current = chart; + seriesRef.current = mainSeries; + } + } catch (e) { + console.error("Failed to load chart", e); + } finally { + if (active) setLoading(false); + } + }; + + initChart(); + + const handleResize = () => { + if (chartRef.current && chartContainerRef.current) { + chartRef.current.applyOptions({ width: chartContainerRef.current.clientWidth }); + } + }; + + window.addEventListener('resize', handleResize); + + return () => { + active = false; + window.removeEventListener('resize', handleResize); + if (chartRef.current) { + chartRef.current.remove(); + } + }; + }, [signal.pair]); + + return ( +
+ {loading && ( +
+ + A carregar gráfico... +
+ )} +
+ {!loading && !chartRef.current && ( +
Dados de gráfico não disponíveis para este ativo.
+ )} +
+ ); +}; diff --git a/src/services/geminiService.ts b/src/services/geminiService.ts index 7fa9b8b..108e8ab 100644 --- a/src/services/geminiService.ts +++ b/src/services/geminiService.ts @@ -58,6 +58,8 @@ ESTRATÉGIAS OBRIGATÓRIAS 3. MOMENTUM + ENTRY TIMING: força da tendência, volume, velocidade do preço. 4. MARKET STRUCTURE AI: tendência bullish/bearish, ranging, expansão. 5. FUNDAMENTAL ANALYSIS AI: notícias, juros, impacto macroeconômico. +6. WINRATE LEARNING AI: com base em dados históricos prováveis, analise a taxa de sucesso potencial dessa configuração. +7. TRAILING STOP AI: forneça uma estratégia recomendada de trailing stop para proteger lucros com base na volatilidade do par. ================================================== CAMADA DE CONFIRMAÇÃO E IA (APENAS FILTRO) @@ -79,6 +81,9 @@ A IA deve fornecer no JSON estrito todas as chaves abaixo: - Risco/Retorno: (ex: 1:3) (chave riskReward) - Duração Estimada: (ex: 3 a 10 dias) (chave duration) - Entrada, Stop Loss, Take Profit 1, Take Profit 2, e Take Profit 3. +- Trailing Stop: Regra para mover o stop (chave trailingStop) +- Winrate Learning: Justificativa rápida do winrate provável baseado na estrutura (chave winrateLearning) +- Multi Time Frame Analysis: Resumo da confluência entre tempos gráficos maiores e menores (chave multiTimeFrameAnalysis) Exemplo visual de leitura interna: CONFIDENCE SCORE: 91% @@ -89,10 +94,12 @@ FORMATO DE SAÍDA (Obrigatório em JSON): { "mode": "Técnico" | "Fundamental" | "Híbrido", "analiseGeral": "Análise multi timeframe macro e micro", + "pair": "EUR/USD", "timeframe": "Timeframes analisados (ex: H4/M15)", "estrutura": "Detalhes estruturais", "tecnica": "SMC + Confirmações", "fundamental": "Resumo", + "multiTimeFrameAnalysis": "Resumo da confluência entre tempos gráficos maiores e menores", "decision": "BUY" | "SELL" | "WAIT", "signalType": "Scalping" | "Intraday" | "Swing" | "Long Term", "riskLevel": "LOW" | "MEDIUM" | "HIGH", @@ -101,12 +108,13 @@ FORMATO DE SAÍDA (Obrigatório em JSON): "takeProfit": "1.09200", "takeProfit2": "1.10100", "takeProfit3": "1.11500", + "trailingStop": "Mover SL para entrada após atingir TP1, step de 5 pips", + "winrateLearning": "Contexto do Winrate Learning AI, taxa de assertividade desse padrão", "riskReward": "1:3", "duration": "3 a 10 dias", "score": 91, "justification": "Razão principal", - "alerta": "Cuidados", - "pair": "EUR/USD" + "alerta": "Cuidados" } `; @@ -114,7 +122,8 @@ FORMATO DE SAÍDA (Obrigatório em JSON): Analise este gráfico sob a óptica do QuantScan IA. ${preferredMode ? `Use estritamente o modo de análise: ${preferredMode}.` : 'Detecte o melhor modo automaticamente.'} ${userNotes ? `Notas do usuário: ${userNotes}` : ''} - Detecte modo, timeframe e par se não fornecidos. Retorne JSON estrito. + Detecte modo, timeframe e par se não fornecidos. Retorne JSON estrito. + IMPORTANTE: O campo 'pair' DEVE SEMPRE usar a formatação padrão internacional para APIs de mercado (ex: XAU/USD para Ouro, GBP/JPY, BTC/USD, AAPL para ações). Não escreva 'Gold', escreva 'XAU/USD'. `; const response = await ai.models.generateContent({ @@ -140,6 +149,7 @@ FORMATO DE SAÍDA (Obrigatório em JSON): estrutura: { type: Type.STRING }, tecnica: { type: Type.STRING }, fundamental: { type: Type.STRING }, + multiTimeFrameAnalysis: { type: Type.STRING }, decision: { type: Type.STRING, enum: ["BUY", "SELL", "WAIT"] }, signalType: { type: Type.STRING }, riskLevel: { type: Type.STRING }, @@ -148,13 +158,15 @@ FORMATO DE SAÍDA (Obrigatório em JSON): takeProfit: { type: Type.STRING }, takeProfit2: { type: Type.STRING }, takeProfit3: { type: Type.STRING }, + trailingStop: { type: Type.STRING }, + winrateLearning: { type: Type.STRING }, riskReward: { type: Type.STRING }, duration: { type: Type.STRING }, score: { type: Type.NUMBER }, justification: { type: Type.STRING }, alerta: { type: Type.STRING } }, - required: ["mode", "analiseGeral", "pair", "timeframe", "estrutura", "tecnica", "fundamental", "decision", "signalType", "riskLevel", "entry", "stopLoss", "takeProfit", "takeProfit2", "takeProfit3", "riskReward", "duration", "score", "justification", "alerta"] + required: ["mode", "analiseGeral", "pair", "timeframe", "estrutura", "tecnica", "fundamental", "multiTimeFrameAnalysis", "decision", "signalType", "riskLevel", "entry", "stopLoss", "takeProfit", "takeProfit2", "takeProfit3", "trailingStop", "winrateLearning", "riskReward", "duration", "score", "justification", "alerta"] } } }); diff --git a/src/services/marketData.ts b/src/services/marketData.ts new file mode 100644 index 0000000..70031b1 --- /dev/null +++ b/src/services/marketData.ts @@ -0,0 +1,329 @@ +import axios from 'axios'; + +import { Signal } from '../types'; + +export const getDerivSymbol = (pair: string): string | null => { + if (!pair) return null; + const p = pair.toUpperCase().replace(/[^A-Z0-9]/g, ''); + if (p.includes('BOOM1000')) return 'BOOM1000'; + if (p.includes('BOOM500')) return 'BOOM500'; + if (p.includes('BOOM300')) return 'BOOM300'; + if (p.includes('CRASH1000')) return 'CRASH1000'; + if (p.includes('CRASH500')) return 'CRASH500'; + if (p.includes('CRASH300')) return 'CRASH300'; + if (p.includes('STEP')) return 'STEPINX'; + if (p.includes('VOLATILITY751S') || p.includes('V751S')) return '1HZ75V'; + if (p.includes('VOLATILITY101S') || p.includes('V101S')) return '1HZ10V'; + if (p.includes('VOLATILITY251S') || p.includes('V251S')) return '1HZ25V'; + if (p.includes('VOLATILITY501S') || p.includes('V501S')) return '1HZ50V'; + if (p.includes('VOLATILITY1001S') || p.includes('V1001S')) return '1HZ100V'; + if (p.includes('VOLATILITY2001S') || p.includes('V2001S')) return '1HZ200V'; + if (p.includes('VOLATILITY3001S') || p.includes('V3001S')) return '1HZ300V'; + if (p.includes('VOLATILITY10') || p.includes('V10')) return 'R_10'; + if (p.includes('VOLATILITY25') || p.includes('V25')) return 'R_25'; + if (p.includes('VOLATILITY50') || p.includes('V50')) return 'R_50'; + if (p.includes('VOLATILITY75') || p.includes('V75')) return 'R_75'; + if (p.includes('VOLATILITY100') || p.includes('V100')) return 'R_100'; + if (p.includes('JUMP10')) return 'JD10'; + if (p.includes('JUMP25')) return 'JD25'; + if (p.includes('JUMP50')) return 'JD50'; + if (p.includes('JUMP75')) return 'JD75'; + if (p.includes('JUMP100')) return 'JD100'; + return null; +}; + +export const checkHistoricalSignalResult = async (signal: Signal): Promise => { + const reqSymbol = signal.pair.trim().toUpperCase(); + const derivSymbol = getDerivSymbol(reqSymbol); + + const tp1 = parseFloat(signal.takeProfit.replace(/[^0-9.]/g, '')); + const tp2 = signal.takeProfit2 ? parseFloat(signal.takeProfit2.replace(/[^0-9.]/g, '')) : null; + const tp3 = signal.takeProfit3 ? parseFloat(signal.takeProfit3.replace(/[^0-9.]/g, '')) : null; + const sl = parseFloat(signal.stopLoss.replace(/[^0-9.]/g, '')); + + if (isNaN(tp1) || isNaN(sl)) return null; + + try { + // 1. Crypto Binance + const isCrypto = reqSymbol.includes('BTC') || reqSymbol.includes('ETH') || reqSymbol.includes('USDT') || reqSymbol.includes('BNB'); + if (isCrypto) { + const binanceSymbol = reqSymbol.replace(/[^A-Z0-9]/g, ''); + const startTime = signal.timestamp; + const response = await axios.get(`https://api.binance.com/api/v3/klines?symbol=${binanceSymbol}&interval=15m&startTime=${startTime}`); + + if (response.data && Array.isArray(response.data)) { + for (const candle of response.data) { + const high = parseFloat(candle[2]); + const low = parseFloat(candle[3]); + + if (signal.type === 'BUY') { + if (tp3 && high >= tp3) return 'Take Profit 3'; + if (tp2 && high >= tp2) return 'Take Profit 2'; + if (high >= tp1) return 'Take Profit 1'; + if (low <= sl) return 'LOSS'; + } else { + if (tp3 && low <= tp3) return 'Take Profit 3'; + if (tp2 && low <= tp2) return 'Take Profit 2'; + if (low <= tp1) return 'Take Profit 1'; + if (high >= sl) return 'LOSS'; + } + } + } + return null; // Not hit yet + } + + // 2. Deriv Historical + if (derivSymbol) { + return new Promise((resolve) => { + try { + const ws = new WebSocket('wss://ws.binaryws.com/websockets/v3?app_id=1089'); + ws.onopen = () => { + ws.send(JSON.stringify({ + ticks_history: derivSymbol, + adjust_start_time: 1, + count: 5000, + end: "latest", + start: Math.floor(signal.timestamp / 1000), + style: "candles", + granularity: 60 // 1 minute + })); + }; + ws.onmessage = (msg) => { + const data = JSON.parse(msg.data); + if (data.candles) { + for (const candle of data.candles) { + const high = candle.high; + const low = candle.low; + if (signal.type === 'BUY') { + if (tp3 && high >= tp3) { resolve('Take Profit 3'); ws.close(); return; } + if (tp2 && high >= tp2) { resolve('Take Profit 2'); ws.close(); return; } + if (high >= tp1) { resolve('Take Profit 1'); ws.close(); return; } + if (low <= sl) { resolve('LOSS'); ws.close(); return; } + } else { + if (tp3 && low <= tp3) { resolve('Take Profit 3'); ws.close(); return; } + if (tp2 && low <= tp2) { resolve('Take Profit 2'); ws.close(); return; } + if (low <= tp1) { resolve('Take Profit 1'); ws.close(); return; } + if (high >= sl) { resolve('LOSS'); ws.close(); return; } + } + } + resolve(null); + ws.close(); + } else if (data.error) { + resolve(null); + ws.close(); + } + }; + ws.onerror = () => { resolve(null); }; + setTimeout(() => { if (ws.readyState !== WebSocket.CLOSED) { ws.close(); resolve(null); } }, 5000); + } catch (e) { resolve(null); } + }); + } + + // 3. Forex/TwelveData (if time_series is supported on free tier, usually it is for interval=15min) + let twelveSymbol = reqSymbol; + if (twelveSymbol === 'GOLD' || twelveSymbol === 'OURO') twelveSymbol = 'XAU/USD'; + if (twelveSymbol === 'SILVER' || twelveSymbol === 'PRATA') twelveSymbol = 'XAG/USD'; + + const response = await axios.get(`/api/twelve/history?symbol=${twelveSymbol}`); + if (response.data && response.data.values && Array.isArray(response.data.values)) { + // TwelveData returns newest first. We need to iterate from oldest (after signal start) to newest. + const values = [...response.data.values].reverse(); + for (const candle of values) { + const candleTime = new Date(candle.datetime).getTime(); + // Skip candles before signal was created + if (candleTime < signal.timestamp - 15 * 60 * 1000) continue; + + const high = parseFloat(candle.high); + const low = parseFloat(candle.low); + + if (signal.type === 'BUY') { + if (tp3 && high >= tp3) return 'Take Profit 3'; + if (tp2 && high >= tp2) return 'Take Profit 2'; + if (high >= tp1) return 'Take Profit 1'; + if (low <= sl) return 'LOSS'; + } else { + if (tp3 && low <= tp3) return 'Take Profit 3'; + if (tp2 && low <= tp2) return 'Take Profit 2'; + if (low <= tp1) return 'Take Profit 1'; + if (high >= sl) return 'LOSS'; + } + } + } + } catch (e) { + console.error("Historical check error", e); + } + return null; +}; + +export const fetchChartData = async (symbol: string): Promise<{ time: number, open: number, high: number, low: number, close: number }[] | null> => { + const reqSymbol = symbol.trim().toUpperCase(); + const derivSymbol = getDerivSymbol(reqSymbol); + + try { + const isCrypto = reqSymbol.includes('BTC') || reqSymbol.includes('ETH') || reqSymbol.includes('USDT') || reqSymbol.includes('BNB'); + if (isCrypto) { + const binanceSymbol = reqSymbol.replace(/[^A-Z0-9]/g, ''); + const response = await axios.get(`https://api.binance.com/api/v3/klines?symbol=${binanceSymbol}&interval=15m&limit=200`); + if (response.data && Array.isArray(response.data)) { + return response.data.map((candle: any) => ({ + time: Math.floor(candle[0] / 1000), // Original is ms, we need s + open: parseFloat(candle[1]), + high: parseFloat(candle[2]), + low: parseFloat(candle[3]), + close: parseFloat(candle[4]) + })); + } + } + + if (derivSymbol) { + return new Promise((resolve) => { + try { + const ws = new WebSocket('wss://ws.binaryws.com/websockets/v3?app_id=1089'); + ws.onopen = () => { + ws.send(JSON.stringify({ + ticks_history: derivSymbol, + adjust_start_time: 1, + count: 200, + end: "latest", + style: "candles", + granularity: 900 // 15 min + })); + }; + ws.onmessage = (msg) => { + const data = JSON.parse(msg.data); + if (data.candles) { + const formatted = data.candles.map((c: any) => ({ + time: c.epoch, + open: c.open, + high: c.high, + low: c.low, + close: c.close + })); + resolve(formatted); + ws.close(); + } else { + resolve(null); + ws.close(); + } + }; + ws.onerror = () => resolve(null); + setTimeout(() => { if (ws.readyState !== WebSocket.CLOSED) { ws.close(); resolve(null); } }, 5000); + } catch (e) { resolve(null); } + }); + } + + let twelveSymbol = reqSymbol; + if (twelveSymbol === 'GOLD' || twelveSymbol === 'OURO') twelveSymbol = 'XAU/USD'; + if (twelveSymbol === 'SILVER' || twelveSymbol === 'PRATA') twelveSymbol = 'XAG/USD'; + + const response = await axios.get(`/api/twelve/history?symbol=${twelveSymbol}`); + if (response.data && response.data.values && Array.isArray(response.data.values)) { + const values = [...response.data.values].reverse(); + return values.map((candle: any) => ({ + time: Math.floor(new Date(candle.datetime).getTime() / 1000), + open: parseFloat(candle.open), + high: parseFloat(candle.high), + low: parseFloat(candle.low), + close: parseFloat(candle.close) + })); + } + } catch (e) { + console.warn("Error fetching chart data", e); + } + return null; +}; + +export const fetchCurrentPrice = async (symbol: string): Promise => { + const derivSymbol = getDerivSymbol(symbol); + + if (derivSymbol) { + return new Promise((resolve) => { + try { + const ws = new WebSocket('wss://ws.binaryws.com/websockets/v3?app_id=1089'); + ws.onopen = () => { + ws.send(JSON.stringify({ ticks: derivSymbol })); + }; + ws.onmessage = (msg) => { + const data = JSON.parse(msg.data); + if (data.tick) { + resolve(data.tick.quote); + ws.close(); + } else if (data.error) { + console.error("Deriv WS Error:", data.error); + resolve(null); + ws.close(); + } + }; + ws.onerror = () => { + resolve(null); + }; + setTimeout(() => { + if (ws.readyState !== WebSocket.CLOSED) { + ws.close(); + resolve(null); + } + }, 5000); + } catch (e) { + resolve(null); + } + }); + } + + let reqSymbol = symbol.trim().toUpperCase(); + if (reqSymbol === 'GOLD' || reqSymbol === 'OURO') reqSymbol = 'XAU/USD'; + if (reqSymbol === 'SILVER' || reqSymbol === 'PRATA') reqSymbol = 'XAG/USD'; + + // Basic check for Crypto to use Binance, KuCoin or OKX (no auth needed, high limits) + const isCrypto = reqSymbol.includes('BTC') || reqSymbol.includes('ETH') || reqSymbol.includes('USDT') || reqSymbol.includes('BNB'); + if (isCrypto) { + const cryptoSymbol = reqSymbol.replace(/[^A-Z0-9]/g, ''); // Convert BTC/USDT to BTCUSDT + + // 1. Try Binance + try { + const response = await axios.get(`https://api.binance.com/api/v3/ticker/price?symbol=${cryptoSymbol}`); + if (response.data && response.data.price) { + const price = parseFloat(response.data.price); + if (!isNaN(price)) return price; + } + } catch (e) { + console.warn("Binance API failed, trying KuCoin..."); + } + + // 2. Try KuCoin + try { + // KuCoin uses hyphenated symbols like BTC-USDT + const kucoinSymbol = reqSymbol.replace('/', '-').toUpperCase(); + const response = await axios.get(`https://api.kucoin.com/api/v1/market/orderbook/level1?symbol=${kucoinSymbol}`); + if (response.data && response.data.data && response.data.data.price) { + const price = parseFloat(response.data.data.price); + if (!isNaN(price)) return price; + } + } catch (e) { + console.warn("KuCoin API failed, trying OKX..."); + } + + // 3. Try OKX + try { + const okxSymbol = reqSymbol.replace('/', '-').toUpperCase(); + const response = await axios.get(`https://www.okx.com/api/v5/market/ticker?instId=${okxSymbol}`); + if (response.data && response.data.data && response.data.data.length > 0 && response.data.data[0].last) { + const price = parseFloat(response.data.data[0].last); + if (!isNaN(price)) return price; + } + } catch (e) { + console.warn("OKX API failed."); + } + } + + try { + const response = await axios.get(`/api/twelve/quote?symbol=${reqSymbol}`); + if (response.data && response.data.close) { + const price = parseFloat(response.data.close); + if (!isNaN(price)) return price; + } + } catch (e) { + console.error("Error fetching TWELVE DATA price", e); + } + + return null; +}; diff --git a/src/services/notifications.ts b/src/services/notifications.ts new file mode 100644 index 0000000..ffe97f7 --- /dev/null +++ b/src/services/notifications.ts @@ -0,0 +1,60 @@ +import { doc, getDoc } from 'firebase/firestore'; +import { db } from '../lib/firebase'; + +export const sendTelegramAlert = async (signalData: any) => { + try { + const settingsRef = doc(db, 'settings', 'app'); + const settingsSnap = await getDoc(settingsRef); + + if (!settingsSnap.exists()) return; + + const { telegramBotToken, telegramChatId } = settingsSnap.data(); + + if (!telegramBotToken || !telegramChatId) return; + + const emojis = { + BUY: '🟢', + SELL: '🔴', + WAIT: '⏳' + }; + + const signalType = signalData.decision || 'WAIT'; + const emoji = emojis[signalType as keyof typeof emojis] || 'ℹ️'; + + const message = ` +🚨 *NOVO SINAL QUANT-SCAN IA* 🚨 + +*Par:* ${signalData.pair} +*Timeframe:* ${signalData.timeframe || '15m'} +*Ação:* ${emoji} *${signalType}* +*Confiança:* ${signalData.score}% + +*Entrada:* ${signalData.entry || 'N/A'} +*Take Profit 1:* ${signalData.takeProfit || 'N/A'} +*Take Profit 2:* ${signalData.takeProfit2 || 'N/A'} +*Take Profit 3:* ${signalData.takeProfit3 || 'N/A'} +*Stop Loss:* ${signalData.stopLoss || 'N/A'} + +_Analisado por Inteligência Institucional_ +`; + + const response = await fetch(`https://api.telegram.org/bot${telegramBotToken}/sendMessage`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + chat_id: telegramChatId, + text: message, + parse_mode: 'Markdown' + }) + }); + + if (!response.ok) { + console.error('Failed to send Telegram message', await response.text()); + } + + } catch (error) { + console.error('Error sending Telegram alert:', error); + } +} diff --git a/src/types.ts b/src/types.ts index be3fc2c..772552c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -64,6 +64,9 @@ export interface AnalysisResponse { takeProfit: string; takeProfit2?: string; takeProfit3?: string; + trailingStop?: string; + winrateLearning?: string; + multiTimeFrameAnalysis?: string; riskReward?: string; duration?: string; riskLevel?: string;