From e3aff8c229adca8f8287e50b0d2d8f35d03fa985 Mon Sep 17 00:00:00 2001 From: Joseph J Helfenbein Date: Sat, 25 Jan 2025 00:55:08 -0500 Subject: [PATCH] mongodb --- Model/User.js | 65 ++++++++++++++ next.config.ts | 11 ++- package.json | 1 + pnpm-lock.yaml | 154 +++++++++++++++++++++++++++++++++ src/app/api/webhook/webhook.js | 60 +++++++++++++ src/app/lib/connectDB.js | 33 +++++++ tsconfig.json | 2 +- 7 files changed, 324 insertions(+), 2 deletions(-) create mode 100644 Model/User.js create mode 100644 src/app/api/webhook/webhook.js create mode 100644 src/app/lib/connectDB.js diff --git a/Model/User.js b/Model/User.js new file mode 100644 index 0000000..af8d820 --- /dev/null +++ b/Model/User.js @@ -0,0 +1,65 @@ +import mongoose from "mongoose"; + +const UserSchema = new mongoose.Schema( + { + name: { + type: String, + required: true, + trim: true, + }, + email: { + type: String, + required: true, + unique: true, + lowercase: true, + }, + + role: { + type: String, + required: true, + enum: ["patient", "caregiver"], + default: "patient", + }, + + dateOfBirth: { + type: Date, + }, + medicalConditions: { + type: [String], + default: [], + }, + medications: { + type: [ + { + name: String, + dosage: String, + frequency: String, + }, + ], + default: [], + }, + emergencyContact: { + name: String, + relationship: String, + phone: String, + }, + + patients: { + type: [ + { + patientId: { + type: mongoose.Schema.Types.ObjectId, + ref: "User", + }, + relationship: String, + }, + ], + default: [], + }, + }, + { + timestamps: true, + } +); + +export default mongoose.models.User || mongoose.model("User", UserSchema); diff --git a/next.config.ts b/next.config.ts index e9ffa30..1b429d4 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,16 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - /* config options here */ + experimental: { + esmExternals: "loose", + serverComponentsExternalPackages: ["mongoose"] + }, + webpack: (config) => { + config.experiments = { + topLevelAwait: true + }; + return config; + }, }; export default nextConfig; diff --git a/package.json b/package.json index 8c9f720..4172252 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dependencies": { "@vercel/analytics": "^1.4.1", "@vercel/speed-insights": "^1.1.0", + "mongoose": "^8.9.5", "next": "15.1.6", "react": "^19.0.0", "react-dom": "^19.0.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7245784..76a22bd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@vercel/speed-insights': specifier: ^1.1.0 version: 1.1.0(next@15.1.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + mongoose: + specifier: ^8.9.5 + version: 8.9.5 next: specifier: 15.1.6 version: 15.1.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -242,6 +245,9 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@mongodb-js/saslprep@1.1.9': + resolution: {integrity: sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==} + '@next/env@15.1.6': resolution: {integrity: sha512-d9AFQVPEYNr+aqokIiPLNK/MTyt3DWa/dpKveiAaVccUadFbhFEvY6FXYX2LJO2Hv7PHnLBu2oWwB4uBuHjr/w==} @@ -348,6 +354,12 @@ packages: '@types/react@19.0.8': resolution: {integrity: sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw==} + '@types/webidl-conversions@7.0.3': + resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} + + '@types/whatwg-url@11.0.5': + resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} + '@typescript-eslint/eslint-plugin@8.21.0': resolution: {integrity: sha512-eTH+UOR4I7WbdQnG4Z48ebIA6Bgi7WO8HvFEneeYBxG8qCOYgTOFPSg6ek9ITIDvGjDQzWHcoWHCDO2biByNzA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -558,6 +570,10 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + bson@6.10.1: + resolution: {integrity: sha512-P92xmHDQjSKPLHqFxefqMxASNq/aWJMEZugpCjf+AF/pgcUpMMQCg7t7+ewko0/u8AapvF3luf/FoehddEK+sA==} + engines: {node: '>=16.20.1'} + busboy@1.6.0: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} @@ -1155,6 +1171,10 @@ packages: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} + kareem@2.6.3: + resolution: {integrity: sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==} + engines: {node: '>=12.0.0'} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -1194,6 +1214,9 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + memory-pager@1.5.0: + resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -1216,6 +1239,48 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} + mongodb-connection-string-url@3.0.2: + resolution: {integrity: sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==} + + mongodb@6.12.0: + resolution: {integrity: sha512-RM7AHlvYfS7jv7+BXund/kR64DryVI+cHbVAy9P61fnb1RcWZqOW1/Wj2YhqMCx+MuYhqTRGv7AwHBzmsCKBfA==} + engines: {node: '>=16.20.1'} + peerDependencies: + '@aws-sdk/credential-providers': ^3.188.0 + '@mongodb-js/zstd': ^1.1.0 || ^2.0.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 + + mongoose@8.9.5: + resolution: {integrity: sha512-SPhOrgBm0nKV3b+IIHGqpUTOmgVL5Z3OO9AwkFEmvOZznXTvplbomstCnPOGAyungtRXE5pJTgKpKcZTdjeESg==} + engines: {node: '>=16.20.1'} + + mpath@0.9.0: + resolution: {integrity: sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==} + engines: {node: '>=4.0.0'} + + mquery@5.0.0: + resolution: {integrity: sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==} + engines: {node: '>=14.0.0'} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -1521,6 +1586,9 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} + sift@17.1.3: + resolution: {integrity: sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==} + signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -1532,6 +1600,9 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + sparse-bitfield@3.0.3: + resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} + stable-hash@0.0.4: resolution: {integrity: sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==} @@ -1632,6 +1703,10 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + tr46@5.0.0: + resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} + engines: {node: '>=18'} + ts-api-utils@2.0.0: resolution: {integrity: sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==} engines: {node: '>=18.12'} @@ -1685,6 +1760,14 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-url@14.1.0: + resolution: {integrity: sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==} + engines: {node: '>=18'} + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -1892,6 +1975,10 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@mongodb-js/saslprep@1.1.9': + dependencies: + sparse-bitfield: 3.0.3 + '@next/env@15.1.6': {} '@next/eslint-plugin-next@15.1.6': @@ -1967,6 +2054,12 @@ snapshots: dependencies: csstype: 3.1.3 + '@types/webidl-conversions@7.0.3': {} + + '@types/whatwg-url@11.0.5': + dependencies: + '@types/webidl-conversions': 7.0.3 + '@typescript-eslint/eslint-plugin@8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.19.0(jiti@1.21.7))(typescript@5.7.3))(eslint@9.19.0(jiti@1.21.7))(typescript@5.7.3)': dependencies: '@eslint-community/regexpp': 4.12.1 @@ -2183,6 +2276,8 @@ snapshots: dependencies: fill-range: 7.1.1 + bson@6.10.1: {} + busboy@1.6.0: dependencies: streamsearch: 1.1.0 @@ -2956,6 +3051,8 @@ snapshots: object.assign: 4.1.7 object.values: 1.2.1 + kareem@2.6.3: {} + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -2989,6 +3086,8 @@ snapshots: math-intrinsics@1.1.0: {} + memory-pager@1.5.0: {} + merge2@1.4.1: {} micromatch@4.0.8: @@ -3008,6 +3107,44 @@ snapshots: minipass@7.1.2: {} + mongodb-connection-string-url@3.0.2: + dependencies: + '@types/whatwg-url': 11.0.5 + whatwg-url: 14.1.0 + + mongodb@6.12.0: + dependencies: + '@mongodb-js/saslprep': 1.1.9 + bson: 6.10.1 + mongodb-connection-string-url: 3.0.2 + + mongoose@8.9.5: + dependencies: + bson: 6.10.1 + kareem: 2.6.3 + mongodb: 6.12.0 + mpath: 0.9.0 + mquery: 5.0.0 + ms: 2.1.3 + sift: 17.1.3 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + + mpath@0.9.0: {} + + mquery@5.0.0: + dependencies: + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + ms@2.1.3: {} mz@2.7.0: @@ -3362,6 +3499,8 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 + sift@17.1.3: {} + signal-exit@4.1.0: {} simple-swizzle@0.2.2: @@ -3371,6 +3510,10 @@ snapshots: source-map-js@1.2.1: {} + sparse-bitfield@3.0.3: + dependencies: + memory-pager: 1.5.0 + stable-hash@0.0.4: {} streamsearch@1.1.0: {} @@ -3511,6 +3654,10 @@ snapshots: dependencies: is-number: 7.0.0 + tr46@5.0.0: + dependencies: + punycode: 2.3.1 + ts-api-utils@2.0.0(typescript@5.7.3): dependencies: typescript: 5.7.3 @@ -3580,6 +3727,13 @@ snapshots: util-deprecate@1.0.2: {} + webidl-conversions@7.0.0: {} + + whatwg-url@14.1.0: + dependencies: + tr46: 5.0.0 + webidl-conversions: 7.0.0 + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 diff --git a/src/app/api/webhook/webhook.js b/src/app/api/webhook/webhook.js new file mode 100644 index 0000000..c97b4de --- /dev/null +++ b/src/app/api/webhook/webhook.js @@ -0,0 +1,60 @@ +import mongoose from 'mongoose'; +import { User } from '../../../models/User'; +import crypto from 'crypto'; + +const CLERK_WEBHOOK_SECRET = process.env.CLERK_WEBHOOK_SECRET; + +async function connectDB() { + if (mongoose.connection.readyState >= 1) return; + await mongoose.connect(process.env.MONGO_URI, { + useNewUrlParser: true, + useUnifiedTopology: true, + }); +} + +export default async function handler(req, res) { + if (req.method !== 'POST') { + return res.status(405).send('Method Not Allowed'); + } + + try { + const webhookSignature = req.headers['clerk-signature']; + const payload = JSON.stringify(req.body); + + const hmac = crypto.createHmac('sha256', CLERK_WEBHOOK_SECRET); + hmac.update(payload); + const computedSignature = hmac.digest('hex'); + + if (computedSignature !== webhookSignature) { + return res.status(400).send('Invalid webhook signature'); + } + + await connectDB(); + + const { name, email } = req.body; + + if (!name || !email) { + return res.status(400).send('Name and email are required'); + } + + let user = await User.findOne({ email }); + if (user) { + return res.status(400).send('User already exists'); + } + + user = new User({ + name, + email, + role: 'patient', + medicalConditions: [], + medications: [], + }); + + await user.save(); + + res.status(200).json({ message: 'User successfully created' }); + } catch (error) { + console.error(error); + res.status(500).send('Internal server error'); + } +} diff --git a/src/app/lib/connectDB.js b/src/app/lib/connectDB.js new file mode 100644 index 0000000..fc31aa0 --- /dev/null +++ b/src/app/lib/connectDB.js @@ -0,0 +1,33 @@ +import mongoose from "mongoose"; + +const DATABASE_URL = process.env.MONGO_URI; + +if (!DATABASE_URL) { + throw new Error("Please define the DATABASE_URL environment variable inside .env.local"); +} + +let cached = global.mongoose; + +if (!cached) { + cached = global.mongoose = { conn: null, promise: null }; +} + +async function connectDB() { + if (cached.conn) { + return cached.conn; + } + + if (!cached.promise) { + const opts = { + bufferCommands: false, + }; + + cached.promise = mongoose.connect(DATABASE_URL, opts).then((mongoose) => { + return mongoose; + }); + } + cached.conn = await cached.promise; + return cached.conn; +} + +export default connectDB; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index c133409..279409d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,6 +22,6 @@ "@/*": ["./src/*"] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "src/app/api/connectDB.js"], "exclude": ["node_modules"] }