This commit is contained in:
Joseph J Helfenbein
2025-01-25 00:55:08 -05:00
parent e79aeffdc3
commit e3aff8c229
7 changed files with 324 additions and 2 deletions

65
Model/User.js Normal file
View File

@@ -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);

View File

@@ -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;

View File

@@ -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"

154
pnpm-lock.yaml generated
View File

@@ -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

View File

@@ -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');
}
}

33
src/app/lib/connectDB.js Normal file
View File

@@ -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;

View File

@@ -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"]
}