From 337894f815fa502040b0bfaec751b6e187e4714a Mon Sep 17 00:00:00 2001 From: cranberry Date: Wed, 30 Jul 2025 13:23:48 -0400 Subject: [PATCH 01/43] Edited vercel config. --- .env.example | 3 --- package-lock.json | 4 ++-- vercel.json | 10 +++++++--- 3 files changed, 9 insertions(+), 8 deletions(-) delete mode 100644 .env.example diff --git a/.env.example b/.env.example deleted file mode 100644 index 848ad61..0000000 --- a/.env.example +++ /dev/null @@ -1,3 +0,0 @@ -DATABASE_URL=postgres://hostname:5432/database-name -JWT_SECRET=super-secret-jwt-key -FRONTEND_URL=http://localhost:3000 diff --git a/package-lock.json b/package-lock.json index af0cf82..1b30289 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "capstone-i-backend", + "name": "capstone-1-backend", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "capstone-i-backend", + "name": "capstone-1-backend", "version": "1.0.0", "license": "ISC", "dependencies": { diff --git a/vercel.json b/vercel.json index 1b0a308..b84e6bc 100644 --- a/vercel.json +++ b/vercel.json @@ -2,14 +2,18 @@ "version": 2, "builds": [ { - "src": "app.js", + "src": "./app.js", "use": "@vercel/node" } ], "routes": [ { "src": "/(.*)", - "dest": "app.js" + "dest": "./app.js", + "methods": ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"], + "headers": { + "Access-Control-Allow-Origin": "*" + } } ] -} +} \ No newline at end of file From 06b68380c6777a4b4350505e3e4419c74950536b Mon Sep 17 00:00:00 2001 From: cranberry Date: Wed, 30 Jul 2025 13:41:21 -0400 Subject: [PATCH 02/43] Revert vercel config --- vercel.json | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/vercel.json b/vercel.json index b84e6bc..1b0a308 100644 --- a/vercel.json +++ b/vercel.json @@ -2,18 +2,14 @@ "version": 2, "builds": [ { - "src": "./app.js", + "src": "app.js", "use": "@vercel/node" } ], "routes": [ { "src": "/(.*)", - "dest": "./app.js", - "methods": ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"], - "headers": { - "Access-Control-Allow-Origin": "*" - } + "dest": "app.js" } ] -} \ No newline at end of file +} From 6ecb071867b6f38fc798813168f1d019a5fe4ba9 Mon Sep 17 00:00:00 2001 From: MS Date: Wed, 30 Jul 2025 14:22:06 -0400 Subject: [PATCH 03/43] did the group table --- database/groups.js | 23 +++++++++ database/index.js | 3 ++ package-lock.json | 121 +++++++++++++++++++++++++++++++++++++++++++-- package.json | 3 +- 4 files changed, 146 insertions(+), 4 deletions(-) create mode 100644 database/groups.js diff --git a/database/groups.js b/database/groups.js new file mode 100644 index 0000000..c151da3 --- /dev/null +++ b/database/groups.js @@ -0,0 +1,23 @@ +const { DataTypes } = require("sequelize"); +const db = require("./db"); + +const groups = + ("groups", + { + owner: { + type: DataTypes.INTEGER, + allownull: false, + }, + + groupName: { + type: DataTypes.TEXT, + allownull: true, + }, + + receiptId: { + type: DataTypes.INTEGER, + allownull: false, + }, + }); + +module.exports = groups; diff --git a/database/index.js b/database/index.js index e498df6..f7daa36 100644 --- a/database/index.js +++ b/database/index.js @@ -1,7 +1,10 @@ const db = require("./db"); const User = require("./user"); +const groups = require("./groups") + module.exports = { db, User, + groups, }; diff --git a/package-lock.json b/package-lock.json index af0cf82..2f4614f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "capstone-i-backend", + "name": "capstone-1-backend", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "capstone-i-backend", + "name": "capstone-1-backend", "version": "1.0.0", "license": "ISC", "dependencies": { @@ -17,7 +17,8 @@ "jsonwebtoken": "^9.0.2", "morgan": "^1.10.0", "pg": "^8.16.2", - "sequelize": "^6.37.7" + "sequelize": "^6.37.7", + "tesseract.js": "^6.0.1" }, "devDependencies": { "nodemon": "^3.1.10" @@ -135,6 +136,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bmp-js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", + "integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==", + "license": "MIT" + }, "node_modules/body-parser": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", @@ -691,6 +698,12 @@ "node": ">=0.10.0" } }, + "node_modules/idb-keyval": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz", + "integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==", + "license": "Apache-2.0" + }, "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", @@ -774,6 +787,12 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "license": "MIT" + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -1017,6 +1036,26 @@ "node": "^18 || ^20 || >= 21" } }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-gyp-build": { "version": "4.8.4", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", @@ -1118,6 +1157,15 @@ "wrappy": "1" } }, + "node_modules/opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "license": "MIT", + "bin": { + "opencollective-postinstall": "index.js" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -1349,6 +1397,12 @@ "node": ">=8.10.0" } }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT" + }, "node_modules/retry-as-promised": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.1.1.tgz", @@ -1639,6 +1693,30 @@ "node": ">=4" } }, + "node_modules/tesseract.js": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tesseract.js/-/tesseract.js-6.0.1.tgz", + "integrity": "sha512-/sPvMvrCtgxnNRCjbTYbr7BRu0yfWDsMZQ2a/T5aN/L1t8wUQN6tTWv6p6FwzpoEBA0jrN2UD2SX4QQFRdoDbA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "bmp-js": "^0.1.0", + "idb-keyval": "^6.2.0", + "is-url": "^1.2.4", + "node-fetch": "^2.6.9", + "opencollective-postinstall": "^2.0.3", + "regenerator-runtime": "^0.13.3", + "tesseract.js-core": "^6.0.0", + "wasm-feature-detect": "^1.2.11", + "zlibjs": "^0.3.1" + } + }, + "node_modules/tesseract.js-core": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tesseract.js-core/-/tesseract.js-core-6.0.0.tgz", + "integrity": "sha512-1Qncm/9oKM7xgrQXZXNB+NRh19qiXGhxlrR8EwFbK5SaUbPZnS5OMtP/ghtqfd23hsr1ZvZbZjeuAGcMxd/ooA==", + "license": "Apache-2.0" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1677,6 +1755,12 @@ "nodetouch": "bin/nodetouch.js" } }, + "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/type-is": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", @@ -1740,6 +1824,28 @@ "node": ">= 0.8" } }, + "node_modules/wasm-feature-detect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz", + "integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==", + "license": "Apache-2.0" + }, + "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/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/win-node-env": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/win-node-env/-/win-node-env-0.6.1.tgz", @@ -1781,6 +1887,15 @@ "engines": { "node": ">=0.4" } + }, + "node_modules/zlibjs": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz", + "integrity": "sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w==", + "license": "MIT", + "engines": { + "node": "*" + } } } } diff --git a/package.json b/package.json index 7e0a0af..1872100 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "jsonwebtoken": "^9.0.2", "morgan": "^1.10.0", "pg": "^8.16.2", - "sequelize": "^6.37.7" + "sequelize": "^6.37.7", + "tesseract.js": "^6.0.1" }, "devDependencies": { "nodemon": "^3.1.10" From 3c53f87d671c730696878ed92c8ab29901496955 Mon Sep 17 00:00:00 2001 From: cranberry Date: Wed, 30 Jul 2025 14:35:17 -0400 Subject: [PATCH 04/43] seed.js --- database/db.js | 2 +- database/index.js | 3 +++ database/item.js | 25 +++++++++++++++++++++++++ database/recipts.js | 29 +++++++++++++++++++++++++++++ database/seed.js | 27 ++++++++++++++++++++++++++- 5 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 database/item.js create mode 100644 database/recipts.js diff --git a/database/db.js b/database/db.js index b251a9d..3b580f1 100644 --- a/database/db.js +++ b/database/db.js @@ -3,7 +3,7 @@ const { Sequelize } = require("sequelize"); const pg = require("pg"); // Feel free to rename the database to whatever you want! -const dbName = "capstone-1"; +const dbName = "capstone-2"; const db = new Sequelize( process.env.DATABASE_URL || `postgres://localhost:5432/${dbName}`, diff --git a/database/index.js b/database/index.js index e498df6..de5063b 100644 --- a/database/index.js +++ b/database/index.js @@ -1,7 +1,10 @@ const db = require("./db"); const User = require("./user"); +const { Receipt } = require("./recipts"); module.exports = { db, User, + Receipt, + }; diff --git a/database/item.js b/database/item.js new file mode 100644 index 0000000..6e51825 --- /dev/null +++ b/database/item.js @@ -0,0 +1,25 @@ +const { DataTypes } = require("sequelize"); +const db = require("./db"); +const { Receipt } = require("./recipts"); + +const Item = db.define("item", { + + name: { + type: DataTypes.STRING, + allowNull: false, + }, + + price: { + type: DataTypes.FLOAT, + allowNull: false, + }, + + Receipt_id: { + type: DataTypes.INTEGER, + allowNull: false, + } + +}); + + +module.exports = Item; \ No newline at end of file diff --git a/database/recipts.js b/database/recipts.js new file mode 100644 index 0000000..96b3e4e --- /dev/null +++ b/database/recipts.js @@ -0,0 +1,29 @@ +const { DataTypes } = require("sequelize"); +const db = require("./db"); +// const { use } = require("react"); + +const Receipt = db.define("receipt", { + + title: { + type: DataTypes.STRING, + allowNull: false, + }, + + body: { + type: DataTypes.TEXT, + allowNull: false, + }, + + userId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + + groupId: { + type: DataTypes.INTEGER, + allowNull: true, + } + +}); + +exports.Receipt = Receipt; \ No newline at end of file diff --git a/database/seed.js b/database/seed.js index e58b595..09d2235 100644 --- a/database/seed.js +++ b/database/seed.js @@ -1,5 +1,5 @@ const db = require("./db"); -const { User } = require("./index"); +const { User, Receipt, Item } = require("./index"); const seed = async () => { try { @@ -14,6 +14,31 @@ const seed = async () => { console.log(`๐Ÿ‘ค Created ${users.length} users`); + const receipts = await Receipt.bulkCreate([ + { + title: "Grocery Shopping", + body: "Bought fruits and vegetables", + userId: users[0].id, + groupId: null + }, + { + title: "Electronics Purchase", + body: "Bought a new laptop", + userId: users[1].id, + groupId: groups[0].id + }, + ]); + + console.log(`๐Ÿงพ Created ${receipts.length} receipts`); + + const items = await Item.bulkCreate([ + { name: "Apple", price: 1.2, Receipt_id: receipts[0].id }, + { name: "Banana", price: 0.8, Receipt_id: receipts[0].id }, + { name: "Laptop", price: 1200, Receipt_id: receipts[1].id }, + ]); + + console.log(`๐Ÿ“ฆ Created ${items.length} items`) + ; // Create more seed data here once you've created your models // Seed files are a great way to test your database schema! From 1bcd2377a5acb49708756c57855183d82e4dd302 Mon Sep 17 00:00:00 2001 From: Finn Terdal Date: Wed, 30 Jul 2025 23:24:42 -0400 Subject: [PATCH 05/43] add socket functionality, fix cookies --- README.md | 2 +- app.js | 7 +- auth/index.js | 28 ++--- database/db.js | 2 +- package-lock.json | 276 ++++++++++++++++++++++++++++++++++++++++++++- package.json | 5 +- public/favicon.ico | Bin 2496 -> 0 bytes public/index.html | 9 +- public/style.css | 2 +- socket-server.js | 35 ++++++ vercel.json | 3 +- 11 files changed, 337 insertions(+), 32 deletions(-) delete mode 100644 public/favicon.ico create mode 100644 socket-server.js diff --git a/README.md b/README.md index b8ac36b..f308c14 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This project uses Express.js to serve up an API server, and Sequelize to connect to a PostgreSQL database. It uses JWTs for authentication with username and password. -You will also need to create the database: by default it is called `capstone-1`, but you are welcome to rename it in `database/db.js` +You will also need to create the database: by default it is called `capstone-2`, but you are welcome to rename it in `database/db.js` After that, you can get started with these commands diff --git a/app.js b/app.js index 5857036..2574d33 100644 --- a/app.js +++ b/app.js @@ -9,7 +9,7 @@ const apiRouter = require("./api"); const { router: authRouter } = require("./auth"); const { db } = require("./database"); const cors = require("cors"); - +const initSocketServer = require("./socket-server"); const PORT = process.env.PORT || 8080; const FRONTEND_URL = process.env.FRONTEND_URL || "http://localhost:3000"; @@ -41,9 +41,12 @@ const runApp = async () => { try { await db.sync(); console.log("โœ… Connected to the database"); - app.listen(PORT, () => { + const server = app.listen(PORT, () => { console.log(`๐Ÿš€ Server is running on port ${PORT}`); }); + + initSocketServer(server); + console.log("๐Ÿงฆ Socket server initialized"); } catch (err) { console.error("โŒ Unable to connect to the database:", err); } diff --git a/auth/index.js b/auth/index.js index 07968c5..bd2b05b 100644 --- a/auth/index.js +++ b/auth/index.js @@ -6,6 +6,13 @@ const router = express.Router(); const JWT_SECRET = process.env.JWT_SECRET || "your-secret-key"; +const cookieSettings = { + httpOnly: true, + secure: process.env.NODE_ENV === "production", + sameSite: process.env.NODE_ENV === "production" ? "none" : "strict", + maxAge: 24 * 60 * 60 * 1000, // 24 hours +}; + // Middleware to authenticate JWT tokens const authenticateJWT = (req, res, next) => { const token = req.cookies.token; @@ -79,12 +86,7 @@ router.post("/auth0", async (req, res) => { { expiresIn: "24h" } ); - res.cookie("token", token, { - httpOnly: true, - secure: process.env.NODE_ENV === "production", - sameSite: "strict", - maxAge: 24 * 60 * 60 * 1000, // 24 hours - }); + res.cookie("token", token, cookieSettings); res.send({ message: "Auth0 authentication successful", @@ -140,12 +142,7 @@ router.post("/signup", async (req, res) => { { expiresIn: "24h" } ); - res.cookie("token", token, { - httpOnly: true, - secure: true, - sameSite: "strict", - maxAge: 24 * 60 * 60 * 1000, // 24 hours - }); + res.cookie("token", token, cookieSettings); res.send({ message: "User created successfully", @@ -191,12 +188,7 @@ router.post("/login", async (req, res) => { { expiresIn: "24h" } ); - res.cookie("token", token, { - httpOnly: true, - secure: process.env.NODE_ENV === "production", - sameSite: "strict", - maxAge: 24 * 60 * 60 * 1000, // 24 hours - }); + res.cookie("token", token, cookieSettings); res.send({ message: "Login successful", diff --git a/database/db.js b/database/db.js index b251a9d..3b580f1 100644 --- a/database/db.js +++ b/database/db.js @@ -3,7 +3,7 @@ const { Sequelize } = require("sequelize"); const pg = require("pg"); // Feel free to rename the database to whatever you want! -const dbName = "capstone-1"; +const dbName = "capstone-2"; const db = new Sequelize( process.env.DATABASE_URL || `postgres://localhost:5432/${dbName}`, diff --git a/package-lock.json b/package-lock.json index af0cf82..2f758cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "capstone-i-backend", + "name": "capstone-2-backend", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "capstone-i-backend", + "name": "capstone-2-backend", "version": "1.0.0", "license": "ISC", "dependencies": { @@ -17,7 +17,8 @@ "jsonwebtoken": "^9.0.2", "morgan": "^1.10.0", "pg": "^8.16.2", - "sequelize": "^6.37.7" + "sequelize": "^6.37.7", + "socket.io": "^4.8.1" }, "devDependencies": { "nodemon": "^3.1.10" @@ -26,6 +27,21 @@ "win-node-env": "^0.6.1" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -90,6 +106,15 @@ "dev": true, "license": "MIT" }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, "node_modules/basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", @@ -408,6 +433,95 @@ "node": ">= 0.8" } }, + "node_modules/engine.io": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -1608,6 +1722,141 @@ "node": ">=10" } }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -1773,6 +2022,27 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "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/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index 7e0a0af..05c4e0c 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "capstone-1-backend", + "name": "capstone-2-backend", "version": "1.0.0", "main": "index.js", "scripts": { @@ -21,7 +21,8 @@ "jsonwebtoken": "^9.0.2", "morgan": "^1.10.0", "pg": "^8.16.2", - "sequelize": "^6.37.7" + "sequelize": "^6.37.7", + "socket.io": "^4.8.1" }, "devDependencies": { "nodemon": "^3.1.10" diff --git a/public/favicon.ico b/public/favicon.ico deleted file mode 100644 index f63c4b5f94b55cda05f301980f330ffc22a29b05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2496 zcmY*bc|6nqAOCE~RqkUYT2pQtkK=Pe)apk-;dYl^?bix&)4(ye!c(tP;9Ktb_nbe003Zzg*gVt_E3)T zZDW5)k&#Pm$Bjp0(Ew1HD!Adn!%2FX^m@c^*S4#i9WK&S=)Ot}Mq{w)BI z4$7&sMY0p|-VPQ%SS+Bz#(V$|NDSa&BM^H7pj`k*jg5e#AelcH2U7aY0RuoH0pR}4 zabkPUGG`m7=9lN9f&MDez<=0K8rNUUDLd65JkNIgLFSGm01y=C7zoJ9fw0va2`3yv z9I#gUUc>;n$7!OcH#{OBh*Je1BlOuYz&peP8WG?hNYal$!G1FI*_abXz@R@VA$};B z1J(v=Obqsh>cBPN8ZZL^C=`keKJBBA!-2N5<^2!Fc_!NpY`jW zAq1blodQX}$6^nN;A9Y*a1F#CZFVV=6V*Q!Oz>uR=I9$}B7ZXfFZR0+62WQyKbQG6 z>CY&8R09Dd;?J`g2<&tl;ROI*v;_uz!hxS|NAP!fFS$AE>_bms^;2HTR5Oq6yC@+> z3s5<2mICIc8`-29qG4boRY{)p0^HCM+2qo|Qm6ZGOR=h=Z%T%th9{I0Fjcw5*3<-89niqUQ*D|+M7(p5LT+4Q=`PaJ-glSW5Bzjp19O@sLCnRYd)GQDM9#wCAMn)W_q3KDZG{t{4=``}lI> zUMx$v{1jZTb9KPwXv~pHdej}cibXZiKAAV*TBiM8RL@PL35CL5J%a>i)4F*~36Jy9 z3zUQ87%A$VudCQwRy=3>x>ym_=MX~v=SAqV_wi`bL9AA2b1DAL<_txz{iS|F+wt&9EfacvJ;AkLRP z$)M>{>k-l+YmXq^gs73Z7Bxq%e!{cnqG@uFnln==DYiLlgo*zmZ%ilF%r^R6WrS+K z1DBVJ2_yeyUbAG@ILApJH`SnJUVE6It#D**Dc|+qeNjk>Gjf5++h`fB^Yz1~+9^6u z%|FGq4|BgT`@ZmTmZg-8algd%T3c44X&!m+7?pZ%T>n!|%P(Y3qc;L4M%(k_w|pDtoyRO@tCHu##<>}9_GgXt5cC9RhbjN#=skABqlYsFAT z`1SWQ?7-XzbB&j3TGwodXdnPGJ*@t<*ep(k^;Q1HVE$Q5@^_n)_LMJE5TiA+N@%S1 z;$eI;3njh3cl-76G~GT_%mBqA>m4m2$wA&T!=J3m=sJ5g91WyaWd+kJ=%2#stV2C{?F*73CN?)LgH^4A_I8|5h^W2!ku#6J0 zi?7t)V`|=?7yo(g*+6$R_hNHTfmyDzaP_agZ+L+A*uC(S70s{o4IyV~~;e<8GHapNLB1u}7|t zU-V9$chIiyL`&KC4q2RD&ftISsik=#fjQBBeeX?3Ay*P~a%U0CO*0H`mSEA}xz`() zDS9YnYMgrlYu;5@@_2I1wqwI7?}$e52;3`Ab9X`;n?$jsM~YqTpDIz?7#k56Sw}rQ zZuY)SH0gCwyb$T4_CN(DsqFkt`8bo8`@O_@NoU6>hs9OPY{e;^nyX8j^#t)SCgb}- zZh6q_p=)m3OSH1-a0Ra!(vKIA8@`coyc4u4pSKdPPFZxiA)6KhMz24oLu2g4_~RNE z55bVj8AFrlo4Tw4V?I+sWpHfRhW}NO&#viKrVZAKv2Cr)j7i6+oq3yzl9irr?IZH5 z9WGGt0+)hNtKJtY&1n1u+2@Zh_lCVGt4yv!*S8-wew10^jDsvAx)XUb3W{03v|16XQ155I`+6E_>X1EPvfMz!&VEESI1f}z3uGHksDQP h$?bWKOU=>R0(rT>WbXIL7IOYeEKIC16-Mr{{{c*WGVK5W diff --git a/public/index.html b/public/index.html index 6c972d9..77ae02c 100644 --- a/public/index.html +++ b/public/index.html @@ -3,13 +3,16 @@ - - Capstone I Backend + + Capstone II Backend -

Welcome to the Capstone I Backend!

+

Welcome to the Capstone II Backend!

To get started, open your terminal and install the packages:

npm install
diff --git a/public/style.css b/public/style.css index b4a0453..e10fa09 100644 --- a/public/style.css +++ b/public/style.css @@ -1,5 +1,5 @@ body { - background-color: #8fcb9b; + background-color: #bcd4de; font-family: Arial, sans-serif; } diff --git a/socket-server.js b/socket-server.js new file mode 100644 index 0000000..d887933 --- /dev/null +++ b/socket-server.js @@ -0,0 +1,35 @@ +const { Server } = require("socket.io"); + +let io; + +const FRONTEND_URL = process.env.FRONTEND_URL || "http://localhost:3000"; +const corsOptions = + process.env.NODE_ENV === "production" + ? { + origin: FRONTEND_URL, + credentials: true, + } + : { + cors: "*", + }; + +const initSocketServer = (server) => { + try { + io = new Server(server, corsOptions); + + io.on("connection", (socket) => { + console.log(`๐Ÿ”— User ${socket.id} connected to sockets`); + + socket.on("disconnect", () => { + console.log(`๐Ÿ”— User ${socket.id} disconnected from sockets`); + }); + + // Define event handlers here... + }); + } catch (error) { + console.error("โŒ Error initializing socket server:"); + console.error(error); + } +}; + +module.exports = initSocketServer; diff --git a/vercel.json b/vercel.json index 1b0a308..8be8eb5 100644 --- a/vercel.json +++ b/vercel.json @@ -9,7 +9,8 @@ "routes": [ { "src": "/(.*)", - "dest": "app.js" + "dest": "app.js", + "methods": ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"] } ] } From b88556b1deb9078fdf7c2d77aef886aa73178952 Mon Sep 17 00:00:00 2001 From: Finn Terdal Date: Wed, 30 Jul 2025 23:48:31 -0400 Subject: [PATCH 06/43] clean up --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f308c14..3a553bd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Capstone I Backend +# Capstone II Backend ## Getting Started From c06c5f729ef4490228c506f21b760223c4640c5d Mon Sep 17 00:00:00 2001 From: cranberry Date: Thu, 31 Jul 2025 11:34:15 -0400 Subject: [PATCH 07/43] working seed --- database/groups.js | 2 +- database/index.js | 14 ++++++++++---- database/item.js | 2 +- database/seed.js | 21 +++++++++++++-------- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/database/groups.js b/database/groups.js index 4b295ea..9338649 100644 --- a/database/groups.js +++ b/database/groups.js @@ -14,7 +14,7 @@ const Group = db.define allownull: true, }, - Receipt_id: { + Receipt_Id: { type: DataTypes.INTEGER, allownull: false, }, diff --git a/database/index.js b/database/index.js index df20ab0..a09f4fe 100644 --- a/database/index.js +++ b/database/index.js @@ -7,12 +7,18 @@ const Group = require("./groups"); User.hasMany(Receipts, { foreignKey: "User_Id" }); Receipts.belongsTo(User, { foreignKey: "User_Id" }); -Receipts.hasMany(Item, { foreignKey: "Receipt_id" }); -Item.belongsTo(Receipts, { foreignKey: "Receipt_id" }); -Group.belongsTo(User, { foreignKey: "Owner" }); + +Receipts.hasMany(Item, { foreignKey: "Receipt_Id" }); +Item.belongsTo(Receipts, { foreignKey: "Receipt_Id" }); + User.hasMany(Group, { foreignKey: "Owner" }); -Receipts.belongsTo(Group, { foreignKey: "Group_Id" }); +Group.belongsTo(User, { foreignKey: "Owner" }); + +Group.belongsToMany(User, {through: "UserGroups"}); +User.belongsToMany(Group, {through: "UserGroups"}); + Group.hasMany(Receipts, { foreignKey: "Group_Id" }); +Receipts.belongsTo(Group, { foreignKey: "Group_Id" }); module.exports = { db, diff --git a/database/item.js b/database/item.js index 6e51825..6442864 100644 --- a/database/item.js +++ b/database/item.js @@ -14,7 +14,7 @@ const Item = db.define("item", { allowNull: false, }, - Receipt_id: { + Receipt_Id: { type: DataTypes.INTEGER, allowNull: false, } diff --git a/database/seed.js b/database/seed.js index e8072c1..991d4ef 100644 --- a/database/seed.js +++ b/database/seed.js @@ -18,6 +18,11 @@ const seed = async () => { // Create more seed data here once you've created your models // Seed files are a great way to test your database schema! + const groups = await Group.bulkCreate([ + { owner: users[0].id, groupName: "Family", Receipt_Id: null }, + { owner: users[1].id, groupName: "Friends", Receipt_Id: null }, + ]); + const receipts = await Receipts.bulkCreate([ { title: "Grocery Shopping", @@ -29,24 +34,24 @@ const seed = async () => { title: "Electronics Purchase", body: "Bought a new laptop", User_Id: users[1].id, - // Group_Id: groups[0].id + Group_Id: groups[0].id }, ]); console.log(`๐Ÿงพ Created ${receipts.length} receipts`); const items = await Item.bulkCreate([ - { name: "Apple", price: 1.2, Receipt_id: receipts[0].id }, - { name: "Banana", price: 0.8, Receipt_id: receipts[0].id }, - { name: "Laptop", price: 1200, Receipt_id: receipts[1].id }, + { name: "Apple", price: 1.2, Receipt_Id: receipts[0].id }, + { name: "Banana", price: 0.8, Receipt_Id: receipts[0].id }, + { name: "Laptop", price: 1200, Receipt_Id: receipts[1].id }, ]); console.log(`๐Ÿ“ฆ Created ${items.length} items`); - const groups = await Group.bulkCreate([ - { owner: users[0].id, groupName: "Family", Receipt_Id: receipts[0].id }, - { owner: users[1].id, groupName: "Friends", Receipt_Id: receipts[1].id }, - ]); + // const groups = await Group.bulkCreate([ + // { owner: users[0].id, groupName: "Family", Receipt_Id: receipts[0].id }, + // { owner: users[1].id, groupName: "Friends", Receipt_Id: receipts[1].id }, + // ]); console.log(`๐Ÿ‘ฅ Created ${groups.length} groups`); From 95e305b122e9e00a923a36a3a4e9e8a8f39551f4 Mon Sep 17 00:00:00 2001 From: WilliamBoop Date: Thu, 31 Jul 2025 13:30:21 -0400 Subject: [PATCH 08/43] sign up not done --- .env.example | 3 --- api/index.js | 2 +- auth/index.js | 6 +++--- database/db.js | 2 +- database/user.js | 22 ++++++++++++++++++++++ package-lock.json | 4 ++-- 6 files changed, 29 insertions(+), 10 deletions(-) delete mode 100644 .env.example diff --git a/.env.example b/.env.example deleted file mode 100644 index 848ad61..0000000 --- a/.env.example +++ /dev/null @@ -1,3 +0,0 @@ -DATABASE_URL=postgres://hostname:5432/database-name -JWT_SECRET=super-secret-jwt-key -FRONTEND_URL=http://localhost:3000 diff --git a/api/index.js b/api/index.js index f08162e..73912b1 100644 --- a/api/index.js +++ b/api/index.js @@ -4,4 +4,4 @@ const testDbRouter = require("./test-db"); router.use("/test-db", testDbRouter); -module.exports = router; +module.exports = router; \ No newline at end of file diff --git a/auth/index.js b/auth/index.js index 07968c5..14a26f5 100644 --- a/auth/index.js +++ b/auth/index.js @@ -104,7 +104,7 @@ router.post("/auth0", async (req, res) => { // Signup route router.post("/signup", async (req, res) => { try { - const { username, password } = req.body; + const { firstName, lastName, username, profilePic, password } = req.body; if (!username || !password) { return res @@ -126,7 +126,7 @@ router.post("/signup", async (req, res) => { // Create new user const passwordHash = User.hashPassword(password); - const user = await User.create({ username, passwordHash }); + const user = await User.create({ username, passwordHash, firstName, lastName, profilePic }); // Generate JWT token const token = jwt.sign( @@ -149,7 +149,7 @@ router.post("/signup", async (req, res) => { res.send({ message: "User created successfully", - user: { id: user.id, username: user.username }, + user: { id: user.id, username: user.username, firstName: user.firstName, lastName: user.lastName, profilePic: user.lastName, }, }); } catch (error) { console.error("Signup error:", error); diff --git a/database/db.js b/database/db.js index b251a9d..3b580f1 100644 --- a/database/db.js +++ b/database/db.js @@ -3,7 +3,7 @@ const { Sequelize } = require("sequelize"); const pg = require("pg"); // Feel free to rename the database to whatever you want! -const dbName = "capstone-1"; +const dbName = "capstone-2"; const db = new Sequelize( process.env.DATABASE_URL || `postgres://localhost:5432/${dbName}`, diff --git a/database/user.js b/database/user.js index 755c757..3654750 100644 --- a/database/user.js +++ b/database/user.js @@ -3,6 +3,20 @@ const db = require("./db"); const bcrypt = require("bcrypt"); const User = db.define("user", { + firstName: { + type: DataTypes.STRING, + allowNull: false, + validate: { + len: [1, 30], + } + }, + lastName: { + type: DataTypes.STRING, + allowNull: false, + validate: { + len: [1, 30], + } + }, username: { type: DataTypes.STRING, allowNull: false, @@ -19,6 +33,14 @@ const User = db.define("user", { isEmail: true, }, }, + profilePic: { + type: DataTypes.STRING, + allowNull: true, + defaultValue: "https://i.imgur.com/placeholder.png", + validate: { + isUrl: true, + }, + }, auth0Id: { type: DataTypes.STRING, allowNull: true, diff --git a/package-lock.json b/package-lock.json index af0cf82..1b30289 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "capstone-i-backend", + "name": "capstone-1-backend", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "capstone-i-backend", + "name": "capstone-1-backend", "version": "1.0.0", "license": "ISC", "dependencies": { From 2f2ce1a06693a360bbf05cabdc213282b57d344a Mon Sep 17 00:00:00 2001 From: cranberry Date: Thu, 31 Jul 2025 16:03:16 -0400 Subject: [PATCH 09/43] not fully working ocr --- api/index.js | 3 +++ api/veryfiClient.js | 17 +++++++++++++++++ package-lock.json | 4 ++-- package.json | 4 ++-- 4 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 api/veryfiClient.js diff --git a/api/index.js b/api/index.js index f08162e..b10b63c 100644 --- a/api/index.js +++ b/api/index.js @@ -1,7 +1,10 @@ const express = require("express"); const router = express.Router(); const testDbRouter = require("./test-db"); +const Verify = require("./Verify "); router.use("/test-db", testDbRouter); +//test OCR +router.use("/tesseract", Verify ); module.exports = router; diff --git a/api/veryfiClient.js b/api/veryfiClient.js new file mode 100644 index 0000000..871ace2 --- /dev/null +++ b/api/veryfiClient.js @@ -0,0 +1,17 @@ +const express = require("express"); +const router = express.Router(); +const axios = require("axios"); +const fs = require("fs"); +const FormData = require("form-data"); + +const form = new FormData(); +form + + + + + + + + +module.exports = router; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 66a6dc5..00bd0cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,8 +18,8 @@ "morgan": "^1.10.0", "pg": "^8.16.2", "sequelize": "^6.37.7", - "tesseract.js": "^6.0.1", - "socket.io": "^4.8.1" + "socket.io": "^4.8.1", + "tesseract.js": "^6.0.1" }, "devDependencies": { "nodemon": "^3.1.10" diff --git a/package.json b/package.json index 4ffbdc8..16c38f8 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,8 @@ "morgan": "^1.10.0", "pg": "^8.16.2", "sequelize": "^6.37.7", - "tesseract.js": "^6.0.1", - "socket.io": "^4.8.1" + "socket.io": "^4.8.1", + "tesseract.js": "^6.0.1" }, "devDependencies": { "nodemon": "^3.1.10" From f6bcf324c3af256d58bedfe1de4fb4de4bb77628 Mon Sep 17 00:00:00 2001 From: cranberry Date: Thu, 31 Jul 2025 16:05:01 -0400 Subject: [PATCH 10/43] update --- api/veryfiClient.js | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/api/veryfiClient.js b/api/veryfiClient.js index 871ace2..ca39ebc 100644 --- a/api/veryfiClient.js +++ b/api/veryfiClient.js @@ -1,17 +1,30 @@ const express = require("express"); const router = express.Router(); -const axios = require("axios"); -const fs = require("fs"); -const FormData = require("form-data"); -const form = new FormData(); -form - - - - - - - - -module.exports = router; \ No newline at end of file +require('dotenv').config(); +const axios = require('axios'); + +const VERYFI_API_URL = 'https://api.veryfi.com/api/v8/partner/documents/'; + +const headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Client-Id': process.env.VERYFI_CLIENT_ID, + 'Authorization': `apikey ${process.env.VERYFI_USERNAME}:${process.env.VERYFI_API_KEY}` +}; + +async function processDocument(fileName, fileDataBase64) { + try { + const response = await axios.post(VERYFI_API_URL, { + file_name: fileName, + file_data: fileDataBase64 + }, { headers }); + + return response.data; + } catch (error) { + console.error('Veryfi Error:', error.response?.data || error.message); + throw error; + } +} + +module.exports = { processDocument, router }; From 86a9c8e5831871845539731afab3250101fe7340 Mon Sep 17 00:00:00 2001 From: Jocsan Rodriguez Date: Thu, 31 Jul 2025 23:48:22 -0400 Subject: [PATCH 11/43] verify added --- api/index.js | 5 +- api/veryfiClient.js | 5 +- api/veryfiReceipt.js | 31 +++++++++ package-lock.json | 152 ++++++++++++++++++++++++++++++++++++++++--- package.json | 1 + socket-server.js | 12 +++- 6 files changed, 187 insertions(+), 19 deletions(-) create mode 100644 api/veryfiReceipt.js diff --git a/api/index.js b/api/index.js index b10b63c..ed02308 100644 --- a/api/index.js +++ b/api/index.js @@ -1,10 +1,11 @@ const express = require("express"); const router = express.Router(); const testDbRouter = require("./test-db"); -const Verify = require("./Verify "); +const veryfiClient = require("./veryfiClient"); +const veryfiReceipt = require("./veryfiReceipt"); router.use("/test-db", testDbRouter); //test OCR -router.use("/tesseract", Verify ); +router.use("/veryfiReceipt", veryfiReceipt) module.exports = router; diff --git a/api/veryfiClient.js b/api/veryfiClient.js index ca39ebc..ac2cf53 100644 --- a/api/veryfiClient.js +++ b/api/veryfiClient.js @@ -1,6 +1,3 @@ -const express = require("express"); -const router = express.Router(); - require('dotenv').config(); const axios = require('axios'); @@ -27,4 +24,4 @@ async function processDocument(fileName, fileDataBase64) { } } -module.exports = { processDocument, router }; +module.exports = { processDocument }; diff --git a/api/veryfiReceipt.js b/api/veryfiReceipt.js new file mode 100644 index 0000000..775aa2e --- /dev/null +++ b/api/veryfiReceipt.js @@ -0,0 +1,31 @@ +const express = require("express"); +const router = express.Router(); +const { processDocument } = require("./veryfiClient"); + +//Base64: It encodes binary data as ASCII characters (letters, digits, +, /). + +router.post("/upload", async (req, res) => { + try { + let { fileName, fileDataBase64 } = req.body; + + if (!fileName || !fileDataBase64) { + return res + .status(400) + .json({ error: "Missing fileName or fileDataBase64" }); + } + + //It removes the "data URL prefix" from a base64 string + fileDataBase64 = fileDataBase64.replace(/^data:\w+\/\w+;base64,/, ""); + + // Send the file name and base64-encoded file content to Veryfi's OCR API + const result = await processDocument(fileName, fileDataBase64); + + console.log("Veryfi result:", JSON.stringify(result, null, 2)); + res.json(result); + } catch (err) { + console.error("Veryfi upload error:", err); + res.status(500).json({ error: err.message }); + } +}); + +module.exports = router; diff --git a/package-lock.json b/package-lock.json index 00bd0cb..f294f4c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "axios": "^1.11.0", "bcrypt": "^6.0.0", "cookie-parser": "^1.4.7", "cors": "^2.8.5", @@ -100,6 +101,23 @@ "node": ">= 8" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", + "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -188,9 +206,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -280,6 +298,18 @@ "fsevents": "~2.3.2" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -375,6 +405,15 @@ } } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -559,6 +598,21 @@ "node": ">= 0.4" } }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -646,6 +700,63 @@ "node": ">= 0.8" } }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -772,6 +883,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -1084,16 +1210,16 @@ } }, "node_modules/morgan": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", - "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz", + "integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==", "license": "MIT", "dependencies": { "basic-auth": "~2.0.1", "debug": "2.6.9", "depd": "~2.0.0", "on-finished": "~2.3.0", - "on-headers": "~1.0.2" + "on-headers": "~1.1.0" }, "engines": { "node": ">= 0.8.0" @@ -1254,9 +1380,9 @@ } }, "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -1452,6 +1578,12 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", diff --git a/package.json b/package.json index 16c38f8..a65c62a 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "license": "ISC", "description": "", "dependencies": { + "axios": "^1.11.0", "bcrypt": "^6.0.0", "cookie-parser": "^1.4.7", "cors": "^2.8.5", diff --git a/socket-server.js b/socket-server.js index d887933..80bff3b 100644 --- a/socket-server.js +++ b/socket-server.js @@ -3,14 +3,20 @@ const { Server } = require("socket.io"); let io; const FRONTEND_URL = process.env.FRONTEND_URL || "http://localhost:3000"; + const corsOptions = process.env.NODE_ENV === "production" ? { - origin: FRONTEND_URL, - credentials: true, + cors: { + origin: FRONTEND_URL, + credentials: true, + }, } : { - cors: "*", + cors: { + origin: "*", + methods: ["GET", "POST"], + }, }; const initSocketServer = (server) => { From 8edad294017fcefcba2fb8cbaa7b733e97fac2ce Mon Sep 17 00:00:00 2001 From: Muhammad Date: Fri, 1 Aug 2025 10:34:11 -0400 Subject: [PATCH 12/43] W.I.P --- package-lock.json | 24 ++++++++++++++---------- package.json | 4 ++++ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2f4614f..064f0ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,10 @@ "morgan": "^1.10.0", "pg": "^8.16.2", "sequelize": "^6.37.7", +<<<<<<< Updated upstream +======= + "socket.io": "^4.8.1", +>>>>>>> Stashed changes "tesseract.js": "^6.0.1" }, "devDependencies": { @@ -163,9 +167,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -970,16 +974,16 @@ } }, "node_modules/morgan": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", - "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz", + "integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==", "license": "MIT", "dependencies": { "basic-auth": "~2.0.1", "debug": "2.6.9", "depd": "~2.0.0", "on-finished": "~2.3.0", - "on-headers": "~1.0.2" + "on-headers": "~1.1.0" }, "engines": { "node": ">= 0.8.0" @@ -1140,9 +1144,9 @@ } }, "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "license": "MIT", "engines": { "node": ">= 0.8" diff --git a/package.json b/package.json index 1872100..6bf7589 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,10 @@ "morgan": "^1.10.0", "pg": "^8.16.2", "sequelize": "^6.37.7", +<<<<<<< Updated upstream +======= + "socket.io": "^4.8.1", +>>>>>>> Stashed changes "tesseract.js": "^6.0.1" }, "devDependencies": { From 3e861d9b34b921853784f47586e191d4f3c1bbc0 Mon Sep 17 00:00:00 2001 From: WilliamBoop Date: Fri, 1 Aug 2025 10:48:07 -0400 Subject: [PATCH 13/43] Updated user model with first name, last name, profile pic, and email. Other files related to this were also updated --- database/seed.js | 6 +++--- package-lock.json | 12 ++---------- package.json | 4 ++-- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/database/seed.js b/database/seed.js index 991d4ef..94ff845 100644 --- a/database/seed.js +++ b/database/seed.js @@ -8,9 +8,9 @@ const seed = async () => { await db.sync({ force: true }); // Drop and recreate tables const users = await User.bulkCreate([ - { username: "admin", passwordHash: User.hashPassword("admin123") }, - { username: "user1", passwordHash: User.hashPassword("user111") }, - { username: "user2", passwordHash: User.hashPassword("user222") }, + { username: "admin", passwordHash: User.hashPassword("admin123"), firstName: "Alice", lastName: "Admin", email: "admin@example.com", profilePic: "https://i.pravatar.cc/150?img=1" }, + { username: "user1", passwordHash: User.hashPassword("user111"), firstName: "Bob", lastName: "Builder", email: "bob@example.com", profilePic: "https://i.pravatar.cc/150?img=1" }, + { username: "user2", passwordHash: User.hashPassword("user222"), firstName: "Carol", lastName: "Coder", email: "carol@example.com", profilePic: "https://i.pravatar.cc/150?img=1" }, ]); console.log(`๐Ÿ‘ค Created ${users.length} users`); diff --git a/package-lock.json b/package-lock.json index 9379f8c..00bd0cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,11 @@ { -<<<<<<< HEAD - "name": "capstone-1-backend", -======= "name": "capstone-2-backend", ->>>>>>> origin/jocsan "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { -<<<<<<< HEAD - "name": "capstone-1-backend", -======= "name": "capstone-2-backend", ->>>>>>> origin/jocsan "version": "1.0.0", "license": "ISC", "dependencies": { @@ -26,8 +18,8 @@ "morgan": "^1.10.0", "pg": "^8.16.2", "sequelize": "^6.37.7", - "tesseract.js": "^6.0.1", - "socket.io": "^4.8.1" + "socket.io": "^4.8.1", + "tesseract.js": "^6.0.1" }, "devDependencies": { "nodemon": "^3.1.10" diff --git a/package.json b/package.json index 4ffbdc8..16c38f8 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,8 @@ "morgan": "^1.10.0", "pg": "^8.16.2", "sequelize": "^6.37.7", - "tesseract.js": "^6.0.1", - "socket.io": "^4.8.1" + "socket.io": "^4.8.1", + "tesseract.js": "^6.0.1" }, "devDependencies": { "nodemon": "^3.1.10" From d5b1c130d2a77acd1aba168b2ed157700db96fdb Mon Sep 17 00:00:00 2001 From: Jocsan Rodriguez Date: Fri, 1 Aug 2025 11:45:38 -0400 Subject: [PATCH 14/43] update --- api/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/api/index.js b/api/index.js index 1a62d25..1a2c7ca 100644 --- a/api/index.js +++ b/api/index.js @@ -1,7 +1,6 @@ const express = require("express"); const router = express.Router(); const testDbRouter = require("./test-db"); -const veryfiClient = require("./veryfiClient"); const veryfiReceipt = require("./veryfiReceipt"); const users = require("./users"); From c9d833cbaebf4ac89ae10e45aa7b5557d5778f64 Mon Sep 17 00:00:00 2001 From: Jocsan Rodriguez Date: Fri, 1 Aug 2025 12:13:20 -0400 Subject: [PATCH 15/43] create group route --- api/users.js | 17 ++++++++++++++++- database/groups.js | 6 +++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/api/users.js b/api/users.js index 1d75f8c..5dd7c52 100644 --- a/api/users.js +++ b/api/users.js @@ -1,8 +1,9 @@ const express = require("express"); const router = express.Router(); const { adminAuthenticate, authenticateJWT } = require("../auth"); -const { User } = require("../database"); +const { User, Group } = require("../database"); +// get all users router.get("/Allusers", async (req, res) => { try { const getAllUsers = await User.findAll(); @@ -13,7 +14,21 @@ router.get("/Allusers", async (req, res) => { } }); +// create a group +router.post("/create", async (req, res) => { + const { Owner, groupName, Receipt_Id } = req.body; + if (!Owner) { + return res.status(400).json({ error: "Owner is required" }); + } + try { + const newGroup = await Group.create({ Owner, groupName, Receipt_Id }); + res.status(201).json(newGroup); + } catch (err) { + console.error(err); + res.status(500).json({ error: "Failed to create group" }); + } +}); module.exports = router; diff --git a/database/groups.js b/database/groups.js index 9338649..d106f10 100644 --- a/database/groups.js +++ b/database/groups.js @@ -6,17 +6,17 @@ const Group = db.define { Owner: { type: DataTypes.INTEGER, - allownull: false, + allowNull : false, }, groupName: { type: DataTypes.TEXT, - allownull: true, + allowNull : true, }, Receipt_Id: { type: DataTypes.INTEGER, - allownull: false, + allowNull : true, }, }); From 9716d06bcbf6be180957b8cf93eeb3c3399c9585 Mon Sep 17 00:00:00 2001 From: Muhammad Date: Fri, 1 Aug 2025 14:28:59 -0400 Subject: [PATCH 16/43] did the delete route --- api/index.js | 3 ++- api/items.js | 14 ++++++++++++++ api/users.js | 19 +++++++++++++++++++ database/groups.js | 30 ++++++++++++++---------------- 4 files changed, 49 insertions(+), 17 deletions(-) create mode 100644 api/items.js diff --git a/api/index.js b/api/index.js index 1a2c7ca..5e1582f 100644 --- a/api/index.js +++ b/api/index.js @@ -3,10 +3,11 @@ const router = express.Router(); const testDbRouter = require("./test-db"); const veryfiReceipt = require("./veryfiReceipt"); const users = require("./users"); +const items = require("./items"); router.use("/test-db", testDbRouter); //test OCR router.use("/veryfiReceipt", veryfiReceipt); router.use("/users", users); - +router.use("/items", items); module.exports = router; diff --git a/api/items.js b/api/items.js new file mode 100644 index 0000000..bdb9503 --- /dev/null +++ b/api/items.js @@ -0,0 +1,14 @@ +const express = require("express"); +const router = express.Router(); +const { adminAuthenticate, authenticateJWT } = require("../auth"); +const { User, Group, Item } = require("../database"); + +router.get("/Allitems", async (req, res) => { + try { + const getAll = await Item.findAll(); + res.sendStatus(200); + } catch (err) { + console.error("can not get all the items", err); + res.status(400).json({ message: "there no items" }); + } +}); diff --git a/api/users.js b/api/users.js index 5dd7c52..b8df218 100644 --- a/api/users.js +++ b/api/users.js @@ -31,4 +31,23 @@ router.post("/create", async (req, res) => { } }); +router.delete("/:id", async (req, res) => { + try { + if (!Group) { + return res.status(400).json({ error: "group does not exist" }); + } + + const url = Number(req.params.id); + const deletes = await Group.findByPk(url); + await deletes.destroy(); + res.sendStatus(200); + } catch (err) { + console.error(err); + res.status(500).send({ error: "faillure" }); + } +}); + + + + module.exports = router; diff --git a/database/groups.js b/database/groups.js index d106f10..8eaa465 100644 --- a/database/groups.js +++ b/database/groups.js @@ -1,23 +1,21 @@ const { DataTypes } = require("sequelize"); const db = require("./db"); -const Group = db.define - ("groups", - { - Owner: { - type: DataTypes.INTEGER, - allowNull : false, - }, +const Group = db.define("groups", { + Owner: { + type: DataTypes.INTEGER, + allowNull: false, + }, - groupName: { - type: DataTypes.TEXT, - allowNull : true, - }, + groupName: { + type: DataTypes.TEXT, + allowNull: true, + }, - Receipt_Id: { - type: DataTypes.INTEGER, - allowNull : true, - }, - }); + Receipt_Id: { + type: DataTypes.INTEGER, + allowNull: true, + }, +}); module.exports = Group; From ca04042b3a89f5ae4ed546f1a58defb70d14066b Mon Sep 17 00:00:00 2001 From: Muhammad Date: Fri, 1 Aug 2025 14:33:53 -0400 Subject: [PATCH 17/43] did the get all items --- api/items.js | 1 + 1 file changed, 1 insertion(+) diff --git a/api/items.js b/api/items.js index bdb9503..4ab60b6 100644 --- a/api/items.js +++ b/api/items.js @@ -12,3 +12,4 @@ router.get("/Allitems", async (req, res) => { res.status(400).json({ message: "there no items" }); } }); +module.exports = router; From 989ba821605cb41cc1205ee5ab5af86bf1ffde1b Mon Sep 17 00:00:00 2001 From: Jocsan Rodriguez Date: Fri, 1 Aug 2025 14:38:59 -0400 Subject: [PATCH 18/43] changed some group related stuff, added a group file --- api/group.js | 37 +++++++++++++++++++++++++++++ api/index.js | 4 ++++ api/users.js | 19 ++------------- database/groups.js | 32 +++++++++++-------------- database/index.js | 13 +++++++--- database/{recipts.js => receipt.js} | 5 ++-- 6 files changed, 69 insertions(+), 41 deletions(-) create mode 100644 api/group.js rename database/{recipts.js => receipt.js} (80%) diff --git a/api/group.js b/api/group.js new file mode 100644 index 0000000..a0a08d8 --- /dev/null +++ b/api/group.js @@ -0,0 +1,37 @@ +const express = require("express"); +const router = express.Router(); +const { authenticateJWT } = require("../auth"); +const { User, Group } = require("../database"); + +// create a group +router.post("/create", async (req, res) => { + const userId = req.user?.id; + + if (!userId) { + return res.status(404).json({ error: "Unauthorized" }); + } + + const { groupName, Receipt_Id } = req.body; + + try { + const user = await User.findByPk(userId); + if (!user) { + return res.status(404).json({ error: "User not found" }); + } + + const newGroup = await Group.create({ + Owner: userId, + groupName, + Receipt_Id, + }); + + res.status(200).json(newGroup); + } catch (err) { + console.error(err); + res.status(500).json({ error: "Failed to create group" }); + } +}); + + + +module.exports = router; diff --git a/api/index.js b/api/index.js index 1a2c7ca..07990fb 100644 --- a/api/index.js +++ b/api/index.js @@ -3,10 +3,14 @@ const router = express.Router(); const testDbRouter = require("./test-db"); const veryfiReceipt = require("./veryfiReceipt"); const users = require("./users"); +const group = require("./group"); router.use("/test-db", testDbRouter); //test OCR router.use("/veryfiReceipt", veryfiReceipt); router.use("/users", users); +router.use("/group", group); module.exports = router; + + diff --git a/api/users.js b/api/users.js index 5dd7c52..163cd32 100644 --- a/api/users.js +++ b/api/users.js @@ -1,7 +1,7 @@ const express = require("express"); const router = express.Router(); -const { adminAuthenticate, authenticateJWT } = require("../auth"); -const { User, Group } = require("../database"); +const { authenticateJWT } = require("../auth"); +const { User, Group, Receipts } = require("../database"); // get all users router.get("/Allusers", async (req, res) => { @@ -14,21 +14,6 @@ router.get("/Allusers", async (req, res) => { } }); -// create a group -router.post("/create", async (req, res) => { - const { Owner, groupName, Receipt_Id } = req.body; - if (!Owner) { - return res.status(400).json({ error: "Owner is required" }); - } - - try { - const newGroup = await Group.create({ Owner, groupName, Receipt_Id }); - res.status(201).json(newGroup); - } catch (err) { - console.error(err); - res.status(500).json({ error: "Failed to create group" }); - } -}); module.exports = router; diff --git a/database/groups.js b/database/groups.js index d106f10..38570d6 100644 --- a/database/groups.js +++ b/database/groups.js @@ -1,23 +1,19 @@ const { DataTypes } = require("sequelize"); const db = require("./db"); -const Group = db.define - ("groups", - { - Owner: { - type: DataTypes.INTEGER, - allowNull : false, - }, - - groupName: { - type: DataTypes.TEXT, - allowNull : true, - }, - - Receipt_Id: { - type: DataTypes.INTEGER, - allowNull : true, - }, - }); +const Group = db.define("groups", { + Owner: { + type: DataTypes.INTEGER, + allowNull: false, + }, + groupName: { + type: DataTypes.TEXT, + allowNull: true, + }, + Receipt_Id: { + type: DataTypes.INTEGER, + allowNull: true, + }, +}); module.exports = Group; diff --git a/database/index.js b/database/index.js index a09f4fe..ca27dc8 100644 --- a/database/index.js +++ b/database/index.js @@ -4,22 +4,29 @@ const Receipts = require("./recipts"); const Item = require("./item"); const Group = require("./groups"); - +// 1. Users and Receipts User.hasMany(Receipts, { foreignKey: "User_Id" }); Receipts.belongsTo(User, { foreignKey: "User_Id" }); +// 2. Receipts and Items Receipts.hasMany(Item, { foreignKey: "Receipt_Id" }); Item.belongsTo(Receipts, { foreignKey: "Receipt_Id" }); +// 3. Users and Groups (Owner) User.hasMany(Group, { foreignKey: "Owner" }); Group.belongsTo(User, { foreignKey: "Owner" }); -Group.belongsToMany(User, {through: "UserGroups"}); -User.belongsToMany(Group, {through: "UserGroups"}); +// 4. Users and Groups (Members) +Group.belongsToMany(User, { through: "UserGroups" }); +User.belongsToMany(Group, { through: "UserGroups" }); +// 5. Group has Receipts Group.hasMany(Receipts, { foreignKey: "Group_Id" }); Receipts.belongsTo(Group, { foreignKey: "Group_Id" }); +//Keeps the group but owner is set to null, is user for some reason removed +Group.belongsTo(User, { foreignKey: "Owner", onDelete: "SET NULL" }); + module.exports = { db, User, diff --git a/database/recipts.js b/database/receipt.js similarity index 80% rename from database/recipts.js rename to database/receipt.js index 674996e..4e8cef3 100644 --- a/database/recipts.js +++ b/database/receipt.js @@ -1,8 +1,7 @@ const { DataTypes } = require("sequelize"); const db = require("./db"); -// const { use } = require("react"); -const Recipt = db.define("receipt", { +const Receipt = db.define("receipt", { title: { type: DataTypes.STRING, @@ -26,4 +25,4 @@ const Recipt = db.define("receipt", { }); -module.exports = Recipt; \ No newline at end of file +module.exports = Receipt; \ No newline at end of file From cd39a6834a0ecc44c16763ea277026ee4bb3c36d Mon Sep 17 00:00:00 2001 From: Jocsan Rodriguez Date: Fri, 1 Aug 2025 14:40:41 -0400 Subject: [PATCH 19/43] quick fix --- api/group.js | 16 ++++++++++++++++ api/users.js | 16 ---------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/api/group.js b/api/group.js index a0a08d8..60bd264 100644 --- a/api/group.js +++ b/api/group.js @@ -32,6 +32,22 @@ router.post("/create", async (req, res) => { } }); +router.delete("/:id", async (req, res) => { + try { + if (!Group) { + return res.status(400).json({ error: "group does not exist" }); + } + + const url = Number(req.params.id); + const deletes = await Group.findByPk(url); + await deletes.destroy(); + res.sendStatus(200); + } catch (err) { + console.error(err); + res.status(500).send({ error: "faillure" }); + } +}); + module.exports = router; diff --git a/api/users.js b/api/users.js index c627185..40a02d7 100644 --- a/api/users.js +++ b/api/users.js @@ -16,22 +16,6 @@ router.get("/Allusers", async (req, res) => { -router.delete("/:id", async (req, res) => { - try { - if (!Group) { - return res.status(400).json({ error: "group does not exist" }); - } - - const url = Number(req.params.id); - const deletes = await Group.findByPk(url); - await deletes.destroy(); - res.sendStatus(200); - } catch (err) { - console.error(err); - res.status(500).send({ error: "faillure" }); - } -}); - From 8ba48a6592d07613d3002b4c3c55153ee83681f6 Mon Sep 17 00:00:00 2001 From: Bryan Zhong Date: Fri, 1 Aug 2025 15:28:34 -0400 Subject: [PATCH 20/43] Added routes to get all receipts, get a single receipt, and post a receipt with its associated items. --- api/index.js | 2 + api/receipts.js | 64 +++++++++++++++++++++++++++++ database/index.js | 2 +- database/item.js | 2 +- database/{recipts.js => receipt.js} | 4 +- 5 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 api/receipts.js rename database/{recipts.js => receipt.js} (87%) diff --git a/api/index.js b/api/index.js index f08162e..73be47d 100644 --- a/api/index.js +++ b/api/index.js @@ -1,7 +1,9 @@ const express = require("express"); const router = express.Router(); const testDbRouter = require("./test-db"); +const receiptsRouter = require("./receipts"); router.use("/test-db", testDbRouter); +router.use("/receipts", receiptsRouter); module.exports = router; diff --git a/api/receipts.js b/api/receipts.js new file mode 100644 index 0000000..047fb08 --- /dev/null +++ b/api/receipts.js @@ -0,0 +1,64 @@ +const express = require("express"); +const router = express.Router(); +const { Receipts, Item } = require("../database"); + +// GET all receipts +router.get("/", async (req, res) => { + try { + const receipts = await Receipts.findAll(); + res.status(200).send(receipts); + } catch (error) { + console.error("Error fetching receipts:", error); + res.status(500).send("Failed to fetch receipts."); + } +}); + +// GET a specific receipt by id +router.get("/:id", async (req, res) => { + try { + const receiptId = Number(req.params.id); + const receipt = await Receipts.findByPk(receiptId); + if (!receipt) { + res.status(400).send("Receipt not found") + } + res.status(200).send(receipt); + } catch (error) { + console.error("Error fetching receipt by ID:", error); + res.status(500).send("Failed to find the receipt you were looking for."); + } +}); + +/* POST a receipt and its items, assuming that the request includes: +- receipt: Object +{ + title: string, + body: string, + User_Id: int + Group_Id: int +} +- items: Array of Objects +[ + {name: string, price: float, Receipt_id: int} +] +*/ +router.post("/", async (req, res) => { + try { + const { receipt, items } = req.body; + const newReceipt = await Receipts.create(receipt); // create the new receipt + const newReceiptId = newReceipt.id; // get the id of the created receipt + + // create the new items and associate them with this receipt + for (let i = 0; i < items.length; i++) { + const item = array[i]; + item.Receipt_id = newReceiptId; + await newReceipt.createItem(item); + } + + res.status(200).send(receipt) + } catch (error) { + console.error("Error posting receipt:", error); + res.status(500).send("Failed to post receipt.") + } +}); + +module.exports = router; \ No newline at end of file diff --git a/database/index.js b/database/index.js index df20ab0..7c7ed03 100644 --- a/database/index.js +++ b/database/index.js @@ -1,6 +1,6 @@ const db = require("./db"); const User = require("./user"); -const Receipts = require("./recipts"); +const Receipts = require("./receipt"); const Item = require("./item"); const Group = require("./groups"); diff --git a/database/item.js b/database/item.js index 6e51825..398f268 100644 --- a/database/item.js +++ b/database/item.js @@ -1,6 +1,6 @@ const { DataTypes } = require("sequelize"); const db = require("./db"); -const { Receipt } = require("./recipts"); +const { Receipt } = require("./receipt"); const Item = db.define("item", { diff --git a/database/recipts.js b/database/receipt.js similarity index 87% rename from database/recipts.js rename to database/receipt.js index 674996e..678b958 100644 --- a/database/recipts.js +++ b/database/receipt.js @@ -2,7 +2,7 @@ const { DataTypes } = require("sequelize"); const db = require("./db"); // const { use } = require("react"); -const Recipt = db.define("receipt", { +const Receipt = db.define("receipt", { title: { type: DataTypes.STRING, @@ -26,4 +26,4 @@ const Recipt = db.define("receipt", { }); -module.exports = Recipt; \ No newline at end of file +module.exports = Receipt; \ No newline at end of file From fc734d7e5fe974c9fddd018b03a20ca121bd7df6 Mon Sep 17 00:00:00 2001 From: Bryan Zhong Date: Fri, 1 Aug 2025 15:30:55 -0400 Subject: [PATCH 21/43] Fixed some documentation. --- api/receipts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/receipts.js b/api/receipts.js index 047fb08..ba70e2c 100644 --- a/api/receipts.js +++ b/api/receipts.js @@ -38,7 +38,7 @@ router.get("/:id", async (req, res) => { } - items: Array of Objects [ - {name: string, price: float, Receipt_id: int} + {name: string, price: float}, ... ] */ router.post("/", async (req, res) => { From 2ac830a5a082bd7a5902d82d6dba5d927cee7935 Mon Sep 17 00:00:00 2001 From: Muhammad Date: Fri, 1 Aug 2025 15:40:44 -0400 Subject: [PATCH 22/43] added the route to get User info --- api/items.js | 1 + api/users.js | 26 ++++++++++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/api/items.js b/api/items.js index 4ab60b6..3bd7c95 100644 --- a/api/items.js +++ b/api/items.js @@ -12,4 +12,5 @@ router.get("/Allitems", async (req, res) => { res.status(400).json({ message: "there no items" }); } }); + module.exports = router; diff --git a/api/users.js b/api/users.js index 40a02d7..033919e 100644 --- a/api/users.js +++ b/api/users.js @@ -14,9 +14,27 @@ router.get("/Allusers", async (req, res) => { } }); - - - - +router.get("/me", async (req, res) => { + try { + const userId = 1; + const userInfo = await User.findByPk(userId); + if (!userInfo) { + return res.status(404).json({ error: "user not found" }); + } + res.send({ + message: "SOMTHING", + userInfo: { + username: userInfo.username, + profilePic: userInfo.profilePic, + email: userInfo.email, + firstName: userInfo.firstName, + lastName: userInfo.lastName, + }, + }); + } catch (err) { + console.error("this is not working", err); + res.status(500).json({ message: "internal server error" }); + } +}); module.exports = router; From 88bb5b2fd8de1f8158ae39e70e67350a62ada41b Mon Sep 17 00:00:00 2001 From: Jocsan Rodriguez Date: Fri, 1 Aug 2025 15:40:56 -0400 Subject: [PATCH 23/43] test --- database/groups.js | 2 +- database/index.js | 2 +- database/item.js | 1 - database/{receipt.js => receipts.js} | 4 ++-- 4 files changed, 4 insertions(+), 5 deletions(-) rename database/{receipt.js => receipts.js} (85%) diff --git a/database/groups.js b/database/groups.js index 38570d6..deacddf 100644 --- a/database/groups.js +++ b/database/groups.js @@ -4,7 +4,7 @@ const db = require("./db"); const Group = db.define("groups", { Owner: { type: DataTypes.INTEGER, - allowNull: false, + allowNull: true, }, groupName: { type: DataTypes.TEXT, diff --git a/database/index.js b/database/index.js index ca27dc8..73fdd4e 100644 --- a/database/index.js +++ b/database/index.js @@ -1,6 +1,6 @@ const db = require("./db"); const User = require("./user"); -const Receipts = require("./recipts"); +const Receipts = require("./receipts") const Item = require("./item"); const Group = require("./groups"); diff --git a/database/item.js b/database/item.js index 6442864..6ddc87b 100644 --- a/database/item.js +++ b/database/item.js @@ -1,6 +1,5 @@ const { DataTypes } = require("sequelize"); const db = require("./db"); -const { Receipt } = require("./recipts"); const Item = db.define("item", { diff --git a/database/receipt.js b/database/receipts.js similarity index 85% rename from database/receipt.js rename to database/receipts.js index 4e8cef3..2fd84d6 100644 --- a/database/receipt.js +++ b/database/receipts.js @@ -1,7 +1,7 @@ const { DataTypes } = require("sequelize"); const db = require("./db"); -const Receipt = db.define("receipt", { +const Receipts = db.define("Receipts", { title: { type: DataTypes.STRING, @@ -25,4 +25,4 @@ const Receipt = db.define("receipt", { }); -module.exports = Receipt; \ No newline at end of file +module.exports = Receipts; \ No newline at end of file From 3ec6b2c974037fabd6ef09ca29d7b3b83a8f0d0c Mon Sep 17 00:00:00 2001 From: Bryan Zhong Date: Fri, 1 Aug 2025 15:41:18 -0400 Subject: [PATCH 24/43] Fixed variable name. --- api/receipts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/receipts.js b/api/receipts.js index ba70e2c..a702198 100644 --- a/api/receipts.js +++ b/api/receipts.js @@ -49,7 +49,7 @@ router.post("/", async (req, res) => { // create the new items and associate them with this receipt for (let i = 0; i < items.length; i++) { - const item = array[i]; + const item = items[i]; item.Receipt_id = newReceiptId; await newReceipt.createItem(item); } From dc0c8d3030f862fa45f0dd05fea9a9ac1de5a15a Mon Sep 17 00:00:00 2001 From: Muhammad Date: Fri, 1 Aug 2025 15:54:01 -0400 Subject: [PATCH 25/43] fix spelling mistakes --- database/index.js | 2 +- database/item.js | 31 +++++++++++++--------------- database/{receipt.js => receipts.js} | 0 3 files changed, 15 insertions(+), 18 deletions(-) rename database/{receipt.js => receipts.js} (100%) diff --git a/database/index.js b/database/index.js index ca27dc8..e3d68dd 100644 --- a/database/index.js +++ b/database/index.js @@ -1,6 +1,6 @@ const db = require("./db"); const User = require("./user"); -const Receipts = require("./recipts"); +const Receipts = require("./receipts"); const Item = require("./item"); const Group = require("./groups"); diff --git a/database/item.js b/database/item.js index 6442864..7635381 100644 --- a/database/item.js +++ b/database/item.js @@ -1,25 +1,22 @@ const { DataTypes } = require("sequelize"); const db = require("./db"); -const { Receipt } = require("./recipts"); +const { Receipt } = require("./receipts"); const Item = db.define("item", { + name: { + type: DataTypes.STRING, + allowNull: false, + }, - name: { - type: DataTypes.STRING, - allowNull: false, - }, + price: { + type: DataTypes.FLOAT, + allowNull: false, + }, - price: { - type: DataTypes.FLOAT, - allowNull: false, - }, - - Receipt_Id: { - type: DataTypes.INTEGER, - allowNull: false, - } - + Receipt_Id: { + type: DataTypes.INTEGER, + allowNull: false, + }, }); - -module.exports = Item; \ No newline at end of file +module.exports = Item; diff --git a/database/receipt.js b/database/receipts.js similarity index 100% rename from database/receipt.js rename to database/receipts.js From 9b57bb3e18013a5cc083f018ba4c3d96635956a2 Mon Sep 17 00:00:00 2001 From: Jocsan Rodriguez Date: Fri, 1 Aug 2025 19:13:19 -0400 Subject: [PATCH 26/43] Finish create group and add member, small change on delete --- database/index.js | 1 - database/receipts.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/database/index.js b/database/index.js index 73fdd4e..575c6bd 100644 --- a/database/index.js +++ b/database/index.js @@ -14,7 +14,6 @@ Item.belongsTo(Receipts, { foreignKey: "Receipt_Id" }); // 3. Users and Groups (Owner) User.hasMany(Group, { foreignKey: "Owner" }); -Group.belongsTo(User, { foreignKey: "Owner" }); // 4. Users and Groups (Members) Group.belongsToMany(User, { through: "UserGroups" }); diff --git a/database/receipts.js b/database/receipts.js index 2fd84d6..7e3aad0 100644 --- a/database/receipts.js +++ b/database/receipts.js @@ -1,7 +1,7 @@ const { DataTypes } = require("sequelize"); const db = require("./db"); -const Receipts = db.define("Receipts", { +const Receipts = db.define("receipts", { title: { type: DataTypes.STRING, From 93e9646a28c9847249e17d0046e7b1c55e6e801c Mon Sep 17 00:00:00 2001 From: Jocsan Rodriguez Date: Fri, 1 Aug 2025 19:26:19 -0400 Subject: [PATCH 27/43] fixed merge --- api/group.js | 38 +++++++++++++++++++++++++++++++------- api/items.js | 1 + app.js | 7 +++++++ 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/api/group.js b/api/group.js index 60bd264..b46c3b3 100644 --- a/api/group.js +++ b/api/group.js @@ -3,6 +3,9 @@ const router = express.Router(); const { authenticateJWT } = require("../auth"); const { User, Group } = require("../database"); +//add authenticateJWT later + + // create a group router.post("/create", async (req, res) => { const userId = req.user?.id; @@ -32,22 +35,43 @@ router.post("/create", async (req, res) => { } }); -router.delete("/:id", async (req, res) => { +//delete a group +router.delete("/delete/:id", async (req, res) => { try { - if (!Group) { - return res.status(400).json({ error: "group does not exist" }); + const groupId = Number(req.params.id); + + const group = await Group.findByPk(groupId); + if (!group) { + return res.status(404).json({ error: "Group does not exist" }); } - const url = Number(req.params.id); - const deletes = await Group.findByPk(url); - await deletes.destroy(); + await group.destroy(); res.sendStatus(200); } catch (err) { console.error(err); - res.status(500).send({ error: "faillure" }); + res.status(500).json({ error: "Could not delete group" }); } }); +//adding a user to a group +router.post("/:id/add-user", async (req, res) => { + try { + const groupId = Number(req.params.id); + const { userId } = req.body; + + const group = await Group.findByPk(groupId); + const user = await User.findByPk(userId); + + if (!group || !user) { + return res.status(404).json({ error: "Group or User not found" }); + } + await group.addUser(user); // magic lol + res.status(200).json({ message: "User added to group" }); + } catch (err) { + console.error(err); + res.status(500).json({ error: "Failed to add user to group" }); + } +}); module.exports = router; diff --git a/api/items.js b/api/items.js index 4ab60b6..e7f0c53 100644 --- a/api/items.js +++ b/api/items.js @@ -3,6 +3,7 @@ const router = express.Router(); const { adminAuthenticate, authenticateJWT } = require("../auth"); const { User, Group, Item } = require("../database"); + router.get("/Allitems", async (req, res) => { try { const getAll = await Item.findAll(); diff --git a/app.js b/app.js index 2574d33..7336e30 100644 --- a/app.js +++ b/app.js @@ -28,6 +28,13 @@ app.use(cookieParser()); app.use(morgan("dev")); // logging middleware app.use(express.static(path.join(__dirname, "public"))); // serve static files from public folder + +//test for group +app.use((req, res, next) => { + req.user = { id: 1 }; // mock user id 1 + next(); +}); + app.use("/api", apiRouter); // mount api router app.use("/auth", authRouter); // mount auth router From a962aaedd905f72fe6afb75e550c14a214cb9312 Mon Sep 17 00:00:00 2001 From: Jocsan Rodriguez Date: Sat, 2 Aug 2025 21:58:13 -0400 Subject: [PATCH 28/43] user can now make a invite, accept/decline --- api/group.js | 87 ++++++++++++++++++++++++++++++++++++++-------- api/users.js | 2 ++ database/groups.js | 8 +++++ database/index.js | 11 ++++++ database/invite.js | 27 ++++++++++++++ database/seed.js | 1 + 6 files changed, 121 insertions(+), 15 deletions(-) create mode 100644 database/invite.js diff --git a/api/group.js b/api/group.js index b46c3b3..1d45de8 100644 --- a/api/group.js +++ b/api/group.js @@ -1,11 +1,10 @@ const express = require("express"); const router = express.Router(); const { authenticateJWT } = require("../auth"); -const { User, Group } = require("../database"); +const { User, Group, Invite } = require("../database"); //add authenticateJWT later - // create a group router.post("/create", async (req, res) => { const userId = req.user?.id; @@ -14,7 +13,8 @@ router.post("/create", async (req, res) => { return res.status(404).json({ error: "Unauthorized" }); } - const { groupName, Receipt_Id } = req.body; + const { description, groupName, Receipt_Id } = req.body; + console.log("Request body:", req.body); try { const user = await User.findByPk(userId); @@ -24,6 +24,7 @@ router.post("/create", async (req, res) => { const newGroup = await Group.create({ Owner: userId, + description, groupName, Receipt_Id, }); @@ -35,7 +36,7 @@ router.post("/create", async (req, res) => { } }); -//delete a group +//delete a group (only owner is supposed to this) router.delete("/delete/:id", async (req, res) => { try { const groupId = Number(req.params.id); @@ -53,24 +54,80 @@ router.delete("/delete/:id", async (req, res) => { } }); -//adding a user to a group -router.post("/:id/add-user", async (req, res) => { +//Create an Invite +router.post("/invite", async (req, res) => { try { - const groupId = Number(req.params.id); - const { userId } = req.body; + const { senderId, receiverId, GroupId } = req.body; - const group = await Group.findByPk(groupId); - const user = await User.findByPk(userId); + const invite = await Invite.create({ + senderId, + receiverId, + GroupId, + status: "pending", + }); + + res.status(200).json(invite); + } catch (err) { + console.error(err); + res.status(500).json({ error: "Failed to create invite" }); + } +}); + +//Invite a user to a group(adding) +router.post("/invite/:invId/accept", async (req, res) => { + try { + const invite = await Invite.findByPk(req.params.invId); + + if (!invite || invite.status !== "pending") { + return res + .status(404) + .json({ error: "Invite not found or already handled" }); + } + + const user = await User.findByPk(invite.receiverId);//invite.receiverId + console.log(user); + const group = await Group.findByPk(invite.GroupId);//invite.GroupId + console.log(group); + if (!user || !group) { + return res.status(404).json({ error: "User or group not found" }); + } + + //this auto adds a user to a group, and updates the status to accepted + await group.addUser(user); + invite.status = "accepted"; + await invite.save(); + + res.status(200).json({ message: "Invite accepted, user added to group" }); + } catch (err) { + console.error(err); + res.status(500).json({ error: "Failed to accept invite" }); + } +}); + +//decline an invite +router.post("/invite/:id/decline", async (req, res) => { + try { + const invite = await Invite.findByPk(req.params.id); + if (!invite || invite.status !== "pending") { + return res + .status(404) + .json({ error: "Invite not found or already handled" }); + } + + const user = await User.findByPk(invite.receiverId); + const group = await Group.findByPk(invite.GroupId); - if (!group || !user) { - return res.status(404).json({ error: "Group or User not found" }); + if (!user || !group) { + return res.status(404).json({ error: "User or group not found" }); } - await group.addUser(user); // magic lol - res.status(200).json({ message: "User added to group" }); + //decline invite + invite.status = "declined"; + await invite.save(); + res.status(200).json({ message: "Invite declined" }); } catch (err) { console.error(err); - res.status(500).json({ error: "Failed to add user to group" }); + res.status(500).json({ error: "Failed to decline invite" }); } }); diff --git a/api/users.js b/api/users.js index 033919e..a24dabb 100644 --- a/api/users.js +++ b/api/users.js @@ -3,6 +3,8 @@ const router = express.Router(); const { authenticateJWT } = require("../auth"); const { User, Group, Receipts } = require("../database"); +// add authenticateJWT later + // get all users router.get("/Allusers", async (req, res) => { try { diff --git a/database/groups.js b/database/groups.js index deacddf..f763061 100644 --- a/database/groups.js +++ b/database/groups.js @@ -6,10 +6,18 @@ const Group = db.define("groups", { type: DataTypes.INTEGER, allowNull: true, }, + + // maybe something we can use? + description: { + type: DataTypes.TEXT, + allowNull: true, + }, + groupName: { type: DataTypes.TEXT, allowNull: true, }, + Receipt_Id: { type: DataTypes.INTEGER, allowNull: true, diff --git a/database/index.js b/database/index.js index 575c6bd..8db67b8 100644 --- a/database/index.js +++ b/database/index.js @@ -3,6 +3,7 @@ const User = require("./user"); const Receipts = require("./receipts") const Item = require("./item"); const Group = require("./groups"); +const Invite = require("./invite"); // 1. Users and Receipts User.hasMany(Receipts, { foreignKey: "User_Id" }); @@ -26,10 +27,20 @@ Receipts.belongsTo(Group, { foreignKey: "Group_Id" }); //Keeps the group but owner is set to null, is user for some reason removed Group.belongsTo(User, { foreignKey: "Owner", onDelete: "SET NULL" }); +// 6. Invites +User.hasMany(Invite, {as: "sentInvites", foreignKey: "senderId" }); +User.hasMany(Invite, {as: "receivedInvites", foreignKey: "receiverId"}); + +Invite.belongsTo(User, {as: "sender", foreignKey: "senderId" }); +Invite.belongsTo(User, {as: "receiver", foreignKey: "receiverId" }); +Invite.belongsTo(Group, { foreignKey: "GroupId" }); + + module.exports = { db, User, Receipts, Item, Group, + Invite, }; diff --git a/database/invite.js b/database/invite.js new file mode 100644 index 0000000..2bee832 --- /dev/null +++ b/database/invite.js @@ -0,0 +1,27 @@ +const { DataTypes } = require("sequelize"); +const db = require("./db"); + +const Invite = db.define("invite", { + senderId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + + receiverId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + + GroupId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + + status: { + type: DataTypes.ENUM("pending", "accepted", "declined"), + defaultValue: "pending", + allowNull: false, + }, +}); + +module.exports = Invite; diff --git a/database/seed.js b/database/seed.js index 94ff845..5272fb1 100644 --- a/database/seed.js +++ b/database/seed.js @@ -11,6 +11,7 @@ const seed = async () => { { username: "admin", passwordHash: User.hashPassword("admin123"), firstName: "Alice", lastName: "Admin", email: "admin@example.com", profilePic: "https://i.pravatar.cc/150?img=1" }, { username: "user1", passwordHash: User.hashPassword("user111"), firstName: "Bob", lastName: "Builder", email: "bob@example.com", profilePic: "https://i.pravatar.cc/150?img=1" }, { username: "user2", passwordHash: User.hashPassword("user222"), firstName: "Carol", lastName: "Coder", email: "carol@example.com", profilePic: "https://i.pravatar.cc/150?img=1" }, + { username: "user3", passwordHash: User.hashPassword("user333"), firstName: "Mike", lastName: "Snake", email: "Snake@example.com", profilePic: "https://i.pravatar.cc/150?img=1" }, ]); console.log(`๐Ÿ‘ค Created ${users.length} users`); From ddf41986a0f9d0f26596e3fa5db2ff7fd0ec90c7 Mon Sep 17 00:00:00 2001 From: Jocsan Rodriguez Date: Sat, 2 Aug 2025 22:04:51 -0400 Subject: [PATCH 29/43] updated delete group, added a new line in create --- api/group.js | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/api/group.js b/api/group.js index 1d45de8..d465dbc 100644 --- a/api/group.js +++ b/api/group.js @@ -29,7 +29,7 @@ router.post("/create", async (req, res) => { Receipt_Id, }); - res.status(200).json(newGroup); + res.status(201).json({message: "Group created successfully", group: newGroup}); } catch (err) { console.error(err); res.status(500).json({ error: "Failed to create group" }); @@ -39,15 +39,25 @@ router.post("/create", async (req, res) => { //delete a group (only owner is supposed to this) router.delete("/delete/:id", async (req, res) => { try { - const groupId = Number(req.params.id); + const userId = req.user?.id; //logged in user + if (!userId) { + return res.status(404).json({ error: "Unauthorized" }); + } + const groupId = Number(req.params.id); const group = await Group.findByPk(groupId); if (!group) { return res.status(404).json({ error: "Group does not exist" }); } + //check if logged-in user is the owner + if (group.Owner !== userId) { + //403, Forbidden status means the server understood the request but refuses to authorize it + return res.status(403).json({ error: "Forbidden: Not group owner" }); + } + await group.destroy(); - res.sendStatus(200); + res.status(200).json({ message: "Group deleted successfully" }); } catch (err) { console.error(err); res.status(500).json({ error: "Could not delete group" }); @@ -84,9 +94,9 @@ router.post("/invite/:invId/accept", async (req, res) => { .json({ error: "Invite not found or already handled" }); } - const user = await User.findByPk(invite.receiverId);//invite.receiverId + const user = await User.findByPk(invite.receiverId); //invite.receiverId console.log(user); - const group = await Group.findByPk(invite.GroupId);//invite.GroupId + const group = await Group.findByPk(invite.GroupId); //invite.GroupId console.log(group); if (!user || !group) { return res.status(404).json({ error: "User or group not found" }); @@ -121,7 +131,7 @@ router.post("/invite/:id/decline", async (req, res) => { return res.status(404).json({ error: "User or group not found" }); } - //decline invite + //decline invite invite.status = "declined"; await invite.save(); res.status(200).json({ message: "Invite declined" }); From 2b421ccbcaee43ade0add56007a08a84142eb4fd Mon Sep 17 00:00:00 2001 From: bryan-zhong46 Date: Mon, 4 Aug 2025 19:41:41 +0000 Subject: [PATCH 30/43] Added a route to get all items by receipt ID. --- .gitignore | 1 + api/receipts.js | 34 ++++++++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 400bedc..8f30558 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules .env .vercel +.replit \ No newline at end of file diff --git a/api/receipts.js b/api/receipts.js index a702198..f318e58 100644 --- a/api/receipts.js +++ b/api/receipts.js @@ -9,7 +9,7 @@ router.get("/", async (req, res) => { res.status(200).send(receipts); } catch (error) { console.error("Error fetching receipts:", error); - res.status(500).send("Failed to fetch receipts."); + res.status(500).send("Failed to fetch receipts."); } }); @@ -19,12 +19,30 @@ router.get("/:id", async (req, res) => { const receiptId = Number(req.params.id); const receipt = await Receipts.findByPk(receiptId); if (!receipt) { - res.status(400).send("Receipt not found") - } + res.status(400).send("Receipt not found"); + } res.status(200).send(receipt); } catch (error) { console.error("Error fetching receipt by ID:", error); - res.status(500).send("Failed to find the receipt you were looking for."); + res.status(500).send( + "Failed to find the receipt you were looking for.", + ); + } +}); + +// GET all items associated with a receipt +router.get("/:id/items", async (req, res) => { + try { + const receiptId = Number(req.params.id); + const receipt = await Receipts.findByPk(receiptId); + if (!receipt) { + res.status(400).send("Receipt not found"); + } + const items = await receipt.getItems(); + res.status(200).send(items); + } catch (error) { + console.error("Error fetching items by receipt ID:", error); + res.status(500).send("Failed to find the items you were looking for."); } }); @@ -43,7 +61,7 @@ router.get("/:id", async (req, res) => { */ router.post("/", async (req, res) => { try { - const { receipt, items } = req.body; + const { receipt, items } = req.body; const newReceipt = await Receipts.create(receipt); // create the new receipt const newReceiptId = newReceipt.id; // get the id of the created receipt @@ -54,11 +72,11 @@ router.post("/", async (req, res) => { await newReceipt.createItem(item); } - res.status(200).send(receipt) + res.status(200).send(receipt); } catch (error) { console.error("Error posting receipt:", error); - res.status(500).send("Failed to post receipt.") + res.status(500).send("Failed to post receipt."); } }); -module.exports = router; \ No newline at end of file +module.exports = router; From 4be8a78e9503edbc5c9775a1a841585f6c092ef4 Mon Sep 17 00:00:00 2001 From: bryan-zhong46 Date: Mon, 4 Aug 2025 19:55:14 +0000 Subject: [PATCH 31/43] Added route to delete a receipt. --- api/receipts.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/api/receipts.js b/api/receipts.js index f318e58..c5dece7 100644 --- a/api/receipts.js +++ b/api/receipts.js @@ -79,4 +79,19 @@ router.post("/", async (req, res) => { } }); +// DELETE a receipt +router.delete("/:id", async (req, res) => { + try { + const receiptId = Number(req.params.id); + const receipt = await Receipts.findByPk(receiptId); + if (!receipt) { + res.status(400).send("Receipt not found"); + } + await receipt.destroy(); + } catch (error) { + console.error("Error deleting receipt:", error); + res.status(500).send("Failed to delete receipt."); + } +}); + module.exports = router; From db8a73fdfafc4e1c84ccd70a9ff2b9bc8c2e6018 Mon Sep 17 00:00:00 2001 From: WilliamBoop Date: Tue, 5 Aug 2025 10:52:57 -0400 Subject: [PATCH 32/43] Updated and created routes for searching feature, includes autocomplete --- api/users.js | 56 +++++++++++++++++++++++++++++++++++++++++++++--- auth/index.js | 8 +++---- database/user.js | 2 +- 3 files changed, 58 insertions(+), 8 deletions(-) diff --git a/api/users.js b/api/users.js index 1d75f8c..c25285a 100644 --- a/api/users.js +++ b/api/users.js @@ -1,19 +1,69 @@ const express = require("express"); const router = express.Router(); -const { adminAuthenticate, authenticateJWT } = require("../auth"); +//const { adminAuthenticate, authenticateJWT } = require("../auth"); const { User } = require("../database"); +const { Op } = require("sequelize"); // Used for case-insenitive matching + router.get("/Allusers", async (req, res) => { try { - const getAllUsers = await User.findAll(); - res.status(200).send(getAllUsers); + const { page = 1, limit = 10, search = "" } = req.query; + const offset = (page - 1) * limit; + + // Build search filter + const whereClause = search + ? { + [Op.or]: [ + { username: { [Op.iLike]: `%${search}%`} }, + /*{ email: { [Op.iLike]: `%${search}%`} },*/ + ], + } + : {}; + + // This fetches the users and total count + const { count, rows: users } = await User.findAndCountAll ({ + where: whereClause, + limit: parseInt(limit), + offset: parseInt(offset), + order: [["username", "ASC"]], + }); + + res.status(200).json({ + totalUsers: count, + currentPage: parseInt(page), + totalPages: Math.ceil(count / limit), + users, + }) } catch (err) { console.error("error finding all users", err); res.status(500).json({ message: "internal server error" }); } }); +// Search Route (autocomplete) +router.get("/search", async (req, res) => { + try { + const { query } = req.query; + if (!query) { + return res.status(400).json({ message: "Query is required" }); + } + const users = await User.findAll({ + where: { + username: { + [Op.iLike]: `%${query}%`, // Matches input anywhere, it is case-insensitive + }, + }, + attributes: ["id", "username"], // Returns specific user + limit: 10, + }); + + res.json(users); + } catch (error) { + console.error("Error searching users:", error); + res.status(500).json({ message: "Internal server error" }); + } +}); module.exports = router; diff --git a/auth/index.js b/auth/index.js index 68ab6f3..2d6ddc3 100644 --- a/auth/index.js +++ b/auth/index.js @@ -106,7 +106,7 @@ router.post("/auth0", async (req, res) => { // Signup route router.post("/signup", async (req, res) => { try { - const { firstName, lastName, username, profilePic, password } = req.body; + const { firstName, lastName, username, password, email, profilePic } = req.body; if (!username || !password) { return res @@ -128,7 +128,7 @@ router.post("/signup", async (req, res) => { // Create new user const passwordHash = User.hashPassword(password); - const user = await User.create({ username, passwordHash, firstName, lastName, profilePic }); + const user = await User.create({ username, passwordHash, firstName, lastName, email, profilePic }); // Generate JWT token const token = jwt.sign( @@ -146,7 +146,7 @@ router.post("/signup", async (req, res) => { res.send({ message: "User created successfully", - user: { id: user.id, username: user.username, firstName: user.firstName, lastName: user.lastName, profilePic: user.lastName, }, + user: { id: user.id, username: user.username, firstName: user.firstName, lastName: user.lastName, profilePic: user.profileName, email: user.email }, }); } catch (error) { console.error("Signup error:", error); @@ -192,7 +192,7 @@ router.post("/login", async (req, res) => { res.send({ message: "Login successful", - user: { id: user.id, username: user.username }, + user: { id: user.id, username: user.username, firstName: user.firstName, lastname: user.lastName, email: user.email, profilePic: user.profilePic, }, }); } catch (error) { console.error("Login error:", error); diff --git a/database/user.js b/database/user.js index 3654750..d8ff9cc 100644 --- a/database/user.js +++ b/database/user.js @@ -27,7 +27,7 @@ const User = db.define("user", { }, email: { type: DataTypes.STRING, - allowNull: true, + allowNull: false, unique: true, validate: { isEmail: true, From 36663dadbc12d8301c205993c81dc612a5f2f1fa Mon Sep 17 00:00:00 2001 From: cranberry Date: Tue, 5 Aug 2025 11:43:24 -0400 Subject: [PATCH 33/43] not fully done with groups --- api/group.js | 32 +++++++++++++++++++++++--------- api/index.js | 11 ----------- api/items.js | 17 ----------------- api/test-db.js | 25 ------------------------- api/users.js | 13 ++++++++----- api/veryfiClient.js | 27 --------------------------- api/veryfiReceipt.js | 31 ------------------------------- app.js | 3 ++- database/groups.js | 8 ++++++-- 9 files changed, 39 insertions(+), 128 deletions(-) delete mode 100644 api/items.js delete mode 100644 api/test-db.js delete mode 100644 api/veryfiClient.js delete mode 100644 api/veryfiReceipt.js diff --git a/api/group.js b/api/group.js index d465dbc..1bf6656 100644 --- a/api/group.js +++ b/api/group.js @@ -3,11 +3,9 @@ const router = express.Router(); const { authenticateJWT } = require("../auth"); const { User, Group, Invite } = require("../database"); -//add authenticateJWT later - // create a group router.post("/create", async (req, res) => { - const userId = req.user?.id; + const userId =1; if (!userId) { return res.status(404).json({ error: "Unauthorized" }); @@ -37,7 +35,7 @@ router.post("/create", async (req, res) => { }); //delete a group (only owner is supposed to this) -router.delete("/delete/:id", async (req, res) => { +router.delete("/delete/:id", authenticateJWT, async (req, res) => { try { const userId = req.user?.id; //logged in user if (!userId) { @@ -45,6 +43,11 @@ router.delete("/delete/:id", async (req, res) => { } const groupId = Number(req.params.id); + + if(!groupId){ + return res.status(400).json({ error: "Group ID is required" }); + } + const group = await Group.findByPk(groupId); if (!group) { return res.status(404).json({ error: "Group does not exist" }); @@ -65,12 +68,23 @@ router.delete("/delete/:id", async (req, res) => { }); //Create an Invite -router.post("/invite", async (req, res) => { +router.post("/invite", authenticateJWT, async (req, res) => { try { - const { senderId, receiverId, GroupId } = req.body; + const userId = req.user?.id; //logged in user + if (!userId) { + return res.status(404).json({ error: "Unauthorized" }); + } + + const { receiverId, GroupId } = req.body; + + const group = await Group.findByPk(GroupId); + if (!group) { + return res.status(404).json({ error: "Group not found" }); + } + + const invite = await Invite.create({ - senderId, receiverId, GroupId, status: "pending", @@ -84,7 +98,7 @@ router.post("/invite", async (req, res) => { }); //Invite a user to a group(adding) -router.post("/invite/:invId/accept", async (req, res) => { +router.post("/invite/:invId/accept", authenticateJWT, async (req, res) => { try { const invite = await Invite.findByPk(req.params.invId); @@ -115,7 +129,7 @@ router.post("/invite/:invId/accept", async (req, res) => { }); //decline an invite -router.post("/invite/:id/decline", async (req, res) => { +router.post("/invite/:id/decline", authenticateJWT, async (req, res) => { try { const invite = await Invite.findByPk(req.params.id); if (!invite || invite.status !== "pending") { diff --git a/api/index.js b/api/index.js index 46912ed..c721057 100644 --- a/api/index.js +++ b/api/index.js @@ -1,23 +1,12 @@ const express = require("express"); const router = express.Router(); -const testDbRouter = require("./test-db"); -const veryfiReceipt = require("./veryfiReceipt"); const users = require("./users"); const group = require("./group"); -const items = require("./items"); const receiptsRouter = require("./receipts"); -router.use("/test-db", testDbRouter); - -//test OCR -router.use("/veryfiReceipt", veryfiReceipt); - //DB API router.use("/users", users); router.use("/group", group); -router.use("/items", items); - -//bryan? router.use("/receipts", receiptsRouter); module.exports = router; diff --git a/api/items.js b/api/items.js deleted file mode 100644 index 2b47bc7..0000000 --- a/api/items.js +++ /dev/null @@ -1,17 +0,0 @@ -const express = require("express"); -const router = express.Router(); -const { adminAuthenticate, authenticateJWT } = require("../auth"); -const { User, Group, Item } = require("../database"); - - -router.get("/Allitems", async (req, res) => { - try { - const getAll = await Item.findAll(); - res.sendStatus(200); - } catch (err) { - console.error("can not get all the items", err); - res.status(400).json({ message: "there no items" }); - } -}); - -module.exports = router; diff --git a/api/test-db.js b/api/test-db.js deleted file mode 100644 index 4109c34..0000000 --- a/api/test-db.js +++ /dev/null @@ -1,25 +0,0 @@ -const express = require("express"); -const router = express.Router(); -const { User } = require("../database"); - -// You don't actually need this route, it's just a good way to confirm that your database connection is working. -// Feel free to delete this entire file. -router.get("/", async (req, res) => { - try { - const users = await User.findAll(); - console.log(`Found ${users.length} users`); - res.json({ - message: "You successfully connected to the database ๐Ÿฅณ", - usersCount: users.length, - }); - } catch (error) { - console.error("Error fetching users:", error); - res.status(500).json({ - error: "Failed to fetch users", - message: - "Check your database connection, and consider running your seed file: npm run seed", - }); - } -}); - -module.exports = router; diff --git a/api/users.js b/api/users.js index a24dabb..6c2d542 100644 --- a/api/users.js +++ b/api/users.js @@ -3,10 +3,8 @@ const router = express.Router(); const { authenticateJWT } = require("../auth"); const { User, Group, Receipts } = require("../database"); -// add authenticateJWT later - // get all users -router.get("/Allusers", async (req, res) => { +router.get("/Allusers", authenticateJWT, async (req, res) => { try { const getAllUsers = await User.findAll(); res.status(200).send(getAllUsers); @@ -16,9 +14,14 @@ router.get("/Allusers", async (req, res) => { } }); -router.get("/me", async (req, res) => { +router.get("/me", authenticateJWT, async (req, res) => { try { - const userId = 1; + const userId = req.user?.id; // logged in user + + if (!userId) { + return res.status(404).json({ error: "Unauthorized" }); + } + const userInfo = await User.findByPk(userId); if (!userInfo) { return res.status(404).json({ error: "user not found" }); diff --git a/api/veryfiClient.js b/api/veryfiClient.js deleted file mode 100644 index ac2cf53..0000000 --- a/api/veryfiClient.js +++ /dev/null @@ -1,27 +0,0 @@ -require('dotenv').config(); -const axios = require('axios'); - -const VERYFI_API_URL = 'https://api.veryfi.com/api/v8/partner/documents/'; - -const headers = { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - 'Client-Id': process.env.VERYFI_CLIENT_ID, - 'Authorization': `apikey ${process.env.VERYFI_USERNAME}:${process.env.VERYFI_API_KEY}` -}; - -async function processDocument(fileName, fileDataBase64) { - try { - const response = await axios.post(VERYFI_API_URL, { - file_name: fileName, - file_data: fileDataBase64 - }, { headers }); - - return response.data; - } catch (error) { - console.error('Veryfi Error:', error.response?.data || error.message); - throw error; - } -} - -module.exports = { processDocument }; diff --git a/api/veryfiReceipt.js b/api/veryfiReceipt.js deleted file mode 100644 index 775aa2e..0000000 --- a/api/veryfiReceipt.js +++ /dev/null @@ -1,31 +0,0 @@ -const express = require("express"); -const router = express.Router(); -const { processDocument } = require("./veryfiClient"); - -//Base64: It encodes binary data as ASCII characters (letters, digits, +, /). - -router.post("/upload", async (req, res) => { - try { - let { fileName, fileDataBase64 } = req.body; - - if (!fileName || !fileDataBase64) { - return res - .status(400) - .json({ error: "Missing fileName or fileDataBase64" }); - } - - //It removes the "data URL prefix" from a base64 string - fileDataBase64 = fileDataBase64.replace(/^data:\w+\/\w+;base64,/, ""); - - // Send the file name and base64-encoded file content to Veryfi's OCR API - const result = await processDocument(fileName, fileDataBase64); - - console.log("Veryfi result:", JSON.stringify(result, null, 2)); - res.json(result); - } catch (err) { - console.error("Veryfi upload error:", err); - res.status(500).json({ error: err.message }); - } -}); - -module.exports = router; diff --git a/app.js b/app.js index 7336e30..ab1cc9b 100644 --- a/app.js +++ b/app.js @@ -14,7 +14,8 @@ const PORT = process.env.PORT || 8080; const FRONTEND_URL = process.env.FRONTEND_URL || "http://localhost:3000"; // body parser middleware -app.use(express.json()); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); app.use( cors({ diff --git a/database/groups.js b/database/groups.js index f763061..0d41486 100644 --- a/database/groups.js +++ b/database/groups.js @@ -5,10 +5,14 @@ const Group = db.define("groups", { Owner: { type: DataTypes.INTEGER, allowNull: true, + references: { + model: 'User', + key: 'id', + }, }, // maybe something we can use? - description: { + description: { type: DataTypes.TEXT, allowNull: true, }, @@ -17,7 +21,7 @@ const Group = db.define("groups", { type: DataTypes.TEXT, allowNull: true, }, - + Receipt_Id: { type: DataTypes.INTEGER, allowNull: true, From 51f81ab9ea98023d45118c099968e65edecd9ae5 Mon Sep 17 00:00:00 2001 From: Jocsan Rodriguez Date: Wed, 6 Aug 2025 00:25:30 -0400 Subject: [PATCH 34/43] fixed some group routes, and fixed the post route of receipts --- api/group.js | 67 ++++++++++++---- api/receipts.js | 179 ++++++++++++++++++++++++++++--------------- database/groups.js | 2 +- database/item.js | 2 +- database/receipts.js | 13 ++++ database/user.js | 8 +- 6 files changed, 194 insertions(+), 77 deletions(-) diff --git a/api/group.js b/api/group.js index 1bf6656..3b5773f 100644 --- a/api/group.js +++ b/api/group.js @@ -1,17 +1,17 @@ const express = require("express"); const router = express.Router(); const { authenticateJWT } = require("../auth"); -const { User, Group, Invite } = require("../database"); +const { User, Group, Invite, UserGroups } = require("../database"); // create a group -router.post("/create", async (req, res) => { - const userId =1; +router.post("/create", authenticateJWT, async (req, res) => { + const userId = req.user?.id; if (!userId) { - return res.status(404).json({ error: "Unauthorized" }); + return res.status(401).json({ error: "Unauthorized" }); } - const { description, groupName, Receipt_Id } = req.body; + const { description, groupName } = req.body; console.log("Request body:", req.body); try { @@ -24,10 +24,11 @@ router.post("/create", async (req, res) => { Owner: userId, description, groupName, - Receipt_Id, }); - res.status(201).json({message: "Group created successfully", group: newGroup}); + res + .status(201) + .json({ message: "Group created successfully", group: newGroup }); } catch (err) { console.error(err); res.status(500).json({ error: "Failed to create group" }); @@ -35,16 +36,16 @@ router.post("/create", async (req, res) => { }); //delete a group (only owner is supposed to this) -router.delete("/delete/:id", authenticateJWT, async (req, res) => { +router.delete("/delete/:id", authenticateJWT, async (req, res) => { try { const userId = req.user?.id; //logged in user if (!userId) { - return res.status(404).json({ error: "Unauthorized" }); + return res.status(401).json({ error: "Unauthorized" }); } const groupId = Number(req.params.id); - if(!groupId){ + if (!groupId) { return res.status(400).json({ error: "Group ID is required" }); } @@ -68,11 +69,12 @@ router.delete("/delete/:id", authenticateJWT, async (req, res) => { }); //Create an Invite -router.post("/invite", authenticateJWT, async (req, res) => { +router.post("/invite", authenticateJWT, async (req, res) => { try { - const userId = req.user?.id; //logged in user + const userId = req.user?.id; // logged in user + if (!userId) { - return res.status(404).json({ error: "Unauthorized" }); + return res.status(401).json({ error: "Unauthorized" }); } const { receiverId, GroupId } = req.body; @@ -82,9 +84,46 @@ router.post("/invite", authenticateJWT, async (req, res) => { return res.status(404).json({ error: "Group not found" }); } - + const receiver = await User.findByPk(receiverId); + if (!receiver) { + return res.status(404).json({ error: "Receiver user not found" }); + } + + if (receiverId === userId) { + return res.status(400).json({ error: "Cannot invite yourself" }); + } + + const user = await User.findByPk(userId); + if (!user) { + return res.status(404).json({ error: "Sender user not found" }); + } + + // Check if the sender/user is part of the group. + // This line fetches all groups the user belongs to, but filters only for the current GroupId. + // If the returned array is empty, it means the user is not a member of that group. + const groups = await user.getGroups({ where: { id: GroupId } }); + if (groups.length === 0) { + return res + .status(403) + .json({ error: "You must be part of the group to send invites" }); + } + + //Check if there is already a pending invite from the same sender to the same receiver for the same group. + const existingInvite = await Invite.findOne({ + where: { + senderId: userId, + receiverId, + GroupId, + status: "pending", + }, + }); + + if (existingInvite) { + return res.status(400).json({ error: "Invite already sent and pending" }); + } const invite = await Invite.create({ + senderId: userId, receiverId, GroupId, status: "pending", diff --git a/api/receipts.js b/api/receipts.js index c5dece7..16c4713 100644 --- a/api/receipts.js +++ b/api/receipts.js @@ -1,49 +1,47 @@ const express = require("express"); const router = express.Router(); -const { Receipts, Item } = require("../database"); +const { Receipts, Item, Group } = require("../database"); // GET all receipts router.get("/", async (req, res) => { - try { - const receipts = await Receipts.findAll(); - res.status(200).send(receipts); - } catch (error) { - console.error("Error fetching receipts:", error); - res.status(500).send("Failed to fetch receipts."); - } + try { + const receipts = await Receipts.findAll(); + res.status(200).send(receipts); + } catch (error) { + console.error("Error fetching receipts:", error); + res.status(500).send("Failed to fetch receipts."); + } }); // GET a specific receipt by id router.get("/:id", async (req, res) => { - try { - const receiptId = Number(req.params.id); - const receipt = await Receipts.findByPk(receiptId); - if (!receipt) { - res.status(400).send("Receipt not found"); - } - res.status(200).send(receipt); - } catch (error) { - console.error("Error fetching receipt by ID:", error); - res.status(500).send( - "Failed to find the receipt you were looking for.", - ); + try { + const receiptId = Number(req.params.id); + const receipt = await Receipts.findByPk(receiptId); + if (!receipt) { + res.status(400).send("Receipt not found"); } + res.status(200).send(receipt); + } catch (error) { + console.error("Error fetching receipt by ID:", error); + res.status(500).send("Failed to find the receipt you were looking for."); + } }); // GET all items associated with a receipt router.get("/:id/items", async (req, res) => { - try { - const receiptId = Number(req.params.id); - const receipt = await Receipts.findByPk(receiptId); - if (!receipt) { - res.status(400).send("Receipt not found"); - } - const items = await receipt.getItems(); - res.status(200).send(items); - } catch (error) { - console.error("Error fetching items by receipt ID:", error); - res.status(500).send("Failed to find the items you were looking for."); + try { + const receiptId = Number(req.params.id); + const receipt = await Receipts.findByPk(receiptId); + if (!receipt) { + res.status(400).send("Receipt not found"); } + const items = await receipt.getItems(); + res.status(200).send(items); + } catch (error) { + console.error("Error fetching items by receipt ID:", error); + res.status(500).send("Failed to find the items you were looking for."); + } }); /* POST a receipt and its items, assuming that the request includes: @@ -59,39 +57,100 @@ router.get("/:id/items", async (req, res) => { {name: string, price: float}, ... ] */ -router.post("/", async (req, res) => { - try { - const { receipt, items } = req.body; - const newReceipt = await Receipts.create(receipt); // create the new receipt - const newReceiptId = newReceipt.id; // get the id of the created receipt - - // create the new items and associate them with this receipt - for (let i = 0; i < items.length; i++) { - const item = items[i]; - item.Receipt_id = newReceiptId; - await newReceipt.createItem(item); - } - - res.status(200).send(receipt); - } catch (error) { - console.error("Error posting receipt:", error); - res.status(500).send("Failed to post receipt."); + +//update this where it belongs to user, group โ†“ + +// router.post("/", async (req, res) => { +// try { +// const { receipt, items } = req.body; +// const newReceipt = await Receipts.create(receipt); // create the new receipt +// const newReceiptId = newReceipt.id; // get the id of the created receipt + +// // create the new items and associate them with this receipt +// for (let i = 0; i < items.length; i++) { +// const item = items[i]; +// item.Receipt_id = newReceiptId; +// await newReceipt.createItem(item); +// } + +// res.status(200).send(receipt); +// } catch (error) { +// console.error("Error posting receipt:", error); +// res.status(500).send("Failed to post receipt."); +// } +// }); + +// DELETE a receipt +router.delete("/:id", authenticateJWT, async (req, res) => { + try { + const userId = req.user?.id; + const receiptId = Number(req.params.id); + const receipt = await Receipts.findByPk(receiptId); + + if (!receipt) { + return res.status(404).send("Receipt not found"); + } + + if (receipt.UploadedBy !== userId) { + return res.status(403).send("Not authorized to delete this receipt"); } + + await receipt.destroy(); + res.status(200).send("Receipt deleted"); + } catch (error) { + console.error("Error deleting receipt:", error); + res.status(500).send("Failed to delete receipt."); + } }); -// DELETE a receipt -router.delete("/:id", async (req, res) => { - try { - const receiptId = Number(req.params.id); - const receipt = await Receipts.findByPk(receiptId); - if (!receipt) { - res.status(400).send("Receipt not found"); - } - await receipt.destroy(); - } catch (error) { - console.error("Error deleting receipt:", error); - res.status(500).send("Failed to delete receipt."); +//(HERE NEW POST) +//upload a receipt +router.post("/:id/Upload", authenticateJWT, async (req, res) => { + try { + const userId = req.user?.id; // logged in user + if (!userId) { + return res.status(401).json({ error: "Unauthorized" }); } + + const groupId = Number(req.params.id); + if (!groupId) { + return res.status(400).json({ error: "Group ID is required" }); + } + + const group = await Group.findByPk(groupId); + if (!group) { + return res.status(404).json({ error: "Group does not exist" }); + } + + const { receipt, items } = req.body; + + if (!receipt || !items || !Array.isArray(items) || items.length === 0) { + return res.status(400).json({ error: "Receipt and items are required" }); + } + + // Create the receipt + const newReceipt = await Receipts.create({ + ...receipt, + GroupId: groupId, + uploaded_by: userId, + }); + + const receiptItems = items.map((item) => ({ + ...item, + ReceiptId: newReceipt.id, + })); + + await Item.bulkCreate(receiptItems); + + res.status(201).json({ + message: "Receipt uploaded successfully", + receipt: newReceipt, + items: receiptItems, + }); + } catch (err) { + console.error(err); + res.status(500).json({ error: "Failed to upload receipt to group" }); + } }); module.exports = router; diff --git a/database/groups.js b/database/groups.js index 0d41486..bd07b89 100644 --- a/database/groups.js +++ b/database/groups.js @@ -6,7 +6,7 @@ const Group = db.define("groups", { type: DataTypes.INTEGER, allowNull: true, references: { - model: 'User', + model: 'users', key: 'id', }, }, diff --git a/database/item.js b/database/item.js index 0f6feb7..3f506ea 100644 --- a/database/item.js +++ b/database/item.js @@ -1,7 +1,7 @@ const { DataTypes } = require("sequelize"); const db = require("./db"); -const Item = db.define("item", { +const Item = db.define("items", { name: { type: DataTypes.STRING, allowNull: false, diff --git a/database/receipts.js b/database/receipts.js index f3b711b..491f529 100644 --- a/database/receipts.js +++ b/database/receipts.js @@ -6,18 +6,31 @@ const Receipts = db.define("receipts", { type: DataTypes.STRING, allowNull: false, }, + body: { type: DataTypes.TEXT, allowNull: false, }, + User_Id: { type: DataTypes.INTEGER, allowNull: false, }, + Group_Id: { type: DataTypes.INTEGER, allowNull: true, }, + + // Defines foreign key constraint linking this column to the primary key + uploaded_by: { + type: DataTypes.INTEGER, + allowNull: true, + references: { + model: "users", + key: "id", + }, + }, }); module.exports = Receipts; diff --git a/database/user.js b/database/user.js index d8ff9cc..dc21d02 100644 --- a/database/user.js +++ b/database/user.js @@ -2,7 +2,7 @@ const { DataTypes } = require("sequelize"); const db = require("./db"); const bcrypt = require("bcrypt"); -const User = db.define("user", { +const User = db.define("users", { firstName: { type: DataTypes.STRING, allowNull: false, @@ -10,6 +10,7 @@ const User = db.define("user", { len: [1, 30], } }, + lastName: { type: DataTypes.STRING, allowNull: false, @@ -17,6 +18,7 @@ const User = db.define("user", { len: [1, 30], } }, + username: { type: DataTypes.STRING, allowNull: false, @@ -25,6 +27,7 @@ const User = db.define("user", { len: [3, 20], }, }, + email: { type: DataTypes.STRING, allowNull: false, @@ -33,6 +36,7 @@ const User = db.define("user", { isEmail: true, }, }, + profilePic: { type: DataTypes.STRING, allowNull: true, @@ -41,11 +45,13 @@ const User = db.define("user", { isUrl: true, }, }, + auth0Id: { type: DataTypes.STRING, allowNull: true, unique: true, }, + passwordHash: { type: DataTypes.STRING, allowNull: true, From d4f952022f2f9ce3138e937d43853216453c5afb Mon Sep 17 00:00:00 2001 From: cranberry Date: Wed, 6 Aug 2025 12:03:46 -0400 Subject: [PATCH 35/43] just made it workarounf older post to make it work without group for now --- api/group.js | 2 +- api/receipts.js | 50 +++++++++++++++++++++++++++++-------------------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/api/group.js b/api/group.js index 3b5773f..2cd5fa5 100644 --- a/api/group.js +++ b/api/group.js @@ -194,4 +194,4 @@ router.post("/invite/:id/decline", authenticateJWT, async (req, res) => { } }); -module.exports = router; +module.exports = router; \ No newline at end of file diff --git a/api/receipts.js b/api/receipts.js index 16c4713..d63b223 100644 --- a/api/receipts.js +++ b/api/receipts.js @@ -1,5 +1,6 @@ const express = require("express"); const router = express.Router(); +const { authenticateJWT } = require("../auth"); const { Receipts, Item, Group } = require("../database"); // GET all receipts @@ -60,25 +61,34 @@ router.get("/:id/items", async (req, res) => { //update this where it belongs to user, group โ†“ -// router.post("/", async (req, res) => { -// try { -// const { receipt, items } = req.body; -// const newReceipt = await Receipts.create(receipt); // create the new receipt -// const newReceiptId = newReceipt.id; // get the id of the created receipt - -// // create the new items and associate them with this receipt -// for (let i = 0; i < items.length; i++) { -// const item = items[i]; -// item.Receipt_id = newReceiptId; -// await newReceipt.createItem(item); -// } - -// res.status(200).send(receipt); -// } catch (error) { -// console.error("Error posting receipt:", error); -// res.status(500).send("Failed to post receipt."); -// } -// }); +router.post("/", authenticateJWT, async (req, res) => { + try { + const { receipt, items } = req.body; + + + const userId = req.user ? req.user.id : null; + + + const newReceipt = await Receipts.create({ + ...receipt, + User_Id: userId, + }); + + const newReceiptId = newReceipt.id; + + for (let i = 0; i < items.length; i++) { + const item = items[i]; + item.Receipt_id = newReceiptId; + await newReceipt.createItem(item); + } + + res.status(200).send(newReceipt); + } catch (error) { + console.error("Error posting receipt:", error); + res.status(500).send("Failed to post receipt."); + } +}); + // DELETE a receipt router.delete("/:id", authenticateJWT, async (req, res) => { @@ -153,4 +163,4 @@ router.post("/:id/Upload", authenticateJWT, async (req, res) => { } }); -module.exports = router; +module.exports = router; \ No newline at end of file From d4d152fa2d0bdff537aaeebb8b493745607a3396 Mon Sep 17 00:00:00 2001 From: cranberry Date: Wed, 6 Aug 2025 16:01:26 -0400 Subject: [PATCH 36/43] added category --- api/receipts.js | 14 ++++++++------ database/receipts.js | 6 ++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/api/receipts.js b/api/receipts.js index d63b223..3899565 100644 --- a/api/receipts.js +++ b/api/receipts.js @@ -61,17 +61,18 @@ router.get("/:id/items", async (req, res) => { //update this where it belongs to user, group โ†“ -router.post("/", authenticateJWT, async (req, res) => { +router.post("/", authenticateJWT, async (req, res) => { try { const { receipt, items } = req.body; - - const userId = req.user ? req.user.id : null; - + const userId = req.user ? req.user.id : null; + + const newReceipt = await Receipts.create({ ...receipt, - User_Id: userId, + User_Id: userId, + uploaded_by: userId, }); const newReceiptId = newReceipt.id; @@ -132,7 +133,7 @@ router.post("/:id/Upload", authenticateJWT, async (req, res) => { return res.status(404).json({ error: "Group does not exist" }); } - const { receipt, items } = req.body; + const { receipt, items, Category } = req.body; if (!receipt || !items || !Array.isArray(items) || items.length === 0) { return res.status(400).json({ error: "Receipt and items are required" }); @@ -143,6 +144,7 @@ router.post("/:id/Upload", authenticateJWT, async (req, res) => { ...receipt, GroupId: groupId, uploaded_by: userId, + category: Category, }); const receiptItems = items.map((item) => ({ diff --git a/database/receipts.js b/database/receipts.js index 491f529..52c1a5a 100644 --- a/database/receipts.js +++ b/database/receipts.js @@ -31,6 +31,12 @@ const Receipts = db.define("receipts", { key: "id", }, }, + + category: { + type: DataTypes.STRING, + allowNull: true, + }, + }); module.exports = Receipts; From 69ebb02b0d139ee638e558c5895f8c05a250158d Mon Sep 17 00:00:00 2001 From: Muhammad Date: Thu, 7 Aug 2025 00:54:20 -0400 Subject: [PATCH 37/43] halfway done with route to get all the groups and display them --- api/group.js | 14 +++++++++++++- api/users.js | 8 ++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/api/group.js b/api/group.js index d465dbc..df508db 100644 --- a/api/group.js +++ b/api/group.js @@ -5,6 +5,16 @@ const { User, Group, Invite } = require("../database"); //add authenticateJWT later +router.get("./get", async (req, res) => { + try { + const displayGroups = await Group.findAll(); + console.log(displayGroups) + res.status(200).send(displayGroups); + } catch (err) { + res.send("can not display all the groups").send(400); + } +}); + // create a group router.post("/create", async (req, res) => { const userId = req.user?.id; @@ -29,7 +39,9 @@ router.post("/create", async (req, res) => { Receipt_Id, }); - res.status(201).json({message: "Group created successfully", group: newGroup}); + res + .status(201) + .json({ message: "Group created successfully", group: newGroup }); } catch (err) { console.error(err); res.status(500).json({ error: "Failed to create group" }); diff --git a/api/users.js b/api/users.js index a24dabb..a3f41a5 100644 --- a/api/users.js +++ b/api/users.js @@ -16,15 +16,15 @@ router.get("/Allusers", async (req, res) => { } }); -router.get("/me", async (req, res) => { +router.get("/me", authenticateJWT, async (req, res) => { try { - const userId = 1; + const userId = req.user.id; const userInfo = await User.findByPk(userId); if (!userInfo) { return res.status(404).json({ error: "user not found" }); } res.send({ - message: "SOMTHING", + message: "Display user specific info successful", userInfo: { username: userInfo.username, profilePic: userInfo.profilePic, @@ -34,7 +34,7 @@ router.get("/me", async (req, res) => { }, }); } catch (err) { - console.error("this is not working", err); + console.error("User info not fetched", err); res.status(500).json({ message: "internal server error" }); } }); From 26c2aac652c355e4225429bc2c26fab34d94c0f3 Mon Sep 17 00:00:00 2001 From: MS Date: Thu, 7 Aug 2025 11:38:17 -0400 Subject: [PATCH 38/43] did the route for get all the groups --- api/group.js | 18 ++++++++++++- database/seed.js | 69 +++++++++++++++++++++++++++++++++--------------- 2 files changed, 64 insertions(+), 23 deletions(-) diff --git a/api/group.js b/api/group.js index 2cd5fa5..d72343b 100644 --- a/api/group.js +++ b/api/group.js @@ -3,6 +3,22 @@ const router = express.Router(); const { authenticateJWT } = require("../auth"); const { User, Group, Invite, UserGroups } = require("../database"); +router.get("/myGroups", async (req, res) => { + try { + const userId = 1; + //req.user?.id; + const user = await User.findByPk(userId); + if (!user) { + return res.status(404).json({ error: "User not found" }); + } + const getGroups = await user.getGroups(); + res.status(200).send(getGroups); + } catch (err) { + console.error(err); + res.status(500).json({ error: "Could not fetch groups" }); + } +}); + // create a group router.post("/create", authenticateJWT, async (req, res) => { const userId = req.user?.id; @@ -194,4 +210,4 @@ router.post("/invite/:id/decline", authenticateJWT, async (req, res) => { } }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/database/seed.js b/database/seed.js index 5272fb1..5217527 100644 --- a/database/seed.js +++ b/database/seed.js @@ -1,41 +1,75 @@ const db = require("./db"); const { User, Item, Receipts, Group } = require("./index"); - const seed = async () => { try { db.logging = false; await db.sync({ force: true }); // Drop and recreate tables const users = await User.bulkCreate([ - { username: "admin", passwordHash: User.hashPassword("admin123"), firstName: "Alice", lastName: "Admin", email: "admin@example.com", profilePic: "https://i.pravatar.cc/150?img=1" }, - { username: "user1", passwordHash: User.hashPassword("user111"), firstName: "Bob", lastName: "Builder", email: "bob@example.com", profilePic: "https://i.pravatar.cc/150?img=1" }, - { username: "user2", passwordHash: User.hashPassword("user222"), firstName: "Carol", lastName: "Coder", email: "carol@example.com", profilePic: "https://i.pravatar.cc/150?img=1" }, - { username: "user3", passwordHash: User.hashPassword("user333"), firstName: "Mike", lastName: "Snake", email: "Snake@example.com", profilePic: "https://i.pravatar.cc/150?img=1" }, + { + username: "admin", + passwordHash: User.hashPassword("admin123"), + firstName: "Alice", + lastName: "Admin", + email: "admin@example.com", + profilePic: "https://i.pravatar.cc/150?img=1", + }, + { + username: "user1", + passwordHash: User.hashPassword("user111"), + firstName: "Bob", + lastName: "Builder", + email: "bob@example.com", + profilePic: "https://i.pravatar.cc/150?img=1", + }, + { + username: "user2", + passwordHash: User.hashPassword("user222"), + firstName: "Carol", + lastName: "Coder", + email: "carol@example.com", + profilePic: "https://i.pravatar.cc/150?img=1", + }, + { + username: "user3", + passwordHash: User.hashPassword("user333"), + firstName: "Mike", + lastName: "Snake", + email: "Snake@example.com", + profilePic: "https://i.pravatar.cc/150?img=1", + }, ]); console.log(`๐Ÿ‘ค Created ${users.length} users`); - // Create more seed data here once you've created your models - // Seed files are a great way to test your database schema! - - const groups = await Group.bulkCreate([ - { owner: users[0].id, groupName: "Family", Receipt_Id: null }, - { owner: users[1].id, groupName: "Friends", Receipt_Id: null }, + const groups = await Group.bulkCreate([ + { + Owner: users[0].id, + groupName: "Family", + description: "Family group for shared expenses", + }, + { + Owner: users[1].id, + groupName: "Friends", + description: "Friends group for outings", + }, ]); + console.log(`๐Ÿ‘ฅ Created ${groups.length} groups`); + const receipts = await Receipts.bulkCreate([ { title: "Grocery Shopping", body: "Bought fruits and vegetables", User_Id: users[0].id, - Group_Id: null + Group_Id: groups[0].id, }, { title: "Electronics Purchase", body: "Bought a new laptop", User_Id: users[1].id, - Group_Id: groups[0].id + Group_Id: groups[1].id, }, ]); @@ -49,15 +83,6 @@ const seed = async () => { console.log(`๐Ÿ“ฆ Created ${items.length} items`); - // const groups = await Group.bulkCreate([ - // { owner: users[0].id, groupName: "Family", Receipt_Id: receipts[0].id }, - // { owner: users[1].id, groupName: "Friends", Receipt_Id: receipts[1].id }, - // ]); - - console.log(`๐Ÿ‘ฅ Created ${groups.length} groups`); - - receipts[1].Group_Id = groups[0].id; - console.log("๐ŸŒฑ Seeded the database"); } catch (error) { console.error("Error seeding database:", error); From dca7be8696853e56c84b329a0088f8aabb51d4e8 Mon Sep 17 00:00:00 2001 From: MS Date: Thu, 7 Aug 2025 11:42:55 -0400 Subject: [PATCH 39/43] did the api for get groups and fixed seed --- database/groups.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/database/groups.js b/database/groups.js index bd07b89..70d3d79 100644 --- a/database/groups.js +++ b/database/groups.js @@ -6,8 +6,8 @@ const Group = db.define("groups", { type: DataTypes.INTEGER, allowNull: true, references: { - model: 'users', - key: 'id', + model: "users", + key: "id", }, }, From 59f421781bc39e8c63e3ddc2b6e493625c517239 Mon Sep 17 00:00:00 2001 From: Muhammad Date: Fri, 8 Aug 2025 14:40:47 -0400 Subject: [PATCH 40/43] did the get recpit based of user --- api/users.js | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/api/users.js b/api/users.js index d88f53e..e80b6f8 100644 --- a/api/users.js +++ b/api/users.js @@ -5,24 +5,23 @@ const { User, Group, Receipts } = require("../database"); //const { adminAuthenticate, authenticateJWT } = require("../auth"); const { Op } = require("sequelize"); // Used for case-insenitive matching - // get all users router.get("/Allusers", authenticateJWT, async (req, res) => { try { const { page = 1, limit = 10, search = "" } = req.query; const offset = (page - 1) * limit; - // Build search filter + // Build search filter const whereClause = search ? { - [Op.or]: [ - { username: { [Op.iLike]: `%${search}%` } }, - /*{ email: { [Op.iLike]: `%${search}%`} },*/ - ], - } + [Op.or]: [ + { username: { [Op.iLike]: `%${search}%` } }, + /*{ email: { [Op.iLike]: `%${search}%`} },*/ + ], + } : {}; - // This fetches the users and total count + // This fetches the users and total count const { count, rows: users } = await User.findAndCountAll({ where: whereClause, limit: parseInt(limit), @@ -35,7 +34,7 @@ router.get("/Allusers", authenticateJWT, async (req, res) => { currentPage: parseInt(page), totalPages: Math.ceil(count / limit), users, - }) + }); } catch (err) { console.error("error finding all users", err); res.status(500).json({ message: "internal server error" }); @@ -82,7 +81,7 @@ router.get("/search", async (req, res) => { const users = await User.findAll({ where: { username: { - [Op.iLike]: `%${query}%`, // Matches input anywhere, it is case-insensitive + [Op.iLike]: `%${query}%`, // Matches input anywhere, it is case-insensitive }, }, attributes: ["id", "username"], // Returns specific user @@ -95,5 +94,19 @@ router.get("/search", async (req, res) => { res.status(500).json({ message: "Internal server error" }); } }); +//get recpit based of user +router.get("/:id", async (req, res) => { + try { + const urlId = Number(req.params.id); + const user = await User.findByPk(urlId); + const userReceipts = await Receipts.findAll({ + where: { User_Id: user.id }, + }); + res.status(200).send(userReceipts); + } catch (err) { + console.error("cant not recive and a recpit baseed of id", err); + res.status(500).json({ error: "can not get repictpt based of of user" }); + } +}); module.exports = router; From 09a2325aa8ef3d030fce2fb8a9964196dbf5ec88 Mon Sep 17 00:00:00 2001 From: Muhammad Date: Sat, 9 Aug 2025 19:01:25 -0400 Subject: [PATCH 41/43] did the get repcipt based of group route --- api/group.js | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/api/group.js b/api/group.js index dcfb344..86d3b8e 100644 --- a/api/group.js +++ b/api/group.js @@ -1,7 +1,7 @@ const express = require("express"); const router = express.Router(); const { authenticateJWT } = require("../auth"); -const { User, Group, Invite, UserGroups } = require("../database"); +const { User, Group, Invite, UserGroups, Receipts } = require("../database"); router.patch("/:editGroup", async (req, res) => { try { @@ -240,4 +240,25 @@ router.post("/invite/:id/decline", authenticateJWT, async (req, res) => { } }); +//get a repcipt based of group +router.get("/:id", async (req, res) => { + try { + const getUrl = Number(req.params.id); + const group = await Group.findByPk(getUrl); + if (!Group) { + res.sendStatus(500); + } + if (!Receipts) { + res.sendStatus(500); + } + const groupReceipts = await Receipts.findAll({ + where: { Group_Id: group.id }, + }); + res.status(200).send(groupReceipts); + } catch (err) { + console.error("can not get groups based of repcipts"); + res.status(400).json({ error: "not able to find the group" }); + } +}); + module.exports = router; From 5b218eb7ce4be6955d2cae5b375528b629c20cab Mon Sep 17 00:00:00 2001 From: Muhammad Date: Sun, 10 Aug 2025 20:34:07 -0400 Subject: [PATCH 42/43] fix the error code to be more exact --- api/group.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/group.js b/api/group.js index 86d3b8e..539f4bc 100644 --- a/api/group.js +++ b/api/group.js @@ -246,10 +246,10 @@ router.get("/:id", async (req, res) => { const getUrl = Number(req.params.id); const group = await Group.findByPk(getUrl); if (!Group) { - res.sendStatus(500); + res.sendStatus(404); } if (!Receipts) { - res.sendStatus(500); + res.sendStatus(404); } const groupReceipts = await Receipts.findAll({ where: { Group_Id: group.id }, From 2e29e8833869f7e43d5e0f3ec709abff472844f4 Mon Sep 17 00:00:00 2001 From: Muhammad Date: Tue, 12 Aug 2025 20:17:58 -0400 Subject: [PATCH 43/43] did the patch api to update user info --- api/users.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/api/users.js b/api/users.js index e80b6f8..a831a34 100644 --- a/api/users.js +++ b/api/users.js @@ -68,6 +68,24 @@ router.get("/me", authenticateJWT, async (req, res) => { res.status(500).json({ message: "internal server error" }); } }); +//to update user info. +router.patch("/:editUser", async (req, res) => { + try { + const id = Number(req.params.editUser); + const userPatch = await User.findByPk(id); + await userPatch.update({ + firstName: req.body.firstName, + lastName: req.body.lastName, + email: req.body.email, + profilePic: req.body.profilePic, + }); + await userPatch.save(); + res.sendStatus(200); + } catch (err) { + console.error("can not update user", err); + res.sendStatus(400); + } +}); // Search Route (autocomplete) router.get("/search", async (req, res) => {