Skip to content
Merged
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: 2 additions & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
packages: [.]
strictDepBuilds: false
20 changes: 19 additions & 1 deletion src/db/dbQueryUtils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { avg, count, sql } from "drizzle-orm";
import { avg, count, gt, sql } from "drizzle-orm";
Comment thread
cirex-web marked this conversation as resolved.
import {
externalIdToInternalIdTable,
emailTable,
Expand All @@ -9,13 +9,15 @@ import {
timeOverwritesTable,
timesTable,
weeklyTimeOverwritesTable,
reportsTable,
} from "./schema";

import { DBType } from "./db";
import { notifySlack } from "utils/slack";
import { and, eq, gte } from "drizzle-orm";
import { parseTimeSlots } from "containers/timeBuilder";
import { ITimeSlot } from "containers/time/parsedTime";
import { DateTime } from "luxon";

type RequiredProperty<T> = { [P in keyof T]: NonNullable<T[P]> };

Expand All @@ -31,6 +33,22 @@ export class QueryUtils {
this.db = db;
}

async getReportsAfter(startTime: DateTime, locationId?: string) {
const reports = await this.db
.select()
.from(reportsTable)
.where(
and(
gt(reportsTable.createdAt, startTime.toJSDate()),
locationId !== undefined
? eq(reportsTable.locationId, locationId)
: undefined,
),
);

return reports;
}

async getSpecials(todayAsSQLString: string) {
const data = await this.db
.select()
Expand Down
18 changes: 14 additions & 4 deletions src/db/getLocations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,32 @@ import { DBType } from "./db";
/**
*
* @param db
* @param today this parameter is necessary so we can get today's specials and the open hours for the next 7 days, rather than returning everything we've stored in the db
* @param now this parameter is necessary so we can get today's specials and the open hours for the next 7 days, rather than returning everything we've stored in the db
* @returns
*/
export async function getAllLocationsFromDB(db: DBType, today: DateTime<true>) {
const timeSearchCutoff = today.minus({ days: 1 }); // 1 days worth of data before today
export async function getAllLocationsFromDB(db: DBType, now: DateTime<true>) {
const timeSearchCutoff = now.minus({ days: 1 }); // 1 days worth of data before today

const DB = new QueryUtils(db);
const locationIdToData = await DB.getLocationIdToDataMap(
timeSearchCutoff.toSQLDate(),
);
const specials = await DB.getSpecials(today.toSQLDate());
const specials = await DB.getSpecials(now.toSQLDate());
const generalOverrides = await DB.getGeneralOverrides();
const { idToPointOverrides, idToWeeklyOverrides } = await DB.getTimeOverrides(
timeSearchCutoff.toSQLDate(),
);
const [ratingsAvgs, ratingsCounts] = await DB.getRatingsAvgsAndCounts();

const reports = await DB.getReportsAfter(timeSearchCutoff, undefined);

const reportCounts = reports.reduce<{
[locationId: string]: number;
}>((acc, currentloc) => {
acc[currentloc.locationId] = (acc[currentloc.locationId] ?? 0) + 1;
return acc;
}, {});

// apply overrides, merge all time intervals, and add specials
const finalLocationData = Object.entries(locationIdToData).map(
([id, data]) => {
Expand All @@ -48,6 +57,7 @@ export async function getAllLocationsFromDB(db: DBType, today: DateTime<true>) {
ratingsCount: ratingsCounts[id] ?? 0,
todaysSoups: specials[id]?.soups ?? [],
todaysSpecials: specials[id]?.specials ?? [],
reportCount: reportCounts[id] ?? 0,
};
},
);
Expand Down
23 changes: 12 additions & 11 deletions src/endpoints/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import cookieSigner from "cookie-signature";
const OIDCConfig = await client.discovery(
env.OIDC_SERVER,
env.OIDC_CLIENT_ID,
env.OIDC_CLIENT_SECRET
env.OIDC_CLIENT_SECRET,
);
export const authEndpoints = new Elysia();
authEndpoints.get(
Expand All @@ -23,7 +23,8 @@ authEndpoints.get(
const redirectURL = client.buildAuthorizationUrl(OIDCConfig, {
redirect_uri: `${curOrigin.origin}/code-exchange`,
scope: "openid email profile",
prompt: "select_account", // force account picker
// prompt: "select_account", // force account picker
Comment thread
cirex-web marked this conversation as resolved.
hd: "andrew.cmu.edu", // idk if this excludes cmu.edu emails... (most ppl should see the login.cmu.edu screen though)
Comment thread
cirex-web marked this conversation as resolved.
});
return new Response(null, {
status: 303,
Expand All @@ -35,7 +36,7 @@ authEndpoints.get(
{
query: t.Object({ redirectURL: t.Nullable(t.String()) }),
detail: { hide: true },
}
},
);
authEndpoints.get(
"/logout",
Expand All @@ -54,7 +55,7 @@ authEndpoints.get(
{
query: t.Object({ redirectURL: t.Nullable(t.String()) }),
detail: { hide: true },
}
},
);
authEndpoints.get(
"/code-exchange",
Expand All @@ -68,8 +69,8 @@ authEndpoints.get(
console.error(e);
notifySlack(
`<!channel> OIDC code exchange failed with error ${e} ${JSON.stringify(
e.cause
)} CODE: ${e.code}`
e.cause,
)} CODE: ${e.code}`,
);
return undefined;
});
Expand All @@ -86,7 +87,7 @@ authEndpoints.get(
if (sessionId !== undefined) {
cookie["session_id"]!.value = cookieSigner.sign(
sessionId,
env.SESSION_COOKIE_SIGNING_SECRET
env.SESSION_COOKIE_SIGNING_SECRET,
);
cookie["session_id"]!.httpOnly = true;
cookie["session_id"]!.secure = true;
Expand All @@ -108,15 +109,15 @@ authEndpoints.get(
},
});
},
{ query: t.Object({ code: t.String() }), detail: { hide: true } }
{ query: t.Object({ code: t.String() }), detail: { hide: true } },
);
export async function fetchUserDetails(sessionId?: string) {
if (env.HARDCODE_SESSION_FOR_DEV_TESTING)
sessionId = env.HARDCODE_SESSION_FOR_DEV_TESTING;
if (sessionId === undefined) return null;
const unsignedSessionId = cookieSigner.unsign(
sessionId,
env.SESSION_COOKIE_SIGNING_SECRET
env.SESSION_COOKIE_SIGNING_SECRET,
);
if (!unsignedSessionId) return null;
return await fetchUserSession(db, unsignedSessionId);
Expand Down Expand Up @@ -153,12 +154,12 @@ authEndpoints.get(
firstName: t.Nullable(t.String()),
lastName: t.Nullable(t.String()),
pictureUrl: t.Nullable(t.String()),
})
}),
),
}),
detail: {
description:
"If you have an active login session with cmueats, this will return your user info. (this is NOT intended to work cross-site)",
},
}
},
);
32 changes: 16 additions & 16 deletions src/endpoints/reviews.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import Elysia, { status, t } from "elysia";
import { fetchUserDetails } from "./auth";
import { eq } from "drizzle-orm"
import {
addStarReview,
deleteStarReview,
Expand All @@ -10,7 +9,8 @@ import {
updateTagReview,
} from "db/reviews";
import { db } from "db/db";
import { reportsTable } from "db/schema";
import { QueryUtils } from "db/dbQueryUtils";
import { DateTime } from "luxon";

export const reviewEndpoints = new Elysia();
reviewEndpoints
Expand Down Expand Up @@ -57,12 +57,12 @@ reviewEndpoints
text: t.Nullable(t.String()),
createdAt: t.Number(),
updatedAt: t.Number(),
})
}),
),
})
}),
),
}),
}
},
)
.put(
"/v2/locations/:locationId/reviews/stars/me",
Expand All @@ -75,15 +75,15 @@ reviewEndpoints
body: t.Object({
stars: t.Number({ minimum: 0.5, maximum: 5, multipleOf: 0.5 }),
}),
}
},
)
.delete(
"/v2/locations/:locationId/reviews/stars/me",
async ({ user, params: { locationId } }) => {
if (user === null) throw status("Unauthorized");
await deleteStarReview(db, { locationId, userId: user.id });
return new Response("{}", { status: 200 });
}
},
)
.put(
"/v2/locations/:locationId/reviews/tags/:tagId/me",
Expand All @@ -107,7 +107,7 @@ reviewEndpoints
voteUp: t.Nullable(t.Boolean()),
text: t.Nullable(t.String({ maxLength: 1000 })),
}),
}
},
)
.get(
"/v2/locations/:locationId/reviews/tags",
Expand All @@ -126,16 +126,16 @@ reviewEndpoints
vote: t.Boolean(),
createdAt: t.Number(),
updatedAt: t.Number(),
})
}),
),
}
},
)
.get(
"/v2/locations/:locationId/reports",
async ({ params: { locationId } }) => {
const reports = await db.select().from(reportsTable).where(eq(reportsTable.locationId, locationId))
const yesterday = DateTime.now().minus({ days: 1 });

return reports
return await new QueryUtils(db).getReportsAfter(yesterday, locationId);
},
{
response: t.Array(
Expand All @@ -144,8 +144,8 @@ reviewEndpoints
userId: t.Nullable(t.Number()),
createdAt: t.Date(),
locationId: t.String(),
message: t.String()
})
)
}
message: t.String(),
}),
),
},
);
1 change: 1 addition & 0 deletions tests/database.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const locationOut = {
todaysSoups: [],
todaysSpecials: [],
conceptId: "1",
reportCount: 0,
};

describe("general location insertion tests", () => {
Expand Down
Loading