Add document unsubscribe link in email footer (#5762)

This commit is contained in:
Tom Moor
2023-09-03 19:04:28 -04:00
committed by GitHub
parent 0261e0712c
commit d7c331532d
24 changed files with 347 additions and 161 deletions

View File

@@ -42,3 +42,15 @@ export const SubscriptionsDeleteSchema = BaseSchema.extend({
});
export type SubscriptionsDeleteReq = z.infer<typeof SubscriptionsDeleteSchema>;
export const SubscriptionsDeleteTokenSchema = BaseSchema.extend({
query: z.object({
userId: z.string().uuid(),
documentId: z.string().uuid(),
token: z.string(),
}),
});
export type SubscriptionsDeleteTokenReq = z.infer<
typeof SubscriptionsDeleteTokenSchema
>;

View File

@@ -1,13 +1,19 @@
import Router from "koa-router";
import { Transaction } from "sequelize";
import { QueryNotices } from "@shared/types";
import subscriptionCreator from "@server/commands/subscriptionCreator";
import subscriptionDestroyer from "@server/commands/subscriptionDestroyer";
import env from "@server/env";
import auth from "@server/middlewares/authentication";
import { rateLimiter } from "@server/middlewares/rateLimiter";
import { transaction } from "@server/middlewares/transaction";
import validate from "@server/middlewares/validate";
import { Subscription, Document } from "@server/models";
import { Subscription, Document, User } from "@server/models";
import SubscriptionHelper from "@server/models/helpers/SubscriptionHelper";
import { authorize } from "@server/policies";
import { presentSubscription } from "@server/presenters";
import { APIContext } from "@server/types";
import { RateLimiterStrategy } from "@server/utils/RateLimiter";
import pagination from "../middlewares/pagination";
import * as T from "./schema";
@@ -103,6 +109,51 @@ router.post(
}
);
router.get(
"subscriptions.delete",
validate(T.SubscriptionsDeleteTokenSchema),
rateLimiter(RateLimiterStrategy.FivePerMinute),
transaction(),
async (ctx: APIContext<T.SubscriptionsDeleteTokenReq>) => {
const { transaction } = ctx.state;
const { userId, documentId, token } = ctx.input.query;
const unsubscribeToken = SubscriptionHelper.unsubscribeToken(
userId,
documentId
);
if (unsubscribeToken !== token) {
ctx.redirect(`${env.URL}?notice=invalid-auth`);
return;
}
const [subscription, user] = await Promise.all([
Subscription.findOne({
where: {
userId,
documentId,
},
lock: Transaction.LOCK.UPDATE,
rejectOnEmpty: true,
transaction,
}),
User.scope("withTeam").findByPk(userId, {
rejectOnEmpty: true,
transaction,
}),
]);
authorize(user, "delete", subscription);
await subscription.destroy({ transaction });
ctx.redirect(
`${user.team.url}/home?notice=${QueryNotices.UnsubscribeDocument}`
);
}
);
router.post(
"subscriptions.delete",
auth(),