aboutsummaryrefslogtreecommitdiffstats
path: root/packages/trpc
diff options
context:
space:
mode:
Diffstat (limited to 'packages/trpc')
-rw-r--r--packages/trpc/email.ts4
-rw-r--r--packages/trpc/routers/invites.test.ts70
-rw-r--r--packages/trpc/routers/invites.ts32
3 files changed, 25 insertions, 81 deletions
diff --git a/packages/trpc/email.ts b/packages/trpc/email.ts
index e69c4507..edf8ec92 100644
--- a/packages/trpc/email.ts
+++ b/packages/trpc/email.ts
@@ -100,7 +100,7 @@ export async function sendInviteEmail(
</p>
<p>If the button doesn't work, you can copy and paste this link into your browser:</p>
<p><a href="${inviteUrl}">${inviteUrl}</a></p>
- <p>This invitation will expire in 7 days.</p>
+
<p>If you weren't expecting this invitation, you can safely ignore this email.</p>
</div>
`,
@@ -112,7 +112,7 @@ ${inviterName} has invited you to join Karakeep, a powerful bookmarking and cont
Accept your invitation by visiting this link:
${inviteUrl}
-This invitation will expire in 7 days.
+
If you weren't expecting this invitation, you can safely ignore this email.
`,
diff --git a/packages/trpc/routers/invites.test.ts b/packages/trpc/routers/invites.test.ts
index f4f048df..5cadbc14 100644
--- a/packages/trpc/routers/invites.test.ts
+++ b/packages/trpc/routers/invites.test.ts
@@ -53,7 +53,6 @@ describe("Invites Router", () => {
});
expect(invite.email).toBe("newuser@test.com");
- expect(invite.expiresAt).toBeDefined();
expect(invite.id).toBeDefined();
// Verify the invite was created in the database
@@ -238,7 +237,7 @@ describe("Invites Router", () => {
).rejects.toThrow(/Invite not found/);
});
- test<CustomTestContext>("cannot get expired invite", async ({
+ test<CustomTestContext>("can get invite by token", async ({
db,
unauthedAPICaller,
}) => {
@@ -255,22 +254,16 @@ describe("Invites Router", () => {
email: "newuser@test.com",
});
- // Set expiry to past date
- const pastDate = new Date(Date.now() - 24 * 60 * 60 * 1000);
- await db
- .update(invites)
- .set({ expiresAt: pastDate })
- .where(eq(invites.id, invite.id));
-
const dbInvite = await db.query.invites.findFirst({
where: eq(invites.id, invite.id),
});
- await expect(() =>
- unauthedAPICaller.invites.get({
- token: dbInvite!.token,
- }),
- ).rejects.toThrow(/Invite has expired/);
+ const result = await unauthedAPICaller.invites.get({
+ token: dbInvite!.token,
+ });
+
+ expect(result.email).toBe("newuser@test.com");
+ expect(result.expired).toBe(false);
});
test<CustomTestContext>("cannot get used invite (deleted)", async ({
@@ -346,7 +339,7 @@ describe("Invites Router", () => {
expect(deletedInvite).toBeUndefined();
});
- test<CustomTestContext>("cannot accept expired invite", async ({
+ test<CustomTestContext>("can accept valid invite", async ({
db,
unauthedAPICaller,
}) => {
@@ -363,23 +356,18 @@ describe("Invites Router", () => {
email: "newuser@test.com",
});
- const pastDate = new Date(Date.now() - 24 * 60 * 60 * 1000);
- await db
- .update(invites)
- .set({ expiresAt: pastDate })
- .where(eq(invites.id, invite.id));
-
const dbInvite = await db.query.invites.findFirst({
where: eq(invites.id, invite.id),
});
- await expect(() =>
- unauthedAPICaller.invites.accept({
- token: dbInvite!.token,
- name: "New User",
- password: "newpass123",
- }),
- ).rejects.toThrow(/This invite has expired/);
+ const result = await unauthedAPICaller.invites.accept({
+ token: dbInvite!.token,
+ name: "New User",
+ password: "newpass123",
+ });
+
+ expect(result.email).toBe("newuser@test.com");
+ expect(result.name).toBe("New User");
});
test<CustomTestContext>("cannot accept used invite (deleted)", async ({
@@ -510,9 +498,7 @@ describe("Invites Router", () => {
});
expect(resentInvite.email).toBe("newuser@test.com");
- expect(resentInvite.expiresAt.getTime()).toBeGreaterThan(
- originalDbInvite!.expiresAt.getTime(),
- );
+ expect(resentInvite.id).toBe(originalDbInvite!.id);
// Verify token was updated in database
const updatedDbInvite = await db.query.invites.findFirst({
@@ -556,7 +542,7 @@ describe("Invites Router", () => {
).rejects.toThrow(/Invite not found/);
});
- test<CustomTestContext>("invite expiration is set correctly", async ({
+ test<CustomTestContext>("invite creation works without expiration", async ({
db,
unauthedAPICaller,
}) => {
@@ -569,26 +555,12 @@ describe("Invites Router", () => {
const adminCaller = getApiCaller(db, admin.id, admin.email, "admin");
- const beforeCreate = new Date();
const invite = await adminCaller.invites.create({
email: "newuser@test.com",
});
- const afterCreate = new Date();
-
- // Allow for some timing variance (1 second buffer)
- const expectedMinExpiry = new Date(
- beforeCreate.getTime() + 7 * 24 * 60 * 60 * 1000 - 1000,
- );
- const expectedMaxExpiry = new Date(
- afterCreate.getTime() + 7 * 24 * 60 * 60 * 1000 + 1000,
- );
-
- expect(invite.expiresAt.getTime()).toBeGreaterThanOrEqual(
- expectedMinExpiry.getTime(),
- );
- expect(invite.expiresAt.getTime()).toBeLessThanOrEqual(
- expectedMaxExpiry.getTime(),
- );
+
+ expect(invite.email).toBe("newuser@test.com");
+ expect(invite.id).toBeDefined();
});
test<CustomTestContext>("invite includes inviter information", async ({
diff --git a/packages/trpc/routers/invites.ts b/packages/trpc/routers/invites.ts
index 0a98f36a..e010fd73 100644
--- a/packages/trpc/routers/invites.ts
+++ b/packages/trpc/routers/invites.ts
@@ -1,6 +1,6 @@
import { randomBytes } from "crypto";
import { TRPCError } from "@trpc/server";
-import { and, eq, gt } from "drizzle-orm";
+import { eq } from "drizzle-orm";
import { z } from "zod";
import { invites, users } from "@karakeep/db/schema";
@@ -35,10 +35,7 @@ export const invitesAppRouter = router({
}
const existingInvite = await ctx.db.query.invites.findFirst({
- where: and(
- eq(invites.email, input.email),
- gt(invites.expiresAt, new Date()),
- ),
+ where: eq(invites.email, input.email),
});
if (existingInvite) {
@@ -49,14 +46,12 @@ export const invitesAppRouter = router({
}
const token = randomBytes(32).toString("hex");
- const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); // 7 days
const [invite] = await ctx.db
.insert(invites)
.values({
email: input.email,
token,
- expiresAt,
invitedBy: ctx.user.id,
})
.returning();
@@ -76,7 +71,6 @@ export const invitesAppRouter = router({
return {
id: invite.id,
email: invite.email,
- expiresAt: invite.expiresAt,
};
}),
@@ -88,7 +82,6 @@ export const invitesAppRouter = router({
id: z.string(),
email: z.string(),
createdAt: z.date(),
- expiresAt: z.date(),
invitedBy: z.object({
id: z.string(),
name: z.string(),
@@ -148,16 +141,6 @@ export const invitesAppRouter = router({
});
}
- const now = new Date();
- const expired = invite.expiresAt < now;
-
- if (expired) {
- throw new TRPCError({
- code: "BAD_REQUEST",
- message: "Invite has expired",
- });
- }
-
return {
email: invite.email,
expired: false,
@@ -191,14 +174,6 @@ export const invitesAppRouter = router({
});
}
- const now = new Date();
- if (invite.expiresAt < now) {
- throw new TRPCError({
- code: "BAD_REQUEST",
- message: "This invite has expired",
- });
- }
-
const existingUser = await ctx.db.query.users.findFirst({
where: eq(users.email, invite.email),
});
@@ -273,13 +248,11 @@ export const invitesAppRouter = router({
}
const newToken = randomBytes(32).toString("hex");
- const newExpiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); // 7 days
await ctx.db
.update(invites)
.set({
token: newToken,
- expiresAt: newExpiresAt,
})
.where(eq(invites.id, input.inviteId));
@@ -298,7 +271,6 @@ export const invitesAppRouter = router({
return {
id: invite.id,
email: invite.email,
- expiresAt: newExpiresAt,
};
}),
});