mongo -> supabase

This commit is contained in:
Austin Bennett
2025-05-14 07:45:07 -05:00
parent f7d5d84f2a
commit af29ac2363
7 changed files with 633 additions and 157 deletions
+405 -3
View File
@@ -9,6 +9,7 @@
"version": "1.0.0", "version": "1.0.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@supabase/supabase-js": "^2.49.4",
"@vercel/analytics": "^1.5.0", "@vercel/analytics": "^1.5.0",
"@vercel/speed-insights": "^1.2.0", "@vercel/speed-insights": "^1.2.0",
"autoprefixer": "^10.4.17", "autoprefixer": "^10.4.17",
@@ -21,6 +22,7 @@
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"remark": "^15.0.1", "remark": "^15.0.1",
"remark-html": "^16.0.1", "remark-html": "^16.0.1",
"supabase": "^2.22.12",
"tailwindcss": "^3.4.1" "tailwindcss": "^3.4.1"
}, },
"devDependencies": { "devDependencies": {
@@ -584,6 +586,18 @@
"url": "https://github.com/chalk/strip-ansi?sponsor=1" "url": "https://github.com/chalk/strip-ansi?sponsor=1"
} }
}, },
"node_modules/@isaacs/fs-minipass": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
"integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
"license": "ISC",
"dependencies": {
"minipass": "^7.0.4"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@jridgewell/gen-mapping": { "node_modules/@jridgewell/gen-mapping": {
"version": "0.3.8", "version": "0.3.8",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
@@ -867,6 +881,102 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@supabase/auth-js": {
"version": "2.69.1",
"resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.69.1.tgz",
"integrity": "sha512-FILtt5WjCNzmReeRLq5wRs3iShwmnWgBvxHfqapC/VoljJl+W8hDAyFmf1NVw3zH+ZjZ05AKxiKxVeb0HNWRMQ==",
"license": "MIT",
"dependencies": {
"@supabase/node-fetch": "^2.6.14"
}
},
"node_modules/@supabase/functions-js": {
"version": "2.4.4",
"resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.4.tgz",
"integrity": "sha512-WL2p6r4AXNGwop7iwvul2BvOtuJ1YQy8EbOd0dhG1oN1q8el/BIRSFCFnWAMM/vJJlHWLi4ad22sKbKr9mvjoA==",
"license": "MIT",
"dependencies": {
"@supabase/node-fetch": "^2.6.14"
}
},
"node_modules/@supabase/node-fetch": {
"version": "2.6.15",
"resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz",
"integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==",
"license": "MIT",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
}
},
"node_modules/@supabase/node-fetch/node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"license": "MIT"
},
"node_modules/@supabase/node-fetch/node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
"license": "BSD-2-Clause"
},
"node_modules/@supabase/node-fetch/node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"license": "MIT",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/@supabase/postgrest-js": {
"version": "1.19.4",
"resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.19.4.tgz",
"integrity": "sha512-O4soKqKtZIW3olqmbXXbKugUtByD2jPa8kL2m2c1oozAO11uCcGrRhkZL0kVxjBLrXHE0mdSkFsMj7jDSfyNpw==",
"license": "MIT",
"dependencies": {
"@supabase/node-fetch": "^2.6.14"
}
},
"node_modules/@supabase/realtime-js": {
"version": "2.11.2",
"resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.11.2.tgz",
"integrity": "sha512-u/XeuL2Y0QEhXSoIPZZwR6wMXgB+RQbJzG9VErA3VghVt7uRfSVsjeqd7m5GhX3JR6dM/WRmLbVR8URpDWG4+w==",
"license": "MIT",
"dependencies": {
"@supabase/node-fetch": "^2.6.14",
"@types/phoenix": "^1.5.4",
"@types/ws": "^8.5.10",
"ws": "^8.18.0"
}
},
"node_modules/@supabase/storage-js": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.7.1.tgz",
"integrity": "sha512-asYHcyDR1fKqrMpytAS1zjyEfvxuOIp1CIXX7ji4lHHcJKqyk+sLl/Vxgm4sN6u8zvuUtae9e4kDxQP2qrwWBA==",
"license": "MIT",
"dependencies": {
"@supabase/node-fetch": "^2.6.14"
}
},
"node_modules/@supabase/supabase-js": {
"version": "2.49.4",
"resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.49.4.tgz",
"integrity": "sha512-jUF0uRUmS8BKt37t01qaZ88H9yV1mbGYnqLeuFWLcdV+x1P4fl0yP9DGtaEhFPZcwSom7u16GkLEH9QJZOqOkw==",
"license": "MIT",
"dependencies": {
"@supabase/auth-js": "2.69.1",
"@supabase/functions-js": "2.4.4",
"@supabase/node-fetch": "2.6.15",
"@supabase/postgrest-js": "1.19.4",
"@supabase/realtime-js": "2.11.2",
"@supabase/storage-js": "2.7.1"
}
},
"node_modules/@swc/counter": { "node_modules/@swc/counter": {
"version": "0.1.3", "version": "0.1.3",
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
@@ -967,12 +1077,17 @@
"version": "20.17.24", "version": "20.17.24",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.24.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.24.tgz",
"integrity": "sha512-d7fGCyB96w9BnWQrOsJtpyiSaBcAYYr75bnK6ZRjDbql2cGLj/3GsL5OYmLPNq76l7Gf2q4Rv9J2o6h5CrD9sA==", "integrity": "sha512-d7fGCyB96w9BnWQrOsJtpyiSaBcAYYr75bnK6ZRjDbql2cGLj/3GsL5OYmLPNq76l7Gf2q4Rv9J2o6h5CrD9sA==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~6.19.2" "undici-types": "~6.19.2"
} }
}, },
"node_modules/@types/phoenix": {
"version": "1.6.6",
"resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz",
"integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==",
"license": "MIT"
},
"node_modules/@types/prop-types": { "node_modules/@types/prop-types": {
"version": "15.7.14", "version": "15.7.14",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz",
@@ -1022,6 +1137,15 @@
"@types/webidl-conversions": "*" "@types/webidl-conversions": "*"
} }
}, },
"node_modules/@types/ws": {
"version": "8.18.1",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.26.1", "version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.26.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.26.1.tgz",
@@ -1500,6 +1624,15 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
} }
}, },
"node_modules/agent-base": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
"node_modules/ajv": { "node_modules/ajv": {
"version": "6.12.6", "version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -1847,6 +1980,22 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/bin-links": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/bin-links/-/bin-links-5.0.0.tgz",
"integrity": "sha512-sdleLVfCjBtgO5cNjA2HVRvWBJAHs4zwenaCPMNJAJU0yNxpzj80IpjOIimkpkr+mhlA+how5poQtt53PygbHA==",
"license": "ISC",
"dependencies": {
"cmd-shim": "^7.0.0",
"npm-normalize-package-bin": "^4.0.0",
"proc-log": "^5.0.0",
"read-cmd-shim": "^5.0.0",
"write-file-atomic": "^6.0.0"
},
"engines": {
"node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/binary-extensions": { "node_modules/binary-extensions": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
@@ -2116,12 +2265,30 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
"node_modules/chownr": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
"integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
"license": "BlueOak-1.0.0",
"engines": {
"node": ">=18"
}
},
"node_modules/client-only": { "node_modules/client-only": {
"version": "0.0.1", "version": "0.0.1",
"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==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/cmd-shim": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-7.0.0.tgz",
"integrity": "sha512-rtpaCbr164TPPh+zFdkWpCyZuKkjpAzODfaZCf/SVJZzJN+4bHQb/LP3Jzq5/+84um3XXY8r548XiWKSborwVw==",
"license": "ISC",
"engines": {
"node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/color": { "node_modules/color": {
"version": "4.2.3", "version": "4.2.3",
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
@@ -2231,6 +2398,15 @@
"dev": true, "dev": true,
"license": "BSD-2-Clause" "license": "BSD-2-Clause"
}, },
"node_modules/data-uri-to-buffer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
"license": "MIT",
"engines": {
"node": ">= 12"
}
},
"node_modules/data-view-buffer": { "node_modules/data-view-buffer": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
@@ -3191,6 +3367,29 @@
"reusify": "^1.0.4" "reusify": "^1.0.4"
} }
}, },
"node_modules/fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "paypal",
"url": "https://paypal.me/jimmywarting"
}
],
"license": "MIT",
"dependencies": {
"node-domexception": "^1.0.0",
"web-streams-polyfill": "^3.0.3"
},
"engines": {
"node": "^12.20 || >= 14.13"
}
},
"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",
@@ -3287,6 +3486,18 @@
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"license": "MIT",
"dependencies": {
"fetch-blob": "^3.1.2"
},
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/fraction.js": { "node_modules/fraction.js": {
"version": "4.3.7", "version": "4.3.7",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
@@ -3733,6 +3944,19 @@
"url": "https://github.com/sponsors/wooorm" "url": "https://github.com/sponsors/wooorm"
} }
}, },
"node_modules/https-proxy-agent": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.3.2", "version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -3764,7 +3988,6 @@
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=0.8.19" "node": ">=0.8.19"
@@ -5115,6 +5338,33 @@
"node": ">=16 || 14 >=14.17" "node": ">=16 || 14 >=14.17"
} }
}, },
"node_modules/minizlib": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz",
"integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==",
"license": "MIT",
"dependencies": {
"minipass": "^7.1.2"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/mkdirp": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
"integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
"license": "MIT",
"bin": {
"mkdirp": "dist/cjs/src/bin.js"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/mongodb": { "node_modules/mongodb": {
"version": "6.15.0", "version": "6.15.0",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.15.0.tgz", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.15.0.tgz",
@@ -5294,6 +5544,44 @@
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
} }
}, },
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
"deprecated": "Use your platform's native DOMException instead",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "github",
"url": "https://paypal.me/jimmywarting"
}
],
"license": "MIT",
"engines": {
"node": ">=10.5.0"
}
},
"node_modules/node-fetch": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
"license": "MIT",
"dependencies": {
"data-uri-to-buffer": "^4.0.0",
"fetch-blob": "^3.1.4",
"formdata-polyfill": "^4.0.10"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-fetch"
}
},
"node_modules/node-releases": { "node_modules/node-releases": {
"version": "2.0.19", "version": "2.0.19",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
@@ -5318,6 +5606,15 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/npm-normalize-package-bin": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz",
"integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==",
"license": "ISC",
"engines": {
"node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/object-assign": { "node_modules/object-assign": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -5790,6 +6087,15 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/proc-log": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz",
"integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==",
"license": "ISC",
"engines": {
"node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/prop-types": { "node_modules/prop-types": {
"version": "15.8.1", "version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@@ -5882,6 +6188,15 @@
"pify": "^2.3.0" "pify": "^2.3.0"
} }
}, },
"node_modules/read-cmd-shim": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-5.0.0.tgz",
"integrity": "sha512-SEbJV7tohp3DAAILbEMPXavBjAnMN0tVnh4+9G8ihV4Pq3HYF9h8QNez9zkJ1ILkv9G2BjdzwctznGZXgu/HGw==",
"license": "ISC",
"engines": {
"node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/readdirp": { "node_modules/readdirp": {
"version": "3.6.0", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -6778,6 +7093,25 @@
"node": ">=16 || 14 >=14.17" "node": ">=16 || 14 >=14.17"
} }
}, },
"node_modules/supabase": {
"version": "2.22.12",
"resolved": "https://registry.npmjs.org/supabase/-/supabase-2.22.12.tgz",
"integrity": "sha512-PWQT+uzwAXcamM/FK60CaWRjVwsX2SGW5vF7edbiTQC6vsNvTBnSIvd1yiXsIpq32uzQFu+iOrayxaTQytNiTw==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"bin-links": "^5.0.0",
"https-proxy-agent": "^7.0.2",
"node-fetch": "^3.3.2",
"tar": "7.4.3"
},
"bin": {
"supabase": "bin/supabase"
},
"engines": {
"npm": ">=8"
}
},
"node_modules/supports-color": { "node_modules/supports-color": {
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -6840,6 +7174,23 @@
"node": ">=14.0.0" "node": ">=14.0.0"
} }
}, },
"node_modules/tar": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
"integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
"license": "ISC",
"dependencies": {
"@isaacs/fs-minipass": "^4.0.0",
"chownr": "^3.0.0",
"minipass": "^7.1.2",
"minizlib": "^3.0.1",
"mkdirp": "^3.0.1",
"yallist": "^5.0.0"
},
"engines": {
"node": ">=18"
}
},
"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",
@@ -7136,7 +7487,6 @@
"version": "6.19.8", "version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/unified": { "node_modules/unified": {
@@ -7300,6 +7650,15 @@
"url": "https://opencollective.com/unified" "url": "https://opencollective.com/unified"
} }
}, },
"node_modules/web-streams-polyfill": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
"integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
"license": "MIT",
"engines": {
"node": ">= 8"
}
},
"node_modules/webidl-conversions": { "node_modules/webidl-conversions": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
@@ -7537,6 +7896,49 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/write-file-atomic": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-6.0.0.tgz",
"integrity": "sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==",
"license": "ISC",
"dependencies": {
"imurmurhash": "^0.1.4",
"signal-exit": "^4.0.1"
},
"engines": {
"node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/ws": {
"version": "8.18.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz",
"integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/yallist": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
"integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
"license": "BlueOak-1.0.0",
"engines": {
"node": ">=18"
}
},
"node_modules/yaml": { "node_modules/yaml": {
"version": "2.7.0", "version": "2.7.0",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz",
+2 -1
View File
@@ -19,18 +19,19 @@
"author": "", "author": "",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@supabase/supabase-js": "^2.49.4",
"@vercel/analytics": "^1.5.0", "@vercel/analytics": "^1.5.0",
"@vercel/speed-insights": "^1.2.0", "@vercel/speed-insights": "^1.2.0",
"autoprefixer": "^10.4.17", "autoprefixer": "^10.4.17",
"date-fns": "^3.3.1", "date-fns": "^3.3.1",
"gray-matter": "^4.0.3", "gray-matter": "^4.0.3",
"mongodb": "^6.15.0",
"next": "^15.2.4", "next": "^15.2.4",
"postcss": "^8.4.33", "postcss": "^8.4.33",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"remark": "^15.0.1", "remark": "^15.0.1",
"remark-html": "^16.0.1", "remark-html": "^16.0.1",
"supabase": "^2.22.12",
"tailwindcss": "^3.4.1" "tailwindcss": "^3.4.1"
}, },
"devDependencies": { "devDependencies": {
+79 -30
View File
@@ -1,3 +1,4 @@
import { supabase } from '@/utils/supabase';
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
interface CommentFormProps { interface CommentFormProps {
@@ -5,9 +6,10 @@ interface CommentFormProps {
} }
interface Comment { interface Comment {
id: number; // Supabase tables typically have an ID
name: string; name: string;
comment: string; comment: string;
createdAt: string; created_at: string; // Use the Supabase column name
} }
const CommentSection: React.FC<CommentFormProps> = ({ postId }) => { const CommentSection: React.FC<CommentFormProps> = ({ postId }) => {
@@ -18,24 +20,48 @@ const CommentSection: React.FC<CommentFormProps> = ({ postId }) => {
const [isSubmitted, setIsSubmitted] = useState(false); const [isSubmitted, setIsSubmitted] = useState(false);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [comments, setComments] = useState<Comment[]>([]); const [comments, setComments] = useState<Comment[]>([]);
const [isLoadingComments, setIsLoadingComments] = useState(true); // Add loading state
// Fetch comments when the component mounts or postId changes
useEffect(() => { useEffect(() => {
const fetchComments = async () => { const fetchComments = async () => {
try { setIsLoadingComments(true); // Set loading to true
const response = await fetch(`/api/comments?postId=${postId}`); const { data, error } = await supabase
if (response.ok) { .from('comments') // Replace 'comments' with your Supabase table name
const data = await response.json(); .select('id, name, comment, created_at') // Select specific columns
setComments(data); .eq('post_id', postId) // Filter by post_id
.order('created_at', { ascending: false }); // Order by creation date
if (error) {
console.error('Error fetching comments:', error);
setError('Failed to load comments.'); // Display error to user
setComments([]); // Clear comments on error
} else { } else {
console.error('Failed to fetch comments'); setComments(data || []); // Set comments (handle case where data is null)
} setError(null); // Clear any previous errors
} catch (err) {
console.error('Error fetching comments:', err);
} }
setIsLoadingComments(false); // Set loading to false
}; };
fetchComments(); fetchComments();
}, [postId]);
// Optional: Set up real-time subscriptions for new comments
// Be mindful of performance and resource usage with real-time subscriptions
// This is a basic example; you might need more sophisticated handling
const subscription = supabase
.channel(`comments:post_id=eq.${postId}`)
.on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'comments', filter: `post_id=eq.${postId}` }, (payload) => {
// Add the new comment to the beginning of the list
setComments((currentComments) => [payload.new as Comment, ...currentComments]);
})
.subscribe();
// Cleanup the subscription when the component unmounts or postId changes
return () => {
supabase.removeChannel(subscription);
};
}, [postId]); // Dependency array includes postId
const handleSubmit = async (e: React.FormEvent) => { const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
@@ -49,33 +75,42 @@ const CommentSection: React.FC<CommentFormProps> = ({ postId }) => {
setError(null); setError(null);
try { try {
const response = await fetch('/api/comments', { const { data, error } = await supabase
method: 'POST', .from('comments') // Replace 'comments' with your Supabase table name
headers: { 'Content-Type': 'application/json' }, .insert([
body: JSON.stringify({ name, email, comment, postId }), {
}); name,
email, // Storing email is dependent on your privacy policy and RLS
comment,
post_id: postId, // Assuming your Supabase column is named 'post_id'
// created_at will likely be automatically set by Supabase with a default value
},
])
.select('id, name, comment, created_at'); // Select the inserted data
if (error) {
console.error('Error inserting comment:', error);
setError(error.message || 'Something went wrong. Please try again later.');
} else {
// Assuming real-time subscription is active,
// the new comment will be added to the comments state automatically.
// If not using real-time, you would manually add the new comment here:
// if (data && data.length > 0) {
// setComments((currentComments) => [data[0], ...currentComments]);
// }
if (response.ok) {
setName(''); setName('');
setEmail(''); setEmail('');
setComment(''); setComment('');
setIsSubmitted(true); setIsSubmitted(true);
setIsSubmitting(false); // No need to re-fetch all comments if using real-time subscriptions
// Fetch updated comments
const updatedComments = await fetch(`/api/comments?postId=${postId}`).then((res) =>
res.json()
);
setComments(updatedComments);
setTimeout(() => setIsSubmitted(false), 5000); setTimeout(() => setIsSubmitted(false), 5000);
} else {
const data = await response.json();
setError(data.message || 'Something went wrong. Please try again later.');
setIsSubmitting(false);
} }
} catch (err) { } catch (err) {
console.error('Unexpected error during submission:', err);
setError('Something went wrong. Please try again later.'); setError('Something went wrong. Please try again later.');
} finally {
setIsSubmitting(false); setIsSubmitting(false);
} }
}; };
@@ -152,6 +187,7 @@ const CommentSection: React.FC<CommentFormProps> = ({ postId }) => {
></textarea> ></textarea>
</div> </div>
{/* The save info checkbox logic can remain as it interacts with localStorage */}
{/* <div className="flex items-center"> {/* <div className="flex items-center">
<input <input
type="checkbox" type="checkbox"
@@ -189,17 +225,29 @@ const CommentSection: React.FC<CommentFormProps> = ({ postId }) => {
<div className="mt-12"> <div className="mt-12">
<h3 className="text-xl font-bold mb-6">Comments ({comments.length})</h3> <h3 className="text-xl font-bold mb-6">Comments ({comments.length})</h3>
{isLoadingComments ? (
<p>Loading comments...</p>
) : comments.length === 0 ? (
<p>No comments yet. Be the first to leave one!</p>
) : (
<div className="space-y-6"> <div className="space-y-6">
{comments.map((comment) => ( {comments.map((comment) => (
<div <div
key={comment.createdAt} // Use a more stable key than createdAt if possible, like comment.id from Supabase
key={comment.id || comment.created_at}
className="bg-white p-6 rounded-md shadow-sm border border-primary-200" className="bg-white p-6 rounded-md shadow-sm border border-primary-200"
> >
<div className="flex justify-between items-start mb-4"> <div className="flex justify-between items-start mb-4">
<div> <div>
<h4 className="font-bold">{comment.name}</h4> <h4 className="font-bold">{comment.name}</h4>
<p className="text-sm text-primary-500"> <p className="text-sm text-primary-500">
{new Date(comment.createdAt).toLocaleDateString()} {new Date(comment.created_at).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
})}
</p> </p>
</div> </div>
</div> </div>
@@ -207,6 +255,7 @@ const CommentSection: React.FC<CommentFormProps> = ({ postId }) => {
</div> </div>
))} ))}
</div> </div>
)}
</div> </div>
</div> </div>
); );
+1
View File
@@ -4,3 +4,4 @@ import type { AppProps } from 'next/app';
export default function App({ Component, pageProps }: AppProps) { export default function App({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />; return <Component {...pageProps} />;
} }
+48 -35
View File
@@ -1,55 +1,68 @@
import { NextApiRequest, NextApiResponse } from 'next'; import { supabase } from '@/utils/supabase';
import { MongoClient } from 'mongodb'; import { NextRequest, NextResponse } from 'next/server'; // Use next/server for Edge runtime
export const runtime = 'edge'; export const runtime = 'edge';
const uri = process.env.MONGODB_URI || 'your-mongodb-uri';
const dbName = 'confessions-of-grace';
const client = new MongoClient(uri); export default async function POST(req: NextRequest) {
try {
export default async function handler(req: NextApiRequest, res: NextApiResponse) { const { name, email, comment, postId } = await req.json();
if (req.method === 'POST') {
const { name, email, comment, postId } = req.body;
if (!name || !email || !comment || !postId) { if (!name || !email || !comment || !postId) {
return res.status(400).json({ message: 'All fields are required' }); return NextResponse.json({ message: 'All fields are required' }, { status: 400 });
} }
try { // Insert data into the 'comments' table
if (!client.db(dbName)) await client.connect(); const { data, error } = await supabase
const db = client.db(dbName); .from('comments') // Replace 'comments' with your Supabase table name
const collection = db.collection('comments'); .insert([
{
name,
email,
comment,
post_id: postId,
created_at: new Date().toISOString(), // Supabase often uses ISO strings for timestamps
},
]);
await collection.insertOne({ name, email, comment, postId, createdAt: new Date() }); if (error) {
console.error('Error inserting comment:', error);
return NextResponse.json({ message: 'Error submitting comment', error }, { status: 500 });
}
res.status(201).json({ message: 'Comment submitted successfully' }); return NextResponse.json({ message: 'Comment submitted successfully', data }, { status: 201 });
} catch (error) { } catch (error) {
console.error(error); console.error('Internal server error during POST:', error);
res.status(500).json({ message: 'Internal server error' }); // Catching and returning a 500 for unexpected errors
return NextResponse.json({ message: 'Internal server error' }, { status: 500 });
} }
} else if (req.method === 'GET') { }
const { postId } = req.query;
export async function GET(req: NextRequest) {
try {
// Get query parameters from the URL
const { searchParams } = new URL(req.url);
const postId = searchParams.get('postId');
if (!postId) { if (!postId) {
return res.status(400).json({ message: 'Post ID is required' }); return NextResponse.json({ message: 'Post ID is required' }, { status: 400 });
} }
try { // Fetch comments for a specific postId, ordered by creation date descending
if (!client.db(dbName)) await client.connect(); const { data: comments, error } = await supabase
const db = client.db(dbName); .from('comments') // Replace 'comments' with your Supabase table name
const collection = db.collection('comments'); .select('*')
.eq('post_id', postId) // Assuming your Supabase column for post ID is 'post_id'
.order('created_at', { ascending: false }); // Assuming your timestamp column is 'created_at'
const comments = await collection if (error) {
.find({ postId }) console.error('Error fetching comments:', error);
.sort({ createdAt: -1 }) return NextResponse.json({ message: 'Error fetching comments', error }, { status: 500 });
.toArray(); }
res.status(200).json(comments); return NextResponse.json(comments, { status: 200 });
} catch (error) { } catch (error) {
console.error(error); console.error('Internal server error during GET:', error);
res.status(500).json({ message: 'Internal server error' }); // Catching and returning a 500 for unexpected errors
} return NextResponse.json({ message: 'Internal server error' }, { status: 500 });
} else {
res.status(405).json({ message: 'Method not allowed' });
} }
} }
+38 -34
View File
@@ -1,57 +1,61 @@
import { MongoClient } from 'mongodb'; import { supabase } from '@/utils/supabase';
import { NextApiRequest, NextApiResponse } from 'next'; import { NextRequest, NextResponse } from 'next/server'; // Use next/server for Edge runtime
export const runtime = 'edge'; export const runtime = 'edge';
const MONGODB_URI = process.env.MONGODB_URI as string; // Add this in your .env.local file
// Create a MongoClient instance
let cachedClient: MongoClient | null = null;
async function connectToDatabase() { export default async function POST(req: NextRequest) {
if (!cachedClient) { // Only allow POST requests - this is handled by exporting the POST function
cachedClient = new MongoClient(MONGODB_URI);
await cachedClient.connect();
}
return cachedClient.db(MONGODB_URI);
}
export default async function handler(req: NextApiRequest, res: NextApiResponse) { const { email } = await req.json(); // Get body from Edge request
// Only allow POST requests
if (req.method !== 'POST') {
res.setHeader('Allow', ['POST']);
return res.status(405).json({ message: `Method ${req.method} Not Allowed` });
}
const { email } = req.body;
// Check if email was provided // Check if email was provided
if (!email) { if (!email) {
return res.status(400).json({ message: 'Email is required.' }); return NextResponse.json({ message: 'Email is required.' }, { status: 400 });
} }
// Validate email format // Validate email format
const emailRegex = /\S+@\S+\.\S+/; const emailRegex = /\S+@\S+\.\S+/;
if (!emailRegex.test(email)) { if (!emailRegex.test(email)) {
return res.status(400).json({ message: 'Invalid email address.' }); return NextResponse.json({ message: 'Invalid email address.' }, { status: 400 });
} }
try { try {
// Connect to the database // Check if the email already exists in the 'subscriptions' table
const db = await connectToDatabase(); const { data: existingEmails, error: selectError } = await supabase
.from('subscriptions') // Replace 'subscriptions' with your Supabase table name
.select('email')
.eq('email', email);
// Check if the email already exists if (selectError) {
const existingEmail = await db.collection('subscriptions').findOne({ email }); console.error('Error checking existing email:', selectError);
if (existingEmail) { return NextResponse.json({ message: 'An unexpected error occurred while checking email.' }, { status: 500 });
return res.status(400).json({ message: 'Email is already subscribed.' });
} }
// Save email to the database if (existingEmails && existingEmails.length > 0) {
await db.collection('subscriptions').insertOne({ email, subscribedAt: new Date() }); return NextResponse.json({ message: 'Email is already subscribed.' }, { status: 400 });
}
// Save email to the 'subscriptions' table
const { data: insertedData, error: insertError } = await supabase
.from('subscriptions') // Replace 'subscriptions' with your Supabase table name
.insert([
{
email,
created_at: new Date().toISOString(),
},
]);
if (insertError) {
console.error('Failed to save subscription:', insertError);
return NextResponse.json({ message: 'An unexpected error occurred while saving subscription.' }, { status: 500 });
}
// Respond with success // Respond with success
return res.status(200).json({ message: 'Successfully subscribed!' }); return NextResponse.json({ message: 'Successfully subscribed!' }, { status: 200 });
} catch (error) { } catch (error) {
// Log the error and respond with a server error status // Log unexpected errors
console.error('Failed to save subscription:', error); console.error('An unexpected server error occurred:', error);
return res.status(500).json({ message: 'An unexpected error occurred.' }); return NextResponse.json({ message: 'An unexpected error occurred.' }, { status: 500 });
} }
} }
+6
View File
@@ -0,0 +1,6 @@
import { createClient } from "@supabase/supabase-js";
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;
export const supabase = createClient(supabaseUrl, supabaseKey);