Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
337894f
Edited vercel config.
rcjasub Jul 30, 2025
06b6838
Revert vercel config
rcjasub Jul 30, 2025
6ecb071
did the group table
Jul 30, 2025
3c53f87
seed.js
rcjasub Jul 30, 2025
d49b082
Seed.js not fully done
rcjasub Jul 30, 2025
1bcd237
add socket functionality, fix cookies
fterdal Jul 31, 2025
b88556b
clean up
fterdal Jul 31, 2025
c06c5f7
working seed
rcjasub Jul 31, 2025
5932224
new main
rcjasub Jul 31, 2025
d43a18f
merge with main
rcjasub Jul 31, 2025
95e305b
sign up not done
WilliamBoop Jul 31, 2025
6fbe00c
merge
WilliamBoop Jul 31, 2025
2f2ce1a
not fully working ocr
rcjasub Jul 31, 2025
f6bcf32
update
rcjasub Jul 31, 2025
86a9c8e
verify added
rcjasub Aug 1, 2025
8edad29
W.I.P
ty239 Aug 1, 2025
3e861d9
Updated user model with first name, last name, profile pic, and email…
WilliamBoop Aug 1, 2025
e974545
did the get route to get all users
ty239 Aug 1, 2025
0c28947
merge with moe
rcjasub Aug 1, 2025
d5b1c13
update
rcjasub Aug 1, 2025
c9d833c
create group route
rcjasub Aug 1, 2025
9716d06
did the delete route
ty239 Aug 1, 2025
ca04042
did the get all items
ty239 Aug 1, 2025
989ba82
changed some group related stuff, added a group file
rcjasub Aug 1, 2025
bb5185f
fixed merge
rcjasub Aug 1, 2025
cd39a68
quick fix
rcjasub Aug 1, 2025
8ba48a6
Added routes to get all receipts, get a single receipt, and post a re…
bryan-zhong46 Aug 1, 2025
fc734d7
Fixed some documentation.
bryan-zhong46 Aug 1, 2025
2ac830a
added the route to get User info
ty239 Aug 1, 2025
88bb5b2
test
rcjasub Aug 1, 2025
3ec6b2c
Fixed variable name.
bryan-zhong46 Aug 1, 2025
dc0c8d3
fix spelling mistakes
ty239 Aug 1, 2025
9b57bb3
Finish create group and add member, small change on delete
rcjasub Aug 1, 2025
120d99f
Merge remote-tracking branch 'origin/receipts-routes' into jocsan
rcjasub Aug 1, 2025
93e9646
fixed merge
rcjasub Aug 1, 2025
7914ec4
merge, userInfo
rcjasub Aug 1, 2025
a962aae
user can now make a invite, accept/decline
rcjasub Aug 3, 2025
ddf4198
updated delete group, added a new line in create
rcjasub Aug 3, 2025
8379ab6
Merge pull request #2 from cap-5/invite-dt
rcjasub Aug 3, 2025
d5092db
Merge pull request #4 from cap-5/jocsan
ty239 Aug 3, 2025
2b421cc
Added a route to get all items by receipt ID.
bryan-zhong46 Aug 4, 2025
4be8a78
Added route to delete a receipt.
bryan-zhong46 Aug 4, 2025
db8a73f
Updated and created routes for searching feature, includes autocomplete
WilliamBoop Aug 5, 2025
36663da
not fully done with groups
rcjasub Aug 5, 2025
d1b0a8e
merge with william
rcjasub Aug 5, 2025
51f81ab
fixed some group routes, and fixed the post route of receipts
rcjasub Aug 6, 2025
ac2da5d
Merge remote-tracking branch 'origin/jocsan' into moe
Aug 6, 2025
c91bf3a
Merge remote-tracking branch 'origin/jocsan' into moe
Aug 6, 2025
d4f9520
just made it workarounf older post to make it work without group for now
rcjasub Aug 6, 2025
d4d152f
added category
rcjasub Aug 6, 2025
69ebb02
halfway done with route to get all the groups and display them
ty239 Aug 7, 2025
e5f8399
Merge remote-tracking branch 'origin/jocsan' into moe
Aug 7, 2025
26c2aac
did the route for get all the groups
Aug 7, 2025
dca7be8
did the api for get groups and fixed seed
Aug 7, 2025
205db06
did the patch route to group
ty239 Aug 8, 2025
59f4217
did the get recpit based of user
ty239 Aug 8, 2025
09a2325
did the get repcipt based of group route
ty239 Aug 9, 2025
5b218eb
fix the error code to be more exact
ty239 Aug 11, 2025
2e29e88
did the patch api to update user info
ty239 Aug 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .env.example

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules
.env
.vercel
.replit
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Capstone I Backend
# Capstone II Backend

## Getting Started

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

Expand Down
264 changes: 264 additions & 0 deletions api/group.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
const express = require("express");
const router = express.Router();
const { authenticateJWT } = require("../auth");
const { User, Group, Invite, UserGroups, Receipts } = require("../database");

router.patch("/:editGroup", async (req, res) => {
try {
const id = Number(req.params.editGroup);
const groupTOPatch = await Group.findByPk(id);
// if (!Group) {
// res.status(500).json({ error: "groups does not exists" });
// }
await groupTOPatch.update({
groupName: req.body.groupName,
description: req.body.description,
});

await groupTOPatch.save();
res.sendStatus(200);
} catch (err) {
console.error("can not update groups");
res.status(400).json({ error: "not able to work" });
}
});

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" });
}
});

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", authenticateJWT, async (req, res) => {
const userId = req.user?.id;

if (!userId) {
return res.status(401).json({ error: "Unauthorized" });
}

const { description, groupName } = req.body;
console.log("Request body:", 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,
description,
groupName,
});

res
.status(201)
.json({ message: "Group created successfully", group: newGroup });
} catch (err) {
console.error(err);
res.status(500).json({ error: "Failed to create group" });
}
});

//delete a group (only owner is supposed to this)
router.delete("/delete/:id", 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" });
}

//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.status(200).json({ message: "Group deleted successfully" });
} catch (err) {
console.error(err);
res.status(500).json({ error: "Could not delete group" });
}
});

//Create an Invite
router.post("/invite", authenticateJWT, async (req, res) => {
try {
const userId = req.user?.id; // logged in user

if (!userId) {
return res.status(401).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 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",
});

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", authenticateJWT, 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", authenticateJWT, 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 (!user || !group) {
return res.status(404).json({ error: "User or group not found" });
}

//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 decline invite" });
}
});

//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(404);
}
if (!Receipts) {
res.sendStatus(404);
}
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;
11 changes: 9 additions & 2 deletions api/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
const express = require("express");
const router = express.Router();
const testDbRouter = require("./test-db");
const users = require("./users");
const group = require("./group");
const receiptsRouter = require("./receipts");

router.use("/test-db", testDbRouter);
//DB API
router.use("/users", users);
router.use("/group", group);
router.use("/receipts", receiptsRouter);

module.exports = router;


Loading