50 Commits

Author SHA1 Message Date
00b7f3b0b6 bring back collapsible styling from branch 12 and implement endpoints for add api keys and change password 2023-12-05 23:49:38 -05:00
c21b6ff634 Merge branch '26-add-docker-build-ci-job' into 'main'
Add Docker build CI job

Closes #26

See merge request finvis/finvis!27
2023-12-03 18:15:51 -05:00
c96db51219 Switch to latest tag of docker docker image 2023-12-03 18:01:32 -05:00
90e0768054 Add docker build CI stage 2023-12-03 17:57:08 -05:00
9aaa9b003f Merge branch '21-add-functionality-to-login-and-signup-pages' into 'main'
Added login and signup functionality

Closes #21

See merge request finvis/finvis!22
2023-12-03 16:09:12 -05:00
f7eb23c52d Fix linter errors in login/signup code 2023-12-03 16:06:50 -05:00
32c15b2032 Ignore magic numbers -1 and 0 2023-12-03 16:05:25 -05:00
5f4e9a57d7 Merge remote-tracking branch 'origin/main' into 21-add-functionality-to-login-and-signup-pages 2023-12-03 15:45:19 -05:00
29cdd467a1 Use USXI types for trade API endpoints 2023-12-03 15:22:06 -05:00
e5b1212af4 Import types from USXI instead of local copies of files 2023-12-03 15:21:38 -05:00
7bb7986bf6 Install @finvis/usxi 2023-12-03 15:19:51 -05:00
dd691132b2 Merge branch '30-fix-linting-errors' into 'main'
Resolve "Fix linting errors"

Closes #30

See merge request finvis/finvis!26
2023-12-03 14:53:13 -05:00
a2a8cea193 Fix miscellaneous linting errors 2023-12-01 20:40:18 -05:00
5bc1576133 Reformat login and create_account procedures and use async bcrypt for them 2023-12-01 20:39:45 -05:00
49227e397e add a catch to router.push to fix promise linting issue 2023-12-01 20:38:03 -05:00
d1b538e39b Fix magic number linting errors 2023-12-01 20:35:31 -05:00
9df557d972 Changed usermodel to reduce repeat types
modified:   src/models/UserModel.ts
	modified:   src/server/api/routers/user.ts
2023-12-01 20:33:13 -05:00
6e7f968b06 Make connection to db non-async 2023-12-01 20:33:04 -05:00
f9166de925 Modify linting config to warn for console statements 2023-12-01 20:32:30 -05:00
5bd98c32d3 added account value for leaderboard
modified:   src/models/UserModel.ts
2023-12-01 17:00:29 -05:00
10baa478c2 Added Papertrading support to usermodel and api
modified:   src/models/UserModel.ts
	modified:   src/server/api/routers/user.ts
2023-11-24 22:49:45 -05:00
e6289ffc12 Attempt to fix bad Git LFS change 2023-11-10 14:58:29 -05:00
fd1aea0af5 Merge branch '24-remove-default-trpc-router' into 'main'
Remove default tRPC router and remove references to it elsewhere

Closes #24

See merge request finvis/finvis!25
2023-11-09 22:48:23 -05:00
eec61fe330 Remove references to default example router 2023-11-09 22:06:17 -05:00
dba27461b6 Remove example trpc router 2023-11-09 21:52:24 -05:00
485c900160 Merge branch '9-remove-default-t3-readme' into 'main'
Replace t3 generated readme with basic readme

Closes #9

See merge request finvis/finvis!24
2023-11-09 21:33:50 -05:00
b29879c11f Replace t3 generated readme with basic readme 2023-11-09 20:55:10 -05:00
d9ae49c53f fixed home page styling 2023-11-03 16:15:25 -04:00
0234846521 Merge branch '20-create-dockerfile' into 'main'
Create Dockerfile

Closes #20

See merge request finvis/finvis!17
2023-10-31 17:30:22 -04:00
fdd0a294ff Merge branch 'main' into 20-create-dockerfile 2023-10-31 17:27:59 -04:00
0dbb9bdc47 Merge branch '27-fix-build-issues' into 'main'
Fix build issues

Closes #27

See merge request finvis/finvis!18
2023-10-31 16:27:42 -04:00
e6bea411e0 made login functional in login page 2023-10-31 16:24:05 -04:00
ccd6dbdd81 Don't run lint when building 2023-10-31 11:03:25 -04:00
2fd018911e Fix MongoDB user lookup
Wait for query to complete
Only query for a single entry
Don't make extra query to return fetched user
2023-10-31 11:02:07 -04:00
4a66aa87f3 Import UserModel instead of using context 2023-10-31 10:58:15 -04:00
48e3832624 Enable output file tracing
This reduces Docker image size drastically by including exactly what is needed
2023-10-31 10:48:46 -04:00
39a28844dd Add Dockerfile 2023-10-31 10:41:10 -04:00
338694767a Add Docker ignore file 2023-10-31 10:41:03 -04:00
3fa5579a4b fixed signup functionality, it should be working without flaws now 2023-10-27 21:35:36 -04:00
f4b6fdef2c fixed login router 2023-10-27 17:33:52 -04:00
eff4af24da fixed user schema and related 2023-10-27 17:07:11 -04:00
f56ce1c7d8 bruh 2023-10-27 16:42:04 -04:00
730b9939dd got signup to work 2023-10-27 16:42:04 -04:00
acaf711e10 Merge branch '25-add-build-ci-step' into 'main'
Add build CI step

Closes #25

See merge request finvis/finvis!16
2023-10-27 09:57:12 -04:00
722343e754 Don't validate environment variables for CI build 2023-10-27 09:24:13 -04:00
8c2870e5b0 Combine build and lint into single stage 2023-10-27 09:16:56 -04:00
360cd91f2b Add build CI stage 2023-10-27 09:08:56 -04:00
db5219207a added new functions
modified:   package.json
	modified:   src/clients/mongoose.ts
	modified:   src/models/users.ts
	modified:   src/server/api/routers/user.ts
	modified:   src/server/api/trpc.ts
2023-10-24 20:08:39 -04:00
09df7432a3 Created new userRouter
modified:   package.json
	modified:   src/server/api/root.ts
	modified:   src/server/api/routers/login.ts
	new file:   src/server/api/routers/user.ts
2023-10-22 21:58:09 -04:00
e3421ad462 corrected login and signup procedures 2023-10-20 13:18:54 -04:00
30 changed files with 1544 additions and 197 deletions

7
.dockerignore Normal file
View File

@ -0,0 +1,7 @@
Dockerfile
.dockerignore
node_modules
npm-debug.log
README.md
.next
.git

View File

@ -57,7 +57,7 @@ const config = {
"func-style": [ "error", "expression" ], "func-style": [ "error", "expression" ],
"grouped-accessor-pairs": [ "error", "getBeforeSet" ], "grouped-accessor-pairs": [ "error", "getBeforeSet" ],
"no-caller": "error", "no-caller": "error",
"no-console": "error", "no-console": "warn",
"no-eq-null": "error", "no-eq-null": "error",
"no-eval": "error", "no-eval": "error",
"no-extend-native": "error", "no-extend-native": "error",
@ -73,7 +73,7 @@ const config = {
"no-lone-blocks": "error", "no-lone-blocks": "error",
"no-lonely-if": "error", "no-lonely-if": "error",
"no-loop-func": "error", "no-loop-func": "error",
"no-magic-numbers": "error", "no-magic-numbers": [ "error", { "ignore": [-1, 0] } ],
"no-mixed-operators": "error", "no-mixed-operators": "error",
"no-multi-assign": "error", "no-multi-assign": "error",
"no-multi-str": "error", "no-multi-str": "error",

View File

@ -1,10 +1,32 @@
stages: stages:
- lint - build
# Lint the project # Lint the project
lint: lint:
stage: lint stage: build
image: node:18 image: node:18
script: script:
- npm ci - npm ci
- npm run lint - npm run lint
# Do a test build of the project
build:
stage: build
image: node:18
variables:
SKIP_ENV_VALIDATION: 1
script:
- npm ci
- npm run build
# Build the docker image and push it to the registry
docker-build:
stage: build
image: docker:latest
script:
- /usr/local/bin/dockerd-entrypoint.sh &
- while ! docker info; do echo "Waiting for Docker to become available..."; sleep 1; done
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD registry.mregirouard.com
- docker build -t registry.mregirouard.com/finvis/finvis:$CI_COMMIT_SHORT_SHA
-t registry.mregirouard.com/finvis/finvis:$CI_COMMIT_BRANCH .
- docker push registry.mregirouard.com/finvis/finvis --all-tags

1
.npmrc Normal file
View File

@ -0,0 +1 @@
@finvis:registry=https://gitlab.mregirouard.com/api/v4/packages/npm/

40
Dockerfile Normal file
View File

@ -0,0 +1,40 @@
FROM node:18-alpine AS base
LABEL maintainer="FinVis Rensselear Center for Open Source Project Team"
LABEL license="GNU GPLv3"
LABEL description="Docker image for FinVis T3 app."
ENV SKIP_ENV_VALIDATION 1
ENV PORT 80
ENV HOSTNAME "0.0.0.0"
FROM base AS deps
WORKDIR /app
COPY package.json ./
COPY package-lock.json ./
RUN npm ci
FROM base as build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM base AS runner
WORKDIR /app
RUN addgroup --system --gid 1005 nodejs
RUN adduser --system --uid 1005 nextjs
COPY --from=build /app/public ./public
RUN mkdir .next
RUN chown nextjs:nodejs .next
COPY --from=build --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=build --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
CMD ["node", "server.js"]

View File

@ -1,28 +1,34 @@
# Create T3 App ![FinVis](public/finvis_logo.png)
This is a [T3 Stack](https://create.t3.gg/) project bootstrapped with `create-t3-app`. # FinVis
## What's next? How do I make an app with this? A financial tracking web app that allows users to track their investments across multiple exchanges.
We try to keep this project as simple as possible, so you can start with just the scaffolding we set up for you, and add additional things later when they become necessary. ## Purpose
To organize, display, and analyze stock trades on different exchanges in one place.
If you are not familiar with the different technologies used in this project, please refer to the respective docs. If you still are in the wind, please join our [Discord](https://t3.gg/discord) and ask for help. ## Try It
You can run FinVis by checking out this repository, and running:
`npm run start`
- [Next.js](https://nextjs.org) Or view the [production site](https://fin-vis.com).
- [NextAuth.js](https://next-auth.js.org)
- [Prisma](https://prisma.io)
- [Tailwind CSS](https://tailwindcss.com)
- [tRPC](https://trpc.io)
## Learn More ## The Team
To learn more about the [T3 Stack](https://create.t3.gg/), take a look at the following resources: This project is being developed for [Rensselaer Center for Open Source (RCOS)](https://new.rcos.io) by:
- [Documentation](https://create.t3.gg/) - [Ethan Girouard](https://gitlab.mregirouard.com/MRegirouard)
- [Learn the T3 Stack](https://create.t3.gg/en/faq#what-learning-resources-are-currently-available) — Check out these awesome tutorials - [Carter Bertolini](https://gitlab.mregirouard.com/clbertolini)
- [Aaron Verkleeren](https://gitlab.mregirouard.com/averkleeren)
- [Derrick Lin](https://gitlab.mregirouard.com/derricklin)
- [Connor Wittman](https://gitlab.mregirouard.com/ecco257)
- [Danny Zou](https://gitlab.mregirouard.com/dannyzou18)
- [Aidan Westphal](https://gitlab.mregirouard.com/hifwiend)
You can check out the [create-t3-app GitHub repository](https://github.com/t3-oss/create-t3-app) — your feedback and contributions are welcome! ## Stack
## How do I deploy this? - We created this project using [create t3 app](https://create.t3.gg)
- We are making the frontend with [React](https://reactjs.org) and styling is done with [Tailwind CSS](https://tailwindcss.com)
Follow our deployment guides for [Vercel](https://create.t3.gg/en/deployment/vercel), [Netlify](https://create.t3.gg/en/deployment/netlify) and [Docker](https://create.t3.gg/en/deployment/docker) for more information. - For server-side rendering, we are using [Next.js](https://nextjs.org)
- [tRPC](https://trpc.io) is used to handle API calls
- [MongoDB](https://www.mongodb.com) provides the database

View File

@ -17,6 +17,10 @@ const config = {
locales: ["en"], locales: ["en"],
defaultLocale: "en", defaultLocale: "en",
}, },
output: "standalone",
eslint: {
ignoreDuringBuilds: true,
}
}; };
export default config; export default config;

561
package-lock.json generated
View File

@ -8,6 +8,7 @@
"name": "finvis", "name": "finvis",
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@finvis/usxi": "^1.7.0",
"@t3-oss/env-nextjs": "^0.6.0", "@t3-oss/env-nextjs": "^0.6.0",
"@tanstack/react-query": "^4.32.6", "@tanstack/react-query": "^4.32.6",
"@trpc/client": "^10.37.1", "@trpc/client": "^10.37.1",
@ -17,6 +18,7 @@
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"framer-motion": "^10.16.4", "framer-motion": "^10.16.4",
"mongodb": "^6.2.0",
"mongoose": "^7.5.3", "mongoose": "^7.5.3",
"next": "^13.4.19", "next": "^13.4.19",
"next-auth": "^4.23.0", "next-auth": "^4.23.0",
@ -66,6 +68,35 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/@alpacahq/alpaca-trade-api": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@alpacahq/alpaca-trade-api/-/alpaca-trade-api-3.0.1.tgz",
"integrity": "sha512-FOsgA4jREAArWbb7nuKTo2yiUdb8+/4vboyTX2SRJSi+oYBaFrZV+hMNx7fGWtuLuKGRKEdK8Nc+8MFjwcbz4g==",
"dependencies": {
"axios": "^0.21.1",
"dotenv": "^6.2.0",
"events": "^3.2.0",
"just-extend": "^4.1.0",
"lodash": "^4.17.19",
"minimist": "^1.2.6",
"msgpack5": "^5.3.2",
"nats": "^1.4.9",
"urljoin": "^0.1.5",
"ws": "^7.4.3"
},
"engines": {
"node": ">=14.x",
"npm": ">=6"
}
},
"node_modules/@alpacahq/alpaca-trade-api/node_modules/dotenv": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.2.0.tgz",
"integrity": "sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==",
"engines": {
"node": ">=6"
}
},
"node_modules/@babel/runtime": { "node_modules/@babel/runtime": {
"version": "7.23.1", "version": "7.23.1",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.1.tgz", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.1.tgz",
@ -77,6 +108,14 @@
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@colors/colors": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
"integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
"engines": {
"node": ">=0.1.90"
}
},
"node_modules/@cspotcode/source-map-support": { "node_modules/@cspotcode/source-map-support": {
"version": "0.8.1", "version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
@ -97,6 +136,16 @@
"@jridgewell/sourcemap-codec": "^1.4.10" "@jridgewell/sourcemap-codec": "^1.4.10"
} }
}, },
"node_modules/@dabh/diagnostics": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz",
"integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==",
"dependencies": {
"colorspace": "1.1.x",
"enabled": "2.0.x",
"kuler": "^2.0.0"
}
},
"node_modules/@emotion/is-prop-valid": { "node_modules/@emotion/is-prop-valid": {
"version": "0.8.8", "version": "0.8.8",
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
@ -168,6 +217,16 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0" "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
} }
}, },
"node_modules/@finvis/usxi": {
"version": "1.7.0",
"resolved": "http://gitlab.mregirouard.com/api/v4/projects/12/packages/npm/@finvis/usxi/-/@finvis/usxi-1.7.0.tgz",
"integrity": "sha1-BJh1RAFTR/l1JO+P0foxnKV202o=",
"dependencies": {
"@alpacahq/alpaca-trade-api": "^3.0.1",
"dotenv": "^16.3.1",
"winston": "^3.10.0"
}
},
"node_modules/@humanwhocodes/config-array": { "node_modules/@humanwhocodes/config-array": {
"version": "0.11.11", "version": "0.11.11",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz",
@ -270,7 +329,6 @@
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.0.tgz",
"integrity": "sha512-Xfijy7HvfzzqiOAhAepF4SGN5e9leLkMvg/OPOF97XemjfVCYN/oWa75wnkc6mltMSTwY+XlbhWgUOJmkFspSw==", "integrity": "sha512-Xfijy7HvfzzqiOAhAepF4SGN5e9leLkMvg/OPOF97XemjfVCYN/oWa75wnkc6mltMSTwY+XlbhWgUOJmkFspSw==",
"optional": true,
"dependencies": { "dependencies": {
"sparse-bitfield": "^3.0.3" "sparse-bitfield": "^3.0.3"
} }
@ -691,6 +749,11 @@
"integrity": "sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==", "integrity": "sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==",
"dev": true "dev": true
}, },
"node_modules/@types/triple-beam": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
"integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="
},
"node_modules/@types/webidl-conversions": { "node_modules/@types/webidl-conversions": {
"version": "7.0.1", "version": "7.0.1",
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.1.tgz",
@ -1170,6 +1233,11 @@
"integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==",
"dev": true "dev": true
}, },
"node_modules/async": {
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg=="
},
"node_modules/asynciterator.prototype": { "node_modules/asynciterator.prototype": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz",
@ -1237,6 +1305,14 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/axios": {
"version": "0.21.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
"dependencies": {
"follow-redirects": "^1.14.0"
}
},
"node_modules/axobject-query": { "node_modules/axobject-query": {
"version": "3.2.1", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz",
@ -1251,6 +1327,25 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
}, },
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/bcrypt": { "node_modules/bcrypt": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz",
@ -1273,6 +1368,16 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"dependencies": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
"readable-stream": "^3.4.0"
}
},
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@ -1334,6 +1439,29 @@
"node": ">=14.20.1" "node": ">=14.20.1"
} }
}, },
"node_modules/buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"node_modules/busboy": { "node_modules/busboy": {
"version": "1.6.0", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
@ -1463,6 +1591,15 @@
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="
}, },
"node_modules/color": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
"integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
"dependencies": {
"color-convert": "^1.9.3",
"color-string": "^1.6.0"
}
},
"node_modules/color-convert": { "node_modules/color-convert": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@ -1478,8 +1615,16 @@
"node_modules/color-name": { "node_modules/color-name": {
"version": "1.1.4", "version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
"dev": true },
"node_modules/color-string": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
"dependencies": {
"color-name": "^1.0.0",
"simple-swizzle": "^0.2.2"
}
}, },
"node_modules/color-support": { "node_modules/color-support": {
"version": "1.1.3", "version": "1.1.3",
@ -1489,6 +1634,28 @@
"color-support": "bin.js" "color-support": "bin.js"
} }
}, },
"node_modules/color/node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dependencies": {
"color-name": "1.1.3"
}
},
"node_modules/color/node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
},
"node_modules/colorspace": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz",
"integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==",
"dependencies": {
"color": "^3.1.3",
"text-hex": "1.0.x"
}
},
"node_modules/commander": { "node_modules/commander": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
@ -1715,6 +1882,11 @@
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
"dev": true "dev": true
}, },
"node_modules/enabled": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
"integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="
},
"node_modules/enhanced-resolve": { "node_modules/enhanced-resolve": {
"version": "5.15.0", "version": "5.15.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz",
@ -2282,6 +2454,19 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"engines": {
"node": ">=0.8.x"
}
},
"node_modules/extend": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-2.0.2.tgz",
"integrity": "sha512-AgFD4VU+lVLP6vjnlNfF7OeInLTyeyckCNPEsuxz1vi786UuK/nk6ynPuhn/h+Ju9++TQyr5EpLRI14fc1QtTQ=="
},
"node_modules/fast-deep-equal": { "node_modules/fast-deep-equal": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@ -2337,6 +2522,11 @@
"reusify": "^1.0.4" "reusify": "^1.0.4"
} }
}, },
"node_modules/fecha": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
"integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="
},
"node_modules/file-entry-cache": { "node_modules/file-entry-cache": {
"version": "6.0.1", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@ -2397,6 +2587,30 @@
"integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
"dev": true "dev": true
}, },
"node_modules/fn.name": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
},
"node_modules/follow-redirects": {
"version": "1.15.3",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
"integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/for-each": { "node_modules/for-each": {
"version": "0.3.3", "version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
@ -2785,6 +2999,25 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.2.4", "version": "5.2.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@ -2866,6 +3099,11 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/is-arrayish": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
},
"node_modules/is-async-function": { "node_modules/is-async-function": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz",
@ -3107,6 +3345,17 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-string": { "node_modules/is-string": {
"version": "1.0.7", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
@ -3301,6 +3550,11 @@
"node": ">=4.0" "node": ">=4.0"
} }
}, },
"node_modules/just-extend": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz",
"integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg=="
},
"node_modules/kareem": { "node_modules/kareem": {
"version": "2.5.1", "version": "2.5.1",
"resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz",
@ -3318,6 +3572,11 @@
"json-buffer": "3.0.1" "json-buffer": "3.0.1"
} }
}, },
"node_modules/kuler": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
"integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="
},
"node_modules/language-subtag-registry": { "node_modules/language-subtag-registry": {
"version": "0.3.22", "version": "0.3.22",
"resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz",
@ -3376,12 +3635,33 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash.merge": { "node_modules/lodash.merge": {
"version": "4.6.2", "version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true "dev": true
}, },
"node_modules/logform": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz",
"integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==",
"dependencies": {
"@colors/colors": "1.6.0",
"@types/triple-beam": "^1.3.2",
"fecha": "^4.2.0",
"ms": "^2.1.1",
"safe-stable-stringify": "^2.3.1",
"triple-beam": "^1.3.0"
},
"engines": {
"node": ">= 12.0.0"
}
},
"node_modules/loose-envify": { "node_modules/loose-envify": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@ -3434,8 +3714,7 @@
"node_modules/memory-pager": { "node_modules/memory-pager": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
"integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="
"optional": true
}, },
"node_modules/merge2": { "node_modules/merge2": {
"version": "1.4.1", "version": "1.4.1",
@ -3474,7 +3753,6 @@
"version": "1.2.8", "version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true,
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
@ -3522,6 +3800,89 @@
} }
}, },
"node_modules/mongodb": { "node_modules/mongodb": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.2.0.tgz",
"integrity": "sha512-d7OSuGjGWDZ5usZPqfvb36laQ9CPhnWkAGHT61x5P95p/8nMVeH8asloMwW6GcYFeB0Vj4CB/1wOTDG2RA9BFA==",
"dependencies": {
"@mongodb-js/saslprep": "^1.1.0",
"bson": "^6.2.0",
"mongodb-connection-string-url": "^2.6.0"
},
"engines": {
"node": ">=16.20.1"
},
"peerDependencies": {
"@aws-sdk/credential-providers": "^3.188.0",
"@mongodb-js/zstd": "^1.1.0",
"gcp-metadata": "^5.2.0",
"kerberos": "^2.0.1",
"mongodb-client-encryption": ">=6.0.0 <7",
"snappy": "^7.2.2",
"socks": "^2.7.1"
},
"peerDependenciesMeta": {
"@aws-sdk/credential-providers": {
"optional": true
},
"@mongodb-js/zstd": {
"optional": true
},
"gcp-metadata": {
"optional": true
},
"kerberos": {
"optional": true
},
"mongodb-client-encryption": {
"optional": true
},
"snappy": {
"optional": true
},
"socks": {
"optional": true
}
}
},
"node_modules/mongodb-connection-string-url": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz",
"integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==",
"dependencies": {
"@types/whatwg-url": "^8.2.1",
"whatwg-url": "^11.0.0"
}
},
"node_modules/mongodb/node_modules/bson": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/bson/-/bson-6.2.0.tgz",
"integrity": "sha512-ID1cI+7bazPDyL9wYy9GaQ8gEEohWvcUl/Yf0dIdutJxnmInEEyCsb4awy/OiBfall7zBA179Pahi3vCdFze3Q==",
"engines": {
"node": ">=16.20.1"
}
},
"node_modules/mongoose": {
"version": "7.6.1",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-7.6.1.tgz",
"integrity": "sha512-Iflr60FL7mabBdgAtumLTwEGdZGV6IKHfF7F75En2JWpPitorwQeCFqWPcPHRnBxncKANl3gwI9nh2Yb4y3/sA==",
"dependencies": {
"bson": "^5.5.0",
"kareem": "2.5.1",
"mongodb": "5.9.0",
"mpath": "0.9.0",
"mquery": "5.0.0",
"ms": "2.1.3",
"sift": "16.0.1"
},
"engines": {
"node": ">=14.20.1"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mongoose"
}
},
"node_modules/mongoose/node_modules/mongodb": {
"version": "5.9.0", "version": "5.9.0",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.9.0.tgz", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.9.0.tgz",
"integrity": "sha512-g+GCMHN1CoRUA+wb1Agv0TI4YTSiWr42B5ulkiAfLLHitGK1R+PkSAf3Lr5rPZwi/3F04LiaZEW0Kxro9Fi2TA==", "integrity": "sha512-g+GCMHN1CoRUA+wb1Agv0TI4YTSiWr42B5ulkiAfLLHitGK1R+PkSAf3Lr5rPZwi/3F04LiaZEW0Kxro9Fi2TA==",
@ -3561,36 +3922,6 @@
} }
} }
}, },
"node_modules/mongodb-connection-string-url": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz",
"integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==",
"dependencies": {
"@types/whatwg-url": "^8.2.1",
"whatwg-url": "^11.0.0"
}
},
"node_modules/mongoose": {
"version": "7.6.1",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-7.6.1.tgz",
"integrity": "sha512-Iflr60FL7mabBdgAtumLTwEGdZGV6IKHfF7F75En2JWpPitorwQeCFqWPcPHRnBxncKANl3gwI9nh2Yb4y3/sA==",
"dependencies": {
"bson": "^5.5.0",
"kareem": "2.5.1",
"mongodb": "5.9.0",
"mpath": "0.9.0",
"mquery": "5.0.0",
"ms": "2.1.3",
"sift": "16.0.1"
},
"engines": {
"node": ">=14.20.1"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mongoose"
}
},
"node_modules/mongoose/node_modules/ms": { "node_modules/mongoose/node_modules/ms": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@ -3620,6 +3951,17 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}, },
"node_modules/msgpack5": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/msgpack5/-/msgpack5-5.3.2.tgz",
"integrity": "sha512-e9jz+6InQIUb2cGzZ/Mi+rQBs1KFLby+gNi+22VwQ1pnC9ieZjysKGmRMjdlf6IyjsltbgY4tGoHwHmyS7l94A==",
"dependencies": {
"bl": "^4.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.0.0",
"safe-buffer": "^5.1.2"
}
},
"node_modules/mz": { "node_modules/mz": {
"version": "2.7.0", "version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
@ -3648,6 +3990,24 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
} }
}, },
"node_modules/nats": {
"version": "1.4.12",
"resolved": "https://registry.npmjs.org/nats/-/nats-1.4.12.tgz",
"integrity": "sha512-Jf4qesEF0Ay0D4AMw3OZnKMRTQm+6oZ5q8/m4gpy5bTmiDiK6wCXbZpzEslmezGpE93LV3RojNEG6dpK/mysLQ==",
"dependencies": {
"nuid": "^1.1.4",
"ts-nkeys": "^1.0.16"
},
"bin": {
"node-pub": "examples/node-pub",
"node-reply": "examples/node-reply",
"node-req": "examples/node-req",
"node-sub": "examples/node-sub"
},
"engines": {
"node": ">= 8.0.0"
}
},
"node_modules/natural-compare": { "node_modules/natural-compare": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@ -3850,6 +4210,14 @@
"set-blocking": "^2.0.0" "set-blocking": "^2.0.0"
} }
}, },
"node_modules/nuid": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/nuid/-/nuid-1.1.6.tgz",
"integrity": "sha512-Eb3CPCupYscP1/S1FQcO5nxtu6l/F3k0MQ69h7f5osnsemVk5pkc8/5AyalVT+NCfra9M71U8POqF6EZa6IHvg==",
"engines": {
"node": ">= 8.16.0"
}
},
"node_modules/oauth": { "node_modules/oauth": {
"version": "0.9.15", "version": "0.9.15",
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
@ -3996,6 +4364,14 @@
"wrappy": "1" "wrappy": "1"
} }
}, },
"node_modules/one-time": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
"integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
"dependencies": {
"fn.name": "1.x.x"
}
},
"node_modules/openid-client": { "node_modules/openid-client": {
"version": "5.5.0", "version": "5.5.0",
"resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.5.0.tgz", "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.5.0.tgz",
@ -4690,6 +5066,14 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/safe-stable-stringify": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz",
"integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==",
"engines": {
"node": ">=10"
}
},
"node_modules/scheduler": { "node_modules/scheduler": {
"version": "0.23.0", "version": "0.23.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
@ -4776,6 +5160,14 @@
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
}, },
"node_modules/simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
"dependencies": {
"is-arrayish": "^0.3.1"
}
},
"node_modules/slash": { "node_modules/slash": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
@ -4819,11 +5211,18 @@
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
"integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
"optional": true,
"dependencies": { "dependencies": {
"memory-pager": "^1.0.2" "memory-pager": "^1.0.2"
} }
}, },
"node_modules/stack-trace": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
"integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
"engines": {
"node": "*"
}
},
"node_modules/streamsearch": { "node_modules/streamsearch": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
@ -5125,6 +5524,11 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/text-hex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
"integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="
},
"node_modules/text-table": { "node_modules/text-table": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
@ -5175,6 +5579,14 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/triple-beam": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz",
"integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==",
"engines": {
"node": ">= 14.0.0"
}
},
"node_modules/ts-api-utils": { "node_modules/ts-api-utils": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz",
@ -5193,6 +5605,14 @@
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
"dev": true "dev": true
}, },
"node_modules/ts-nkeys": {
"version": "1.0.16",
"resolved": "https://registry.npmjs.org/ts-nkeys/-/ts-nkeys-1.0.16.tgz",
"integrity": "sha512-1qrhAlavbm36wtW+7NtKOgxpzl+70NTF8xlz9mEhiA5zHMlMxjj3sEVKWm3pGZhHXE0Q3ykjrj+OSRVaYw+Dqg==",
"dependencies": {
"tweetnacl": "^1.0.3"
}
},
"node_modules/ts-node": { "node_modules/ts-node": {
"version": "10.9.1", "version": "10.9.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
@ -5257,6 +5677,11 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
}, },
"node_modules/tweetnacl": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw=="
},
"node_modules/type-check": { "node_modules/type-check": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@ -5412,6 +5837,14 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"node_modules/urljoin": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/urljoin/-/urljoin-0.1.5.tgz",
"integrity": "sha512-OSGi+PS3zxk8XfQ+7buaupOdrW9P9p+V9rjxGzJaYEYDe/B2rv3WJCupq5LNERW4w4kWxsduUUrhCxZZiQ2udw==",
"dependencies": {
"extend": "~2.0.0"
}
},
"node_modules/use-sync-external-store": { "node_modules/use-sync-external-store": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
@ -5569,11 +6002,65 @@
"string-width": "^1.0.2 || 2 || 3 || 4" "string-width": "^1.0.2 || 2 || 3 || 4"
} }
}, },
"node_modules/winston": {
"version": "3.11.0",
"resolved": "https://registry.npmjs.org/winston/-/winston-3.11.0.tgz",
"integrity": "sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g==",
"dependencies": {
"@colors/colors": "^1.6.0",
"@dabh/diagnostics": "^2.0.2",
"async": "^3.2.3",
"is-stream": "^2.0.0",
"logform": "^2.4.0",
"one-time": "^1.0.0",
"readable-stream": "^3.4.0",
"safe-stable-stringify": "^2.3.1",
"stack-trace": "0.0.x",
"triple-beam": "^1.3.0",
"winston-transport": "^4.5.0"
},
"engines": {
"node": ">= 12.0.0"
}
},
"node_modules/winston-transport": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.6.0.tgz",
"integrity": "sha512-wbBA9PbPAHxKiygo7ub7BYRiKxms0tpfU2ljtWzb3SjRjv5yl6Ozuy/TkXf00HTAt+Uylo3gSkNwzc4ME0wiIg==",
"dependencies": {
"logform": "^2.3.2",
"readable-stream": "^3.6.0",
"triple-beam": "^1.3.0"
},
"engines": {
"node": ">= 12.0.0"
}
},
"node_modules/wrappy": { "node_modules/wrappy": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
}, },
"node_modules/ws": {
"version": "7.5.9",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
"engines": {
"node": ">=8.3.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/yallist": { "node_modules/yallist": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",

View File

@ -2,7 +2,6 @@
"name": "finvis", "name": "finvis",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"type": "module",
"scripts": { "scripts": {
"build": "next build", "build": "next build",
"dev": "next dev", "dev": "next dev",
@ -10,6 +9,7 @@
"start": "next start" "start": "next start"
}, },
"dependencies": { "dependencies": {
"@finvis/usxi": "^1.7.0",
"@t3-oss/env-nextjs": "^0.6.0", "@t3-oss/env-nextjs": "^0.6.0",
"@tanstack/react-query": "^4.32.6", "@tanstack/react-query": "^4.32.6",
"@trpc/client": "^10.37.1", "@trpc/client": "^10.37.1",
@ -17,8 +17,9 @@
"@trpc/react-query": "^10.37.1", "@trpc/react-query": "^10.37.1",
"@trpc/server": "^10.37.1", "@trpc/server": "^10.37.1",
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"framer-motion": "^10.16.4",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"framer-motion": "^10.16.4",
"mongodb": "^6.2.0",
"mongoose": "^7.5.3", "mongoose": "^7.5.3",
"next": "^13.4.19", "next": "^13.4.19",
"next-auth": "^4.23.0", "next-auth": "^4.23.0",

View File

@ -1,23 +1,24 @@
// Import required modules // Import required modules
import mongoose from 'mongoose' import mongoose from "mongoose";
import UserModel from '../models/users'; import { UserModel } from "../models/UserModel";
import * as dotenv from 'dotenv'; import * as dotenv from "dotenv";
import { api } from '../utils/api'
// Load the environment variables from the .env file // Load the environment variables from the .env file
dotenv.config(); const dbconnect = async () => {
const url = process.env.MONGODB_URI; dotenv.config();
const url = process.env.MONGODB_URI;
if (!url) { if (!url) {
throw new Error('MONGODB_URI is not defined in the .env file.'); throw new Error("MONGODB_URI is not defined in the .env file.");
} }
await mongoose
const dbconnect = async () =>{ .connect(url)
mongoose.connect(url) .then(() => {
.then(() =>{ console.log("Connected to MongoDB");
console.log('Connected to MongoDB');
}) })
.catch((error) => { .catch((error) => {
console.error('Error connecting to MongoDB:', error); console.error("Error connecting to MongoDB:", error);
}); });
} }

View File

@ -16,16 +16,16 @@ const AnimatedWord: React.FC<AnimatedWordProps> = ({
const container = { const container = {
hidden: { opacity: 0 }, hidden: { opacity: 0 },
visible: (i = 1) => ({ visible: {
opacity: 1, opacity: 1,
transition: { staggerChildren: 0.12, delayChildren: 0.04 * i }, transition: { staggerChildren: 0.12, delayChildren: 0.04 },
}), },
}; };
const child = { const child = {
visible: { visible: {
opacity: 1, opacity: 1,
y: 0, y: 0,
x:0, x: 0,
transition: { transition: {
type: "spring", type: "spring",
damping: 12, damping: 12,

View File

@ -16,16 +16,16 @@ const AnimatedText: React.FC<AnimatedTextProps> = ({
const container = { const container = {
hidden: { opacity: 0 }, hidden: { opacity: 0 },
visible: (i = 1) => ({ visible: {
opacity: 1, opacity: 1,
transition: { staggerChildren: 0.04, delayChildren: 0.04 * i }, transition: { staggerChildren: 0.04, delayChildren: 0.04 },
}), },
}; };
const child = { const child = {
visible: { visible: {
opacity: 1, opacity: 1,
y: 0, y: 0,
x:0, x: 0,
transition: { transition: {
type: "spring", type: "spring",
damping: 12, damping: 12,

View File

@ -14,7 +14,7 @@ const WavingHand: React.FC = () => {
transition={{ transition={{
delay: .7, delay: .7,
duration: 1, duration: 1,
onComplete : () => { onComplete: () => {
const wavinghand = document.getElementById("waving-hand"); const wavinghand = document.getElementById("waving-hand");
if (wavinghand) { if (wavinghand) {
wavinghand.style.display="none"; wavinghand.style.display="none";

View File

@ -22,10 +22,12 @@ const Sidebar: React.FC = () => {
setSidebarOpen(false); setSidebarOpen(false);
}; };
const goToLogin = () => { const goToLogin = () => {
router.push("/Login"); router.push("/Login")
.catch((err) => console.log(`Failed to navigate to login page with error: ${err}`));
}; };
const goToSignup = () => { const goToSignup = () => {
router.push("/Signup"); router.push("/Signup")
.catch((err) => console.log(`Failed to navigate to signup page with error: ${err}`));
}; };
const barItems: SidebarItem[] = [ const barItems: SidebarItem[] = [
{ {

120
src/models/UserModel.ts Normal file
View File

@ -0,0 +1,120 @@
import mongoose, {model, Schema } from 'mongoose';
import { type Action, ActionSide, type Position } from '@finvis/usxi';
interface InterfaceUsers{
email: string;
username: string;
password: string;
apiKeys: {
name: string;
key: string;
iv: string;
}[];
value:{
amount: number;
date: string;
}[];
balance: {
amount: number;
date: string;
}[];
trades: Action[];
positions: Position[];
papervalue:{
amount: number;
date: string;
}[];
paperbalance: {
amount: number;
date: string;
}[];
papertrades: Action[];
paperpositions: Position[];
}
const userSchema = new Schema<InterfaceUsers>({
email: {
type: String,
required: true,
unique: true
},
username: {
type: String,
required: true,
// Ensure username is unique in the dataset:
unique: true,
},
password: {
type: String,
required: true,
},
apiKeys: [
{
name: String,
key: String,
iv: String,
},
],
value: [
{
amount: Number,
date: String,
}
],
balance: [
{
amount: Number,
date: String,
}
],
trades: [
{
symbol: String,
quantity: Number,
side: ActionSide,
pricePerShare: Number,
timestamp: Date,
}
],
positions: [
{
symbol: String,
quantity: Number,
marketValue: Number,
costBasis: Number,
pricePerShare: Number,
}
],
papervalue: [
{
amount: Number,
date: String,
}
],
paperbalance: [
{
amount: Number,
date: String,
}
],
papertrades: [
{
symbol: String,
quantity: Number,
side: ActionSide,
pricePerShare: Number,
timestamp: Date,
}
],
paperpositions: [
{
symbol: String,
quantity: Number,
marketValue: Number,
costBasis: Number,
pricePerShare: Number,
}
],
});
export const UserModel = model<InterfaceUsers>('users', userSchema);

View File

@ -1,29 +0,0 @@
import mongoose from 'mongoose';
const userSchema = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true
},
username: {
type: String,
required: true,
unique: true, // Ensures username is unique in the dataset
},
password: {
type: String,
required: true,
},
apiKeys: [
{
name: String,
key: String,
iv: String,
},
],
});
const UserModel = mongoose.model('User', userSchema);
export default UserModel;

View File

@ -3,18 +3,39 @@ import Link from "next/link";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import CustomInput from "~/components/CustomInput"; import CustomInput from "~/components/CustomInput";
import { IoReturnDownBackOutline } from "react-icons/io5"; import { IoReturnDownBackOutline } from "react-icons/io5";
import { api } from "~/utils/api";
const Login: React.FC = () => { const Login: React.FC = () => {
const [ username, setUserName ] = useState<string>(""); const [ username, setUserName ] = useState<string>("");
const [ password, setPassword ] = useState<string>(""); const [ password, setPassword ] = useState<string>("");
const mutation = api.login.Login.useMutation({
onSuccess: async () => {
alert("Log in successful");
await router.push("/");
},
onError: (error) => {
console.log(error);
},
});
const router = useRouter(); const router = useRouter();
const login = (event: React.FormEvent) => {
event.preventDefault();
mutation.mutate({
username: username,
password: password,
});
};
const returnHome = () => { const returnHome = () => {
router.push("/"); router.push("/")
.catch((err) => console.log(`Failed to navigate to home page with error: ${err}`));
}; };
const goToSignup = () => { const goToSignup = () => {
router.push("/Signup"); router.push("/Signup")
.catch((err) => console.log(`Failed to navigate to signup page with error: ${err}`));
} }
return ( return (
@ -26,7 +47,7 @@ const Login: React.FC = () => {
</div> </div>
<h1>FinVis</h1> <h1>FinVis</h1>
</div> </div>
<form className="login-form" action="POST"> <form onSubmit={login} className="login-form" action="POST">
<CustomInput <CustomInput
type="text" type="text"
onChange={(e) => { onChange={(e) => {

112
src/pages/Settings.tsx Normal file
View File

@ -0,0 +1,112 @@
import React, { useState } from 'react';
import Collapsible from 'react-collapsible';
import Layout from "~/components/global/Layout";
import { api } from "~/utils/api";
// ... (other imports and code)
const Settings: React.FC = () => {
const [apiKey, setApiKey] = useState({
username: '',
apiKey: {
name: '',
key: '',
iv: '',
},
});
const [password, setPassword] = useState({
oldPass: '',
newPass: '',
newConfirm: '',
});
const addAPIKeys = api.user.addUserApiKey.useMutation();
const changePassword = api.user.changeUserPass.useMutation();
const handleAddApiKey = async () => {
try {
await addAPIKeys.mutate({ ...apiKey });
} catch (error) {
console.error('Error adding API key:', error);
}
};
const handleChangePassword = async () => {
try {
await changePassword.mutate({ ...password, username: apiKey.username });
// Optionally, you can handle success here, e.g., show a success message.
} catch (error) {
// Handle errors, e.g., show an error message.
console.error('Error changing password:', error);
}
};
return (
<Layout>
<div className="flex flex-col justify-center items-center h-full">
<Collapsible trigger="Account">
</Collapsible>
<Collapsible trigger="Privacy and Security">
{/* Privacy and Security content here */}
<p>Privacy and Security</p>
<p>ADD API KEYS</p>
<input
type="text"
placeholder="Username"
value={apiKey.username}
onChange={(e) => setApiKey({ ...apiKey, username: e.target.value })}
/>
<input
type="text"
placeholder="API Key Name"
value={apiKey.apiKey.name}
onChange={(e) => setApiKey({ ...apiKey, apiKey: { ...apiKey.apiKey, name: e.target.value } })}
/>
<input
type="text"
placeholder="API Key"
value={apiKey.apiKey.key}
onChange={(e) => setApiKey({ ...apiKey, apiKey: { ...apiKey.apiKey, key: e.target.value } })}
/>
<input
type="text"
placeholder="IV"
value={apiKey.apiKey.iv}
onChange={(e) => setApiKey({ ...apiKey, apiKey: { ...apiKey.apiKey, iv: e.target.value } })}
/>
<button onClick={handleAddApiKey}>Add API Key</button>
{/* Change Password feature */}
<p>CHANGE PASSWORD</p>
<input
type="password"
placeholder="Old Password"
value={password.oldPass}
onChange={(e) => setPassword({ ...password, oldPass: e.target.value })}
/>
<input
type="password"
placeholder="New Password"
value={password.newPass}
onChange={(e) => setPassword({ ...password, newPass: e.target.value })}
/>
<input
type="password"
placeholder="Confirm New Password"
value={password.newConfirm}
onChange={(e) => setPassword({ ...password, newConfirm: e.target.value })}
/>
<button onClick={handleChangePassword}>Change Password</button>
</Collapsible>
<Collapsible trigger="Help and Support">
<p>Contact Mr. Girouard</p>
</Collapsible>
</div>
</Layout>
);
};
export default Settings;

View File

@ -3,20 +3,42 @@ import Link from "next/link";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import CustomInput from "~/components/CustomInput"; import CustomInput from "~/components/CustomInput";
import { IoReturnDownBackOutline } from "react-icons/io5"; import { IoReturnDownBackOutline } from "react-icons/io5";
import { api } from "~/utils/api";
const Signup: React.FC = () => { const Signup: React.FC = () => {
const [ username, setUserName ] = useState<string>(""); const [ username, setUserName ] = useState<string>("");
const [ email, setEmail ] = useState<string>(""); const [ email, setEmail ] = useState<string>("");
const [ password, setPassword ] = useState<string>(""); const [ password, setPassword ] = useState<string>("");
const mutation = api.login.create_account.useMutation({
onSuccess: async () => {
alert("Registration Complete! Now login.");
await router.push("/Login");
},
onError: (error) => {
console.log(error);
},
});
const router = useRouter(); const router = useRouter();
const returnHome = () => { const returnHome = () => {
router.push("/"); router.push("/")
.catch((err) => console.log(`Failed to navigate to home page with error: ${err}`));
}; };
const goToLogin = () => { const goToLogin = () => {
router.push("/Login"); router.push("/Login")
} .catch((err) => console.log(`Failed to navigate to login page with error: ${err}`));
};
const signup = (event: React.FormEvent) => {
event.preventDefault();
mutation.mutate({
email: email,
username: username,
password: password,
});
};
return ( return (
<div className="signup-container"> <div className="signup-container">
<IoReturnDownBackOutline onClick={returnHome} className="return" /> <IoReturnDownBackOutline onClick={returnHome} className="return" />
@ -26,7 +48,7 @@ const Signup: React.FC = () => {
</div> </div>
<h1>FinVis</h1> <h1>FinVis</h1>
</div> </div>
<form className="signup-form" action="POST"> <form onSubmit={signup} className="signup-form" action="POST">
<CustomInput <CustomInput
type="text" type="text"
onChange={(e) => { onChange={(e) => {

View File

@ -8,6 +8,7 @@ import "~/styles/globals.css";
import "~/styles/sidebar.css"; import "~/styles/sidebar.css";
import "~/styles/login.css"; import "~/styles/login.css";
import "~/styles/signup.css"; import "~/styles/signup.css";
import "~/styles/collapsible.css";
const MyApp: AppType<{ session: Session | null }> = ({ const MyApp: AppType<{ session: Session | null }> = ({
Component, Component,

View File

@ -11,35 +11,36 @@ import { api } from "~/utils/api";
import Layout from "~/components/global/Layout"; import Layout from "~/components/global/Layout";
export default function Home() { export default function Home() {
const hello = api.example.hello.useQuery({ text: "from tRPC" });
const { data: sessionData } = useSession(); const { data: sessionData } = useSession();
const router = useRouter(); const router = useRouter();
const container = { const container = {
hidden: { opacity: 0 }, hidden: { opacity: 0 },
visible: (i = 1) => ({ visible: {
opacity: 1, opacity: 1,
transition: { delay: 0.5 }, transition: { delay: 0.5 },
}), },
}; };
const goToSignup = () => { const goToSignup = () => {
router.push("/Signup"); router.push("/Signup")
.catch((err) => console.log(`Failed to navigate to signup page with error: ${err}`));
} }
const goToLogin = () => { const goToLogin = () => {
router.push("/Login"); router.push("/Login")
.catch((err) => console.log(`Failed to navigate to login page with error: ${err}`));
} }
return ( return (
<> <>
<Layout> <Layout>
<div className="home-container"> <div className="home-container flex justify-center items-center h-full ">
{sessionData ? ( {sessionData ? (
<div className="signed-in"></div> <div className="signed-in"></div>
) : ( ) : (
<div className="not-signed-in mt-25 flex flex-col items-center justify-center"> <div className="not-signed-in flex flex-col items-center justify-center -mt-14">
<div className="welcome-message flex"> <div className="welcome-message flex">
<WavingHand /> <WavingHand />
<AnimatedWord <AnimatedWord
@ -66,7 +67,8 @@ export default function Home() {
className="login-signup flex flex-col items-center justify-center mt-20" className="login-signup flex flex-col items-center justify-center mt-20"
> >
<motion.button <motion.button
className="signup-button w-60 rounded-lg bg-initial-home-buttons pb-3 pl-4 pr-4 pt-3 text-xl font-bold text-gray-700" className="signup-button w-60 rounded-lg bg-initial-home-buttons pb-3 pl-4 pr-4 pt-3 text-xl
font-bold text-gray-700"
whileHover={{ scale: 1.1 }} whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }} whileTap={{ scale: 0.9 }}
onClick={goToSignup} onClick={goToSignup}
@ -78,7 +80,8 @@ export default function Home() {
whileHover={{ scale: 1.1 }} whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }} whileTap={{ scale: 0.9 }}
onClick={goToLogin} onClick={goToLogin}
className="login-button w-60 rounded-lg bg-initial-home-buttons pb-3 pl-4 pr-4 pt-3 text-xl font-bold text-gray-700" className="login-button w-60 rounded-lg bg-initial-home-buttons pb-3 pl-4 pr-4 pt-3 text-xl
font-bold text-gray-700"
> >
Login Login
</motion.button> </motion.button>
@ -91,6 +94,8 @@ export default function Home() {
); );
} }
// Commenting out as this is just for the default example router
/*
const AuthShowcase = () => { const AuthShowcase = () => {
const { data: sessionData } = useSession(); const { data: sessionData } = useSession();
@ -119,3 +124,4 @@ const AuthShowcase = () => {
</div> </div>
); );
}; };
*/

View File

@ -1,6 +1,6 @@
import { exampleRouter } from "~/server/api/routers/example";
import { createTRPCRouter } from "~/server/api/trpc"; import { createTRPCRouter } from "~/server/api/trpc";
import { loginRouter } from "./routers/login"; import { loginRouter } from "./routers/login";
import { userRouter } from "./routers/user";
/** /**
* This is the primary router for your server. * This is the primary router for your server.
@ -8,8 +8,8 @@ import { loginRouter } from "./routers/login";
* All routers added in /api/routers should be manually added here. * All routers added in /api/routers should be manually added here.
*/ */
export const appRouter = createTRPCRouter({ export const appRouter = createTRPCRouter({
example: exampleRouter,
login: loginRouter, login: loginRouter,
user: userRouter,
}); });
// export type definition of API // export type definition of API

View File

@ -1,21 +0,0 @@
import { z } from "zod";
import {
createTRPCRouter,
protectedProcedure,
publicProcedure,
} from "~/server/api/trpc";
export const exampleRouter = createTRPCRouter({
hello: publicProcedure
.input(z.object({ text: z.string() }))
.query(({ input }) => {
return {
greeting: `Hello ${input.text}`,
};
}),
getSecretMessage: protectedProcedure.query(() => {
return "you can now see this secret message!";
}),
});

View File

@ -6,7 +6,9 @@ import {
protectedProcedure, protectedProcedure,
publicProcedure, publicProcedure,
} from "~/server/api/trpc"; } from "~/server/api/trpc";
import { TRPCError } from "@trpc/server";
import { UserModel } from "~/models/UserModel";
// Main login router for backend // Main login router for backend
export const loginRouter = createTRPCRouter({ export const loginRouter = createTRPCRouter({
@ -15,46 +17,58 @@ export const loginRouter = createTRPCRouter({
.input(z.object({email: z.string(), username: z.string(), password: z.string()})) .input(z.object({email: z.string(), username: z.string(), password: z.string()}))
.mutation(async ({ ctx, input }) => { .mutation(async ({ ctx, input }) => {
// Encryption using bcrypt salt and pepper hashing // Encryption using bcrypt salt and pepper hashing
const user = await UserModel.findOne({username: input.username});
const userEmail = await UserModel.findOne({email: input.email});
if (user) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "Username is already taken!",
})
}
if (userEmail) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "Email is already taken!",
})
}
const saltRounds = 10; const saltRounds = 10;
let password_hash = ""; const hashedPassword: string = await bcrypt.hash(input.password, saltRounds);
bcrypt.hash(input.password, saltRounds, (err,hash) => {
password_hash = hash;
});
// Store in database // Store in database
const userModelData = ctx.UserModel().create({ const newUser = await UserModel.create({username: input.username,
username: input.username, email: input.email ,
password: password_hash, password: hashedPassword });
email: input.email, await newUser.save();
}); // Return greeting Żnformation
// Return greeting information
return { return {
user_info: userModelData user_info: newUser
}; };
}), }),
login: publicProcedure Login: publicProcedure
.input(z.object({ username: z.string(), password: z.string(), })) .input(z.object({ username: z.string(), password: z.string()}))
.query(async ({ ctx, input }) => { .mutation(async ({ ctx, input }) => {
// Encryption using bcrypt salt and pepper hashing // Encryption using bcrypt salt and pepper hashing
const saltRounds = 10; const saltRounds = 10;
// Get hash from database // Get hash from database
const password_hash = ctx.UserModel.find({username: input.username},'password'); const user = await UserModel.findOne({username: input.username});
let is_valid = false; if (!user) {
bcrypt.compare(input.password, password_hash, (err, result) => { throw new TRPCError ({
// returns result code: "BAD_REQUEST",
is_valid = result; message: "User doesn't exist",
}); })
// Return for sign in
if(is_valid) {
return {
user: ctx.UserModel.find({username: input.username}),
success: true,
};
} }
const isPasswordValid = await bcrypt.compare(input.password, user.password);
if (!isPasswordValid) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "Username or Password is incorrect",
})
}
// Return for sign in
return { return {
user: null, user: user,
success: false, success: true,
}; message: "Successfully logged in!",
}
}), }),
}); });

View File

@ -0,0 +1,500 @@
import { z } from "zod";
import bcrypt from "bcrypt";
import { type Action } from '@finvis/usxi'
import {
createTRPCRouter,
protectedProcedure,
publicProcedure,
} from "~/server/api/trpc";
import { UserModel } from "~/models/UserModel";
import { TRPCError } from "@trpc/server";
// Main login router for backend
export const userRouter = createTRPCRouter({
// Function to add a Api Key to a user
addUserApiKey: protectedProcedure
.input(z.object({username: z.string(), apiKey: z.object({name: z.string(), key: z.string(), iv: z.string()}) }))
.mutation(async ({ ctx, input }) => {
const user = await UserModel.findOne({username: input.username});
// Check if user exists
if (!user) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User not found",
})
}
// Add the new API key to the user's array
user.apiKeys.push(input.apiKey);
// Save new user
await user.save();
// Return greeting information
return {
user_info: user
};
}),
// Function to get a users Api Keys
getUserApiKeys: protectedProcedure
.input(z.object({ username: z.string()}))
.query(async ({ ctx, input }) => {
const user = await UserModel.findOne({username: input.username});
// Check if user exists
if (!user) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User doesn't exist",
})
}
// Check if user has API keys
if (!user.apiKeys || user.apiKeys.length) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User has no api keys",
})
}
// Return a list of the users API keys
return user.apiKeys
}),
// Function to change a users password
changeUserPass: protectedProcedure
.input(z.object({username: z.string(), oldPass: z.string(), newPass: z.string(), newConfirm: z.string()}))
.mutation(async ({ ctx, input }) => {
const user = await UserModel.findOne({username: input.username});
// Check if user exists
if (!user) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User not found",
})
}
// Check password validity
const isPasswordValid = await bcrypt.compare(input.oldPass, user.password);
if (!isPasswordValid) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "Username or Password is incorrect",
})
}
// Check new passwords
if (input.newPass !== input.newConfirm) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "New passwords do not match",
})
}
const saltRounds = 10;
const hashedPassword: string = await bcrypt.hash(input.newPass, saltRounds);
// Update password
user.password = hashedPassword
// Save new user information
await user.save();
// Return updated information
return {
user_info: user
};
}),
// Add new balance to user
putUserBalanceHist: protectedProcedure
.input(z.object({username: z.string(), balance: z.object({ amount: z.number(), date: z.string() })}))
.mutation(async ({ ctx, input }) => {
const user = await UserModel.findOne({username: input.username});
// Check if user exists
if (!user) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User not found",
})
}
user.balance.push(input.balance);
// Save new user information
await user.save();
// Return updated information
return {
user_info: user
};
}),
// Function to get a users balance history
getUserBalanceHist: protectedProcedure
.input(z.object({ username: z.string(), amount: z.number() }))
.query(async ({ ctx, input }) => {
const user = await UserModel.findOne({username: input.username});
// Check if user exists
if (!user) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User doesn't exist",
})
}
// Check if user has API keys
if (!user.balance || user.balance.length === 0) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User has no balance history",
})
}
if(user.balance.length < input.amount) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User only has length ${user.balance.length} balance history",
})
}
if(input.amount === -1){
return user.balance
}
const lastBalances = await UserModel.findOne({username: input.username})
// Select only balance
.select('balance')
// Get last n items
.slice('balance', -input.amount)
// Convert to JavaScript object
.lean()
return lastBalances
}),
// Add new trade to user
putUserTradeHist: protectedProcedure
.input(z.object({username: z.string(), trade: z.custom<Action>()}))
.mutation(async ({ ctx, input }) => {
const user = await UserModel.findOne({username: input.username});
// Check if user exists
if (!user) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User not found",
})
}
user.trades.push(input.trade);
// Save new user information
await user.save();
// Return updated information
return {
user_info: user
};
}),
// Function to get a users Trade history
getUserTradeHist: protectedProcedure
.input(z.object({ username: z.string(), amount: z.number() }))
.query(async ({ ctx, input }) => {
const user = await UserModel.findOne({username: input.username});
// Check if user exists
if (!user) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User doesn't exist",
})
}
// Check if user has API keys
if (!user.trades || user.trades.length === 0) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User has no trades history",
})
}
if(user.trades.length < input.amount) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User only has length ${user.trades.length} balance history",
})
}
if(input.amount === -1){
return user.trades
}
const lastTrades = await UserModel.findOne({username: input.username})
// Select only balance
.select('trades')
// Get last n items
.slice('trades', -input.amount)
// Convert to JavaScript object
.lean()
return lastTrades
}),
// Add new position to user
putUserPositions: protectedProcedure
.input(z.object({username: z.string(), position: z.object({ symbol: z.string(),
quantity: z.number(), marketValue: z.number(), costBasis: z.number(), pricePerShare: z.number(), })}))
.mutation(async ({ ctx, input }) => {
const user = await UserModel.findOne({username: input.username});
// Check if user exists
if (!user) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User not found",
})
}
user.positions.push(input.position);
// Save new user information
await user.save();
// Return updated information
return {
user_info: user
};
}),
// Function to get a users positions
getUserPositions: protectedProcedure
.input(z.object({ username: z.string()}))
.query(async ({ ctx, input }) => {
const user = await UserModel.findOne({username: input.username});
// Check if user exists
if (!user) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User doesn't exist",
})
}
// Check if user has API keys
if (!user.positions || user.positions.length === 0) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User has no positions",
})
}
return user.positions
}),
//Paper trading function
// Add new balance to user
putPaperBalanceHist: protectedProcedure
.input(z.object({username: z.string(), balance: z.object({ amount: z.number(), date: z.string() })}))
.mutation(async ({ ctx, input }) => {
const user = await UserModel.findOne({username: input.username});
// Check if user exists
if (!user) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User not found",
})
}
user.paperbalance.push(input.balance);
// Save new user information
await user.save();
// Return updated information
return {
user_info: user
};
}),
// Function to get a users balance history
getPaperBalanceHist: protectedProcedure
.input(z.object({ username: z.string(), amount: z.number() }))
.query(async ({ ctx, input }) => {
const user = await UserModel.findOne({username: input.username});
// Check if user exists
if (!user) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User doesn't exist",
})
}
// Check if user has API keys
if (!user.paperbalance || user.paperbalance.length === 0) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User has no balance history",
})
}
if(user.paperbalance.length < input.amount) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User only has length ${user.balance.length} balance history",
})
}
if(input.amount === -1){
return user.paperbalance
}
const lastBalances = await UserModel.findOne({username: input.username})
// Select only balance
.select('paperbalance')
// Get last n items
.slice('paperbalance', -input.amount)
// Convert to JavaScript object
.lean()
return lastBalances
}),
// Add new trade to user
putPaperTradeHist: protectedProcedure
.input(z.object({username: z.string(), trade: z.custom<Action>()}))
.mutation(async ({ ctx, input }) => {
const user = await UserModel.findOne({username: input.username});
// Check if user exists
if (!user) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User not found",
})
}
user.papertrades.push(input.trade);
// Save new user information
await user.save();
// Return updated information
return {
user_info: user
};
}),
// Function to get a users Trade history
getPaperTradeHist: protectedProcedure
.input(z.object({ username: z.string(), amount: z.number() }))
.query(async ({ ctx, input }) => {
const user = await UserModel.findOne({username: input.username});
// Check if user exists
if (!user) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User doesn't exist",
})
}
// Check if user has API keys
if (!user.papertrades || user.papertrades.length === 0) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User has no trades history",
})
}
if(user.papertrades.length < input.amount) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User only has length ${user.trades.length} balance history",
})
}
if(input.amount === -1){
return user.papertrades
}
const lastTrades = await UserModel.findOne({username: input.username})
// Select only balance
.select('papertrades')
// Get last n items
.slice('papertrades', -input.amount)
// Convert to JavaScript object
.lean()
return lastTrades
}),
// Add new position to user
putPaperPositions: protectedProcedure
.input(z.object({username: z.string(), position: z.object({ symbol: z.string(),
quantity: z.number(), marketValue: z.number(), costBasis: z.number(), pricePerShare: z.number(), })}))
.mutation(async ({ ctx, input }) => {
const user = await UserModel.findOne({username: input.username});
// Check if user exists
if (!user) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User not found",
})
}
user.paperpositions.push(input.position);
// Save new user information
await user.save();
// Return updated information
return {
user_info: user
};
}),
// Function to get a users positions
getPaperPositions: protectedProcedure
.input(z.object({ username: z.string()}))
.query(async ({ ctx, input }) => {
const user = await UserModel.findOne({username: input.username});
// Check if user exists
if (!user) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User doesn't exist",
})
}
// Check if user has positions
if (!user.paperpositions || user.paperpositions.length === 0) {
throw new TRPCError ({
code: "BAD_REQUEST",
message: "User has no positions",
})
}
return user.paperpositions
}),
});

View File

@ -11,7 +11,7 @@ import { type CreateNextContextOptions } from "@trpc/server/adapters/next";
import { type Session } from "next-auth"; import { type Session } from "next-auth";
import superjson from "superjson"; import superjson from "superjson";
import { ZodError } from "zod"; import { ZodError } from "zod";
import { dbconnect } from "~/clients/mongoose";
import { getServerAuthSession } from "~/server/auth"; import { getServerAuthSession } from "~/server/auth";
/** /**
@ -36,11 +36,11 @@ interface CreateContextOptions {
* *
* @see https://create.t3.gg/en/usage/trpc#-serverapitrpcts * @see https://create.t3.gg/en/usage/trpc#-serverapitrpcts
*/ */
const createInnerTRPCContext = (opts: CreateContextOptions) => { const createInnerTRPCContext = async (opts: CreateContextOptions) => {
// Connect our database in inner context // Connect our database in inner context
await dbconnect();
return { return {
session: opts.session, session: opts.session,
}; };
}; };
@ -80,6 +80,7 @@ const t = initTRPC.context<typeof createTRPCContext>().create({
zodError: zodError:
error.cause instanceof ZodError ? error.cause.flatten() : null, error.cause instanceof ZodError ? error.cause.flatten() : null,
}, },
}; };
}, },
}); });

View File

@ -0,0 +1,29 @@
.Collapsible {
width: 85%;
}
.Collapsible__contentInner {
padding: 10px;
background-color: rgb(223, 216, 216);
border: 1px solid white;
border-top: 0;
p {
margin-bottom: 10px;
font-size: 14px;
line-height: 20px;
}
}
.Collapsible__trigger {
display: block;
font-weight: 700;
font-family: "Poppins", sans-serif;
position: relative;
padding: 10px;
background: #186d49;
color: white;
}

View File

@ -89,7 +89,7 @@
.login-form .input-box input:valid ~ i, .login-form .input-box input:valid ~ i,
.login-form .input-box input:focus ~ i { .login-form .input-box input:focus ~ i {
height: 6vh; height: 6.8vh;
} }
.login-form .forgot-pw { .login-form .forgot-pw {
display: inline-flex; display: inline-flex;

View File

@ -40,7 +40,7 @@
} }
.signup-form .input-box:first-child { .signup-form .input-box:first-child {
position: relative; position: relative;
margin-top: 4vh; margin-top: 2vh;
} }
.signup-form .input-box input { .signup-form .input-box input {
position: relative; position: relative;
@ -93,7 +93,7 @@
.signup-form .input-box input:valid ~ i, .signup-form .input-box input:valid ~ i,
.signup-form .input-box input:focus ~ i { .signup-form .input-box input:focus ~ i {
height: 6vh; height: 6.8vh;
} }
.signup-form .forgot-pw { .signup-form .forgot-pw {
display: inline-flex; display: inline-flex;

View File

@ -12,7 +12,7 @@ export default {
}, },
backgroundColor : { backgroundColor : {
'initial-home-buttons' : '#64a88c', 'initial-home-buttons' : '#64a88c',
} },
}, },
}, },
plugins: [], plugins: [],