diff --git a/.gitignore b/.gitignore index 5ef6a52..64e4035 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ yarn-error.log* # env files (can opt-in for committing if needed) .env* +.env # vercel .vercel diff --git a/package-lock.json b/package-lock.json index f15a1ff..0ffb686 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,10 @@ "name": "project2-f25", "version": "0.1.0", "dependencies": { + "argon2": "^0.44.0", + "date-fns": "^4.1.0", + "mongodb": "^7.1.1", + "mongoose": "^9.3.1", "next": "16.0.1", "react": "19.2.0", "react-dom": "19.2.0" @@ -44,6 +48,11 @@ "tslib": "^2.4.0" } }, + "node_modules/@epic-web/invariant": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz", + "integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==" + }, "node_modules/@img/colour": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", @@ -522,6 +531,14 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.6.tgz", + "integrity": "sha512-y+x3H1xBZd38n10NZF/rEBlvDOOMQ6LKUTHqr8R9VkJ+mmQOYtJFxIlkkK8fZrtOiL6VixbOBWMbZGBdal3Z1g==", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, "node_modules/@next/env": { "version": "16.0.1", "resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.1.tgz", @@ -656,6 +673,14 @@ "node": ">= 10" } }, + "node_modules/@phc/format": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz", + "integrity": "sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==", + "engines": { + "node": ">=10" + } + }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -966,6 +991,42 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" + }, + "node_modules/@types/whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/argon2": { + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.44.0.tgz", + "integrity": "sha512-zHPGN3S55sihSQo0dBbK0A5qpi2R31z7HZDZnry3ifOyj8bZZnpZND2gpmhnRGO1V/d555RwBqIK5W4Mrmv3ig==", + "hasInstallScript": true, + "dependencies": { + "@phc/format": "^1.0.0", + "cross-env": "^10.0.0", + "node-addon-api": "^8.5.0", + "node-gyp-build": "^4.8.4" + }, + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/bson": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-7.2.0.tgz", + "integrity": "sha512-YCEo7KjMlbNlyHhz7zAZNDpIpQbd+wOEHJYezv0nMYTn4x31eIUM2yomNNubclAt63dObUzKHWsBLJ9QcZNSnQ==", + "engines": { + "node": ">=20.19.0" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001751", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001751.tgz", @@ -992,6 +1053,35 @@ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, + "node_modules/cross-env": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz", + "integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==", + "dependencies": { + "@epic-web/invariant": "^1.0.0", + "cross-spawn": "^7.0.6" + }, + "bin": { + "cross-env": "dist/bin/cross-env.js", + "cross-env-shell": "dist/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -999,6 +1089,15 @@ "dev": true, "license": "MIT" }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -1030,6 +1129,11 @@ "dev": true, "license": "ISC" }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, "node_modules/jiti": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", @@ -1040,6 +1144,14 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/kareem": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-3.2.0.tgz", + "integrity": "sha512-VS8MWZz/cT+SqBCpVfNN4zoVz5VskR3N4+sTmUXme55e9avQHntpwpNq0yjnosISXqwJ3AQVjlbI4Dyzv//JtA==", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/lightningcss": { "version": "1.30.2", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", @@ -1311,6 +1423,109 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" + }, + "node_modules/mongodb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.1.1.tgz", + "integrity": "sha512-067DXiMjcpYQl6bGjWQoTUEE9UoRViTtKFcoqX7z08I+iDZv/emH1g8XEFiO3qiDfXAheT5ozl1VffDTKhIW/w==", + "dependencies": { + "@mongodb-js/saslprep": "^1.3.0", + "bson": "^7.1.1", + "mongodb-connection-string-url": "^7.0.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.806.0", + "@mongodb-js/zstd": "^7.0.0", + "gcp-metadata": "^7.0.1", + "kerberos": "^7.0.0", + "mongodb-client-encryption": ">=7.0.0 <7.1.0", + "snappy": "^7.3.2", + "socks": "^2.8.6" + }, + "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": "7.0.1", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.1.tgz", + "integrity": "sha512-h0AZ9A7IDVwwHyMxmdMXKy+9oNlF0zFoahHiX3vQ8e3KFcSP3VmsmfvtRSuLPxmyv2vjIDxqty8smTgie/SNRQ==", + "dependencies": { + "@types/whatwg-url": "^13.0.0", + "whatwg-url": "^14.1.0" + }, + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/mongoose": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.3.1.tgz", + "integrity": "sha512-58DuQti+LlRS74/UfWN4F3wZsC0Yr1dgTWZ2Wd3/TuSvm6rIdyAjDWbx2xGyuBooqJYdAWotVv4mQgVdivh+3Q==", + "dependencies": { + "kareem": "3.2.0", + "mongodb": "~7.1", + "mpath": "0.9.0", + "mquery": "6.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-6.0.0.tgz", + "integrity": "sha512-b2KQNsmgtkscfeDgkYMcWGn9vZI9YoXh802VDEwE6qc50zxBFQ0Oo8ROkawbPAsXCY1/Z1yp0MagqsZStPWJjw==", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -1409,6 +1624,32 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/node-addon-api": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.6.0.tgz", + "integrity": "sha512-gBVjCaqDlRUk0EwoPNKzIr9KkS9041G/q31IBShPs1Xz6UTA+EXdZADbzqAJQrpDRq71CIMnOP5VMut3SL0z5Q==", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -1444,6 +1685,14 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, "node_modules/react": { "version": "19.2.0", "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", @@ -1527,6 +1776,30 @@ "@img/sharp-win32-x64": "0.34.4" } }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==" + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -1536,6 +1809,14 @@ "node": ">=0.10.0" } }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", @@ -1580,6 +1861,17 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -1606,6 +1898,40 @@ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } } } } diff --git a/package.json b/package.json index 693e5fc..d73c564 100644 --- a/package.json +++ b/package.json @@ -8,16 +8,20 @@ "start": "next start" }, "dependencies": { + "argon2": "^0.44.0", + "date-fns": "^4.1.0", + "mongodb": "^7.1.1", + "mongoose": "^9.3.1", + "next": "16.0.1", "react": "19.2.0", - "react-dom": "19.2.0", - "next": "16.0.1" + "react-dom": "19.2.0" }, "devDependencies": { - "typescript": "^5", + "@tailwindcss/postcss": "^4", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", - "@tailwindcss/postcss": "^4", - "tailwindcss": "^4" + "tailwindcss": "^4", + "typescript": "^5" } } diff --git a/server/mongodb/actions/animal.ts b/server/mongodb/actions/animal.ts new file mode 100644 index 0000000..6986ea1 --- /dev/null +++ b/server/mongodb/actions/animal.ts @@ -0,0 +1,27 @@ +import { AnimalData } from "@/types/types"; +import animal from "../models/Animal"; + +export async function createAnimal(animalData: AnimalData) { + const newAnimal = new animal(animalData); + await newAnimal.save(); + return newAnimal; +} + +export async function getAnimal(animalId: string) { + const retrievedAnimal = await animal.findById(animalId); + return retrievedAnimal; +} + +export async function getAllAnimals() { + const allAnimals = await animal.find({}); + return allAnimals; +} + +export async function updateAnimal(animalId: string, newData: AnimalData) { + const updatedAnimal = await animal.findByIdAndUpdate(animalId, newData); + return updatedAnimal; +} + +export async function deleteAnimal(animalId: string) { + await animal.findByIdAndDelete(animalId); +} \ No newline at end of file diff --git a/server/mongodb/actions/training.ts b/server/mongodb/actions/training.ts new file mode 100644 index 0000000..fa9f385 --- /dev/null +++ b/server/mongodb/actions/training.ts @@ -0,0 +1,18 @@ +import { TrainingData } from "@/types/types"; +import trainingData from "../models/Training"; + +export async function createLog(log: TrainingData) { + const newLog = new trainingData(log); + await newLog.save(); + return newLog; +} + +export async function updateLog(logId: string, newData: TrainingData) { + const updatedLog = await trainingData.findByIdAndUpdate(logId, newData); + return updatedLog; +} + +export async function getAllLogs() { + const allLogs = await trainingData.find({}); + return allLogs; +} \ No newline at end of file diff --git a/server/mongodb/actions/user.ts b/server/mongodb/actions/user.ts new file mode 100644 index 0000000..a55865a --- /dev/null +++ b/server/mongodb/actions/user.ts @@ -0,0 +1,32 @@ +import { UserData } from "@/types/types"; +import user from "../models/User"; + +export async function createUser(userData : UserData) { + const newUser = new user(userData); + await newUser.save(); + return newUser; +} + +export async function getAllUsers() { + const retrievedUser = await user.find({}).select("-password"); + return retrievedUser; +} + +export async function updateUser(userId: string, newData : UserData) { + const updatedUser = await user.findByIdAndUpdate(userId, newData); + return updatedUser; +} + +export async function getUser(userId: string){ + const retrievedUser = await user.findById(userId); + return retrievedUser; +} + +export async function getUserByEmail(email: string) { + return await user.findOne({email}); +} + + +export async function deleteUser(userId: string) { + await user.findByIdAndDelete(userId); +} \ No newline at end of file diff --git a/server/mongodb/connectDb.ts b/server/mongodb/connectDb.ts new file mode 100644 index 0000000..955f436 --- /dev/null +++ b/server/mongodb/connectDb.ts @@ -0,0 +1,9 @@ +import mongoose from "mongoose"; + +export default async function connectDb() { + try { + await mongoose.connect(process.env.DB_URL!); + } catch (e) { + console.log("Unable to connect", e); + } +} \ No newline at end of file diff --git a/server/mongodb/index.js b/server/mongodb/index.js new file mode 100644 index 0000000..e69de29 diff --git a/server/mongodb/models/Animal.ts b/server/mongodb/models/Animal.ts new file mode 100644 index 0000000..1a78841 --- /dev/null +++ b/server/mongodb/models/Animal.ts @@ -0,0 +1,12 @@ +import mongoose from "mongoose" + +const animalSchema = new mongoose.Schema({ + + name: String, + breed: String, + owner: mongoose.Schema.Types.ObjectId, + hoursTrained: mongoose.Schema.Types.Number, + +}); + +export default mongoose.model("Animal", animalSchema); \ No newline at end of file diff --git a/server/mongodb/models/Training.ts b/server/mongodb/models/Training.ts new file mode 100644 index 0000000..ea7253b --- /dev/null +++ b/server/mongodb/models/Training.ts @@ -0,0 +1,16 @@ +import mongoose from "mongoose"; + + +const trainingSchema = new mongoose.Schema({ + user : mongoose.Schema.Types.ObjectId, + animal : mongoose.Schema.Types.ObjectId, + title : String, + date : Date, + description : String, + hours : mongoose.Schema.Types.Number, + + +}); + +export default mongoose.model("Training", trainingSchema); + diff --git a/server/mongodb/models/User.ts b/server/mongodb/models/User.ts new file mode 100644 index 0000000..9a198a5 --- /dev/null +++ b/server/mongodb/models/User.ts @@ -0,0 +1,23 @@ +import mongoose from "mongoose"; + +const userSchema = new mongoose.Schema({ + + fullName: { + type: String, + required: true + }, + password: { + type: String, + required: true + }, + email: { + type: String, + required: true + }, + admin: { + type: Boolean, + required: true + } +}); + +export default mongoose.model("User", userSchema); \ No newline at end of file diff --git a/src/app/favicon.ico b/src/app/favicon.ico deleted file mode 100644 index 718d6fe..0000000 Binary files a/src/app/favicon.ico and /dev/null differ diff --git a/src/app/globals.css b/src/app/globals.css deleted file mode 100644 index a2dc41e..0000000 --- a/src/app/globals.css +++ /dev/null @@ -1,26 +0,0 @@ -@import "tailwindcss"; - -:root { - --background: #ffffff; - --foreground: #171717; -} - -@theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); -} - -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } -} - -body { - background: var(--background); - color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; -} diff --git a/src/app/layout.tsx b/src/app/layout.tsx deleted file mode 100644 index f7fa87e..0000000 --- a/src/app/layout.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; -import "./globals.css"; - -const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); - -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], -}); - -export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", -}; - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - - - {children} - - - ); -} diff --git a/src/app/page.tsx b/src/app/page.tsx deleted file mode 100644 index 295f8fd..0000000 --- a/src/app/page.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import Image from "next/image"; - -export default function Home() { - return ( -
-
- Next.js logo -
-

- To get started, edit the page.tsx file. -

-

- Looking for a starting point or more instructions? Head over to{" "} - - Templates - {" "} - or the{" "} - - Learning - {" "} - center. -

-
-
- - Vercel logomark - Deploy Now - - - Documentation - -
-
-
- ); -} diff --git a/src/pages/api/admin/animals/route.ts b/src/pages/api/admin/animals/route.ts new file mode 100644 index 0000000..f3b682f --- /dev/null +++ b/src/pages/api/admin/animals/route.ts @@ -0,0 +1,38 @@ +import type { NextApiRequest, NextApiResponse } from "next"; +import { getAllAnimals } from "../../../../../server/mongodb/actions/animal"; +import connectDb from "../../../../../server/mongodb/connectDb"; +import { AnimalData } from "@/types/types"; + +interface AnimalApiData { + animals?: any[]; + message: string; +}; + +export default async function handler( + req : NextApiRequest, + res: NextApiResponse +) { + if (req.method === 'GET') { + try{ + await connectDb(); + const animals = await getAllAnimals(); + + if (!animals || animals.length === 0) { + return res.status(500).json({ + message: "There are no animals in the database" + }); + } + + return res.status(200).json({ + animals: animals, + message: "Succesfully got all animals" + }) + + } catch (e) { + res.status(500).json({ + message: "There was an error in getting the animals" + }); + } + } + +} \ No newline at end of file diff --git a/src/pages/api/admin/training/route.ts b/src/pages/api/admin/training/route.ts new file mode 100644 index 0000000..df7cb51 --- /dev/null +++ b/src/pages/api/admin/training/route.ts @@ -0,0 +1,36 @@ +import type { NextApiRequest, NextApiResponse } from "next"; +import { getAllLogs } from "../../../../../server/mongodb/actions/training"; +import connectDb from "../../../../../server/mongodb/connectDb"; + +interface TrainingApiData{ + logs?: any[]; + message: string; +} + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + if (req.method === 'GET') { + try { + await connectDb(); + const logs = await getAllLogs(); + if (!logs || logs.length === 0) { + res.status(500).json({ + message: "There are no logs to return" + }); + } + + res.status(200).json({ + logs: logs, + message: "Succesfully retrieved all logs" + }); + + } catch (e) { + res.status(500).json({ + message: "There was an error in getting the training logs" + }); + } + } + +} diff --git a/src/pages/api/admin/users/route.ts b/src/pages/api/admin/users/route.ts new file mode 100644 index 0000000..78dc879 --- /dev/null +++ b/src/pages/api/admin/users/route.ts @@ -0,0 +1,37 @@ +import type { NextApiRequest, NextApiResponse } from "next"; +import { getAllUsers } from "../../../../../server/mongodb/actions/user"; +import connectDb from "../../../../../server/mongodb/connectDb"; + + +interface UserApiData { + users?: any[]; + message: string; +} + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse, +) { + if (req.method === 'GET') { + try { + await connectDb(); + const users = await getAllUsers(); + + if (!users || users.length === 0) { + return res.status(404).json({ + message: "There are no users to get" + }); + } + + return res.status(200).json({ + users: users, + message: "Users retrieved successfully" + }); + + } catch (e) { + return res.status(500).json({ + message: "There was an error in retrieving the users" + }); + } + } +} \ No newline at end of file diff --git a/src/pages/api/admin/users/verify/route.ts b/src/pages/api/admin/users/verify/route.ts new file mode 100644 index 0000000..413d2e8 --- /dev/null +++ b/src/pages/api/admin/users/verify/route.ts @@ -0,0 +1,58 @@ +import type { NextApiRequest, NextApiResponse } from "next"; +import { UserData } from "../../../../../types/types"; +import { createUser, deleteUser, updateUser, getUserByEmail } from "../../../../../../server/mongodb/actions/user"; +import connectDb from "../../../../../../server/mongodb/connectDb"; +import * as argon2 from "argon2"; +import { connect } from "http2"; + + +interface UserApiData { + isAdmin? : boolean; + userId? : string; + message : string; +} + +export default async function handler ( + req: NextApiRequest, + res: NextApiResponse, +) { + if (req.method === 'POST') { + try { + if (!req.body.email || !req.body.password) { + res.status(500).json({ + message: "Must provide email and/or password" + }) + } + + await connectDb(); + const email = req.body.email; + const password = req.body.password; + const isAdmin = req.body.admin; + const existUser = await getUserByEmail(email); + if (!existUser) { + return res.status(500).json({ + message: "User not found" + }); + } + + if (existUser) { + const passwordCheck = await argon2.verify(existUser.password, password); + if (passwordCheck) { + return res.status(200).json({ + message: "User succesfully verified", + userId: existUser._id.toString(), + isAdmin: isAdmin, + }); + } else { + return res.status(500).json({ + message: "Incorrect password" + }); + } + } + } catch (e) { + res.status(500).json({ + message: "There was an error in verifying the user" + }); + } + } +} \ No newline at end of file diff --git a/src/pages/api/animal/route.ts b/src/pages/api/animal/route.ts new file mode 100644 index 0000000..72b15a8 --- /dev/null +++ b/src/pages/api/animal/route.ts @@ -0,0 +1,77 @@ +import type { NextApiRequest, NextApiResponse } from "next"; +import { AnimalData } from "@/types/types"; +import { getUser } from "../../../../server/mongodb/actions/user"; +import { createAnimal, deleteAnimal, updateAnimal, getAnimal} from "../../../../server/mongodb/actions/animal"; +import connectDb from "../../../../server/mongodb/connectDb"; + +interface AnimalApiData { + animalData? : AnimalData; + message: string; +} + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse, +) { + if(req.method === 'POST') { + try { + if (!req.body.name || !req.body.breed || !req.body.owner || !req.body.hoursTrained) { + res.status(500).json({ + message: "All information must be given to create an animal" + }); + } + + const animalData = { + name: req.body.name, + breed: req.body.breed, + owner: req.body.owner, + hoursTrained: req.body.hoursTrained, + } as AnimalData + + await connectDb(); + const owner = await getUser(req.body.owner); + if (!owner) { + res.status(400).json({ + message: "Owner of animal does not exist in database" + }); + } + + const animal = await createAnimal(animalData); + res.status(200).json({ + animalData: animalData, + message: "Animal creation succseful" + }); + } catch (e) { + res.status(500).json({ + message: "There was an error in adding the animal to the database" + }); + } + } else if (req.method === "PATCH") { + try { + if (!req.body._id) { + res.status(400).json({ + message: "No animal id given" + }); + }; + const {_id, ...updateData} = req.body; + await connectDb(); + const updatedAnimal = await updateAnimal(_id, updateData); + if (!updatedAnimal) { + res.status(500).json({ + message: "Failed to update animal" + }); + } + + res.status(200).json({ + animalData: updatedAnimal as unknown as AnimalData, + message: "Succesfully update animal" + }); + + } catch (e) { + res.status(500).json({ + message: "Failed to update animal" + }); + } + } + +} \ No newline at end of file diff --git a/src/pages/api/training/route.ts b/src/pages/api/training/route.ts new file mode 100644 index 0000000..2162a41 --- /dev/null +++ b/src/pages/api/training/route.ts @@ -0,0 +1,78 @@ +import type { NextApiRequest, NextApiResponse } from "next"; +import { TrainingData } from "@/types/types"; +import { getAnimal } from "../../../../server/mongodb/actions/animal"; +import {createLog, updateLog} from "../../../../server/mongodb/actions/training"; +import connectDb from "../../../../server/mongodb/connectDb"; +import { isValid, isFuture} from "date-fns"; + +interface LogApiData{ + logData? : TrainingData; + message : string; +} + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + if (req.method === 'POST') { + try { + if (!req.body.user || !req.body.animal || !req.body.title || !req.body.date || !req.body.description || !req.body.hours) { + res.status(500).json({ + message: "You must include all information to create a training log" + }); + } + const checkDate = (date: string | Date) => { + if (!isValid(date)) return false; + if (isFuture(date)) return false; + return true; + }; + + if (!checkDate) { + res.status(400).json({ + message : "invalid date given" + }); + } + + + const animalId = req.body.animal; + const user = req.body.user; + await connectDb(); + const animalInfo = await getAnimal(animalId); + const animalOwner = animalInfo?.owner; + if (!animalOwner) { + res.status(400).json({ + message: "Failed to find animal given" + }); + } + if (animalOwner != user) { + res.status(400).json({ + message : "Animal owner and owner given are not the same" + }); + } + + const logData = { + user : req.body.user, + animal : req.body.animal, + title : req.body.title, + date : req.body.date, + description : req.body.description, + hours : req.body.hours + }; + + const log = await createLog(logData) + + + res.status(200).json({ + logData : logData, + message: "Succesfully created log" + }); + + + } catch (e) { + res.status(500).json({ + message: "There was an issue in creating the training log" + }); + + } + } +} diff --git a/src/pages/api/user/route.ts b/src/pages/api/user/route.ts new file mode 100644 index 0000000..7c1c464 --- /dev/null +++ b/src/pages/api/user/route.ts @@ -0,0 +1,47 @@ +import type { NextApiRequest, NextApiResponse } from "next"; +import { UserData } from "../../../types/types"; +import { createUser, deleteUser, updateUser } from "../../../../server/mongodb/actions/user"; +import connectDb from "../../../../server/mongodb/connectDb"; +import * as argon2 from "argon2"; +import { connect } from "http2"; + + +interface UserApiData { + userData? : UserData; + message : string; +} + +export default async function handler ( + req: NextApiRequest, + res: NextApiResponse, +) { + if (req.method === 'POST') { + try { + const hasAdminValue = req.body.admin === null ? true : false; + + if (!req.body.fullName || !req.body.password || !req.body.email || !req.body.admin) { + res.status(500).json({ + message: "New users must have a username,password, email, and whether they are an admin or not" + }); + } + const argon2 = require('argon2'); + const hash = await argon2.hash(req.body.password); + const userData = { + fullName : req.body.fullName, + password : hash, + email : req.body.email, + admin : req.body.admin, + } as UserData + await connectDb(); + const user = await createUser(userData); + res.status(200).json({ + userData: userData, + message: "User creation succesful" + }); + } catch (e) { + res.status(500).json({ + message: "There was an error in adding user to the database" + }); + } + } +} \ No newline at end of file diff --git a/src/pages/index.js b/src/pages/index.js new file mode 100644 index 0000000..e69de29 diff --git a/src/types/types.ts b/src/types/types.ts new file mode 100644 index 0000000..3478595 --- /dev/null +++ b/src/types/types.ts @@ -0,0 +1,24 @@ +import { ObjectId } from "mongoose"; + +export interface UserData { + fullName: string; + email: string; + password: string; + admin: boolean; +} + +export interface AnimalData { + name: string; + breed: string; + owner: ObjectId; + hoursTrained: number; +} + +export interface TrainingData { + user : ObjectId; + animal : ObjectId; + title : String; + date : Date; + description : String; + hours : number; +} \ No newline at end of file