Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ REDIS_AUTH = Redis password you get while provisioning a Redis DB
REDIS_URL = Redis DB connection string
REDIS_PORT = Any port you want.
REDIS_USERNAME = Username of the user that has access to the Redis DB
INSTANCE = ec2 instance id
INSTANCE = ec2 instance id
62 changes: 31 additions & 31 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -1,45 +1,45 @@
name: Lint

on:
push:
branches: [master]
pull_request:
branches: [master]
push:
branches: [master]
pull_request:
branches: [master]

jobs:
build:
runs-on: ubuntu-latest
build:
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [18.x]
strategy:
matrix:
node-version: [18.x]

steps:
- name: Checkout repository
uses: actions/checkout@v2
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
cache: "npm"
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: npm

- name: Clean npm cache and remove node_modules
run: |
rm -rf node_modules package-lock.json
npm cache clean --force
- name: Clean npm cache and remove node_modules
run: |
rm -rf node_modules package-lock.json
npm cache clean --force

- name: Install dependencies
run: npm install
- name: Install dependencies
run: npm install

- name: Verify package-lock.json
run: git diff --exit-code package-lock.json || echo "⚠️ package-lock.json changed, please update it."
- name: Verify package-lock.json
run: git diff --exit-code package-lock.json || echo "⚠️ package-lock.json changed, please update it."

- name: Run npm ci
run: npm ci
- name: Run npm ci
run: npm ci

- name: Run Prettier
run: npx prettier -c "**/*.js"
- name: Run Prettier
run: npx prettier -c "**/*.js"

- name: Run ESLint
run: npx eslint .
- name: Run ESLint
run: npx eslint .
128 changes: 89 additions & 39 deletions graphql/resolvers.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const resolvers = {
.populate("leader followers")
.populate({
path: "landmarks",
populate: { path: "createdBy", select: "name email" },
populate: { path: "createdBy", select: "name email imageUrl" },
});
if (!beacon) return new UserInputError("No beacon exists with that id.");
// return error iff user not in beacon
Expand Down Expand Up @@ -181,11 +181,11 @@ const resolvers = {
},

oAuth: async (_parent, { userInput }) => {
const { name, email } = userInput;
const { name, email, imageUrl } = userInput;
let user = await User.findOne({ email });

if (!user) {
const newUser = new User({ name, email, isVerified: true });
const newUser = new User({ name, email, isVerified: true, imageUrl });
user = await newUser.save();
}

Expand Down Expand Up @@ -265,9 +265,8 @@ const resolvers = {

return verificationCode;
},
completeVerification: async (_, { userId }, { user }) => {
completeVerification: async (_, { userId }) => {
let currentUser = await User.findById(userId);
console.log("Current user: ", currentUser, user);
currentUser.isVerified = true;
await currentUser.save();
return currentUser;
Expand Down Expand Up @@ -531,32 +530,39 @@ const resolvers = {
},

createLandmark: async (_, { landmark, beaconID }, { user, pubsub }) => {
const beacon = await Beacon.findById(beaconID);
// to save on a db call to populate leader, we just use the stored id to compare
try {
const beacon = await Beacon.findById(beaconID);
// to save on a db call to populate leader, we just use the stored id to compare

if (!beacon) return new UserInputError("Beacon doesn't exist");
if (!beacon) return new UserInputError("Beacon doesn't exist");

if (!beacon.followers.includes(user.id) && beacon.leader != user.id)
return new UserInputError("User should be part of beacon");
const newLandmark = new Landmark({ createdBy: user.id, ...landmark });
const populatedLandmark = await newLandmark.save().then(lan => lan.populate("createdBy"));
if (!beacon.followers.includes(user.id) && beacon.leader != user.id)
return new UserInputError("User should be part of beacon");
const newLandmark = new Landmark({ createdBy: user.id, ...landmark });

beacon.landmarks.push(newLandmark.id);
const savedLandmark = await newLandmark.save();
const populatedLandmark = await savedLandmark.populate("createdBy");

pubsub.publish("BEACON_LOCATIONS", {
beaconLocations: {
userSOS: null,
route: null,
updatedUser: null,
landmark: populatedLandmark,
},
beaconID: beacon.id,
followers: beacon.followers,
leaderID: beacon.leader,
});
await beacon.save();
beacon.landmarks.push(newLandmark.id);

pubsub.publish("BEACON_LOCATIONS", {
beaconLocations: {
userSOS: null,
route: null,
updatedUser: null,
landmark: populatedLandmark,
},
beaconID: beacon.id,
followers: beacon.followers,
leaderID: beacon.leader,
});
await beacon.save();

return populatedLandmark;
return populatedLandmark;
} catch (error) {
console.log("error", error);
throw new Error("Failed to create landmark");
}
},

updateUserLocation: async (_, { id, location }, { user, pubsub }) => {
Expand Down Expand Up @@ -613,7 +619,7 @@ const resolvers = {
const currentDate = new Date();

if (new Date(beacon.expiresAt) < currentDate) return new UserInputError("Beacon is already expired!");

// console.log("inside sos", user);
pubsub.publish("BEACON_LOCATIONS", {
beaconLocations: {
userSOS: user,
Expand All @@ -632,7 +638,6 @@ const resolvers = {
deleteUser: async (_, { credentials }) => {
try {
const userToDelete = await User.findOne({ email: credentials.email });
console.log("User to delete:", userToDelete);
if (!userToDelete) {
throw new UserInputError("User not found");
}
Expand Down Expand Up @@ -694,6 +699,24 @@ const resolvers = {
return false;
}
},

updateUserImage: async (_parent, { userId, imageUrl }, context) => {
// Optional: Add authentication check
if (!context.user) throw new AuthenticationError("Not authenticated");

try {
const updatedUser = await User.findByIdAndUpdate(userId, { imageUrl }, { new: true });

if (!updatedUser) {
throw new UserInputError("User not found");
}

return updatedUser;
} catch (error) {
console.log("error", error);
throw new Error("Failed to update user image");
}
},
},
...(process.env._HANDLER == null && {
Subscription: {
Expand All @@ -706,28 +729,55 @@ const resolvers = {
const { beaconLocations, leaderID, followers, beaconID } = payload;
const { userSOS, route, updatedUser, landmark } = beaconLocations;

// Check if user is part of this beacon
const isFollower = followers.includes(user.id);
const isLeader = leaderID == user.id;
const istrue = variables.id === beaconID && (isFollower || isLeader);
const isBeaconParticipant = variables.id === beaconID && (isFollower || isLeader);

// If user is not part of this beacon, don't send updates
if (!isBeaconParticipant) {
return false;
}

if (userSOS != null && user.id != userSOS._id) {
// Handle userSOS updates
if (userSOS != null) {
// Don't send SOS updates to the user who triggered the SOS
if (user.id == userSOS._id) {
return false;
}
payload.beaconLocations.userSOS = parseUserObject(userSOS);
return istrue;
return true;
}
if (route != null && leaderID != user.id) {
return istrue;

// Handle route updates
if (route != null) {
// Don't send route updates to the leader who created the route
if (leaderID == user.id) {
return false;
}
return true;
}

// stopping user who has updated the location
if (updatedUser != null && updatedUser._id != user.id) {
// Handle user location updates
if (updatedUser != null) {
// Don't send location updates to the user who updated their location
if (updatedUser._id == user.id) {
return false;
}
payload.beaconLocations.updatedUser = parseUserObject(updatedUser);
return istrue;
return true;
}
// stopping the creator of landmark
if (landmark != null && landmark.createdBy._id != user.id) {

// Handle landmark updates
if (landmark != null) {
// Don't send landmark updates to the user who created the landmark
if (landmark.createdBy._id == user.id) {
return false;
}
payload.beaconLocations.landmark = parseLandmarkObject(landmark);
return istrue;
return true;
}
// If none of the above conditions are met, don't send the update
return false;
}
),
Expand Down
5 changes: 5 additions & 0 deletions graphql/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,15 @@ const typeDefs = gql`
_id: ID!
createdAt: Float!
title: String!
icon: String!
location: Location!
createdBy: User!
}

input LandmarkInput {
title: String!
location: LocationInput!
icon: String!
}

type User {
Expand All @@ -73,6 +75,7 @@ const typeDefs = gql`
location: Location
beacons: [Beacon!]!
groups: [Group!]!
imageUrl: String!
}

input AuthPayload {
Expand Down Expand Up @@ -112,6 +115,7 @@ const typeDefs = gql`
input oAuthInput {
email: String
name: String
imageUrl: String
}

type UpdatedGroupPayload {
Expand Down Expand Up @@ -159,6 +163,7 @@ const typeDefs = gql`
deleteBeacon(id: ID!): Boolean!
sos(id: ID!): User!
deleteUser(credentials: AuthPayload!): Boolean!
updateUserImage(userId: ID!, imageUrl: String!): User!
}

type Subscription {
Expand Down
1 change: 0 additions & 1 deletion index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ app.use(
app.get("/", (req, res) => res.send("Hello World! This is a GraphQL API. Check out /graphql"));

app.get("/j/:shortcode", async (_req, res) => {
console.log(`shortcode route hit`);
res.send("this should open in the app eventually");
});

Expand Down
1 change: 1 addition & 0 deletions models/landmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const landmarkSchema = new Schema(
{
title: { type: String, required: true },
location: { type: LocationSchema, required: true },
icon: { type: String, required: true },
createdBy: { type: Schema.Types.ObjectId, required: true, ref: "User" },
},
{
Expand Down
4 changes: 4 additions & 0 deletions models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ const UserSchema = new Schema(
location: LocationSchema,
beacons: { type: [Schema.Types.ObjectId], ref: "Beacon", default: [] },
groups: { type: [Schema.Types.ObjectId], ref: "Group", default: [] },
imageUrl: {
type: String,
default: "https://cdn.jsdelivr.net/gh/alohe/avatars/png/memo_35.png", // default avatar URL
},
},
{
timestamps: true,
Expand Down
Loading