Merge pull request #590 from gitroomhq/feat/digest

Digest Notifications
This commit is contained in:
Nevo David 2025-02-06 13:55:43 +07:00 committed by GitHub
commit dc3296a51e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 103 additions and 12 deletions

View File

@ -15,4 +15,9 @@ export class PostsController {
async payout(data: { id: string; releaseURL: string }) {
return this._postsService.payout(data.id, data.releaseURL);
}
@EventPattern('sendDigestEmail', Transport.REDIS)
async sendDigestEmail(data: { subject: string, org: string; since: string }) {
return this._postsService.sendDigestEmail(data.subject, data.org, data.since);
}
}

View File

@ -2,13 +2,17 @@ import { Injectable } from '@nestjs/common';
import { NotificationsRepository } from '@gitroom/nestjs-libraries/database/prisma/notifications/notifications.repository';
import { EmailService } from '@gitroom/nestjs-libraries/services/email.service';
import { OrganizationRepository } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.repository';
import { BullMqClient } from '@gitroom/nestjs-libraries/bull-mq-transport-new/client';
import { ioRedis } from '@gitroom/nestjs-libraries/redis/redis.service';
import dayjs from 'dayjs';
@Injectable()
export class NotificationService {
constructor(
private _notificationRepository: NotificationsRepository,
private _emailService: EmailService,
private _organizationRepository: OrganizationRepository
private _organizationRepository: OrganizationRepository,
private _workerServiceProducer: BullMqClient
) {}
getMainPageCount(organizationId: string, userId: string) {
@ -25,12 +29,58 @@ export class NotificationService {
);
}
async inAppNotification(orgId: string, subject: string, message: string, sendEmail = false) {
getNotificationsSince(organizationId: string, since: string) {
return this._notificationRepository.getNotificationsSince(
organizationId,
since
);
}
async inAppNotification(
orgId: string,
subject: string,
message: string,
sendEmail = false,
digest = false
) {
const date = new Date().toISOString();
await this._notificationRepository.createNotification(orgId, message);
if (!sendEmail) {
return;
}
if (digest) {
await ioRedis.watch('digest_' + orgId);
const value = await ioRedis.get('digest_' + orgId);
if (value) {
return;
}
await ioRedis
.multi()
.set('digest_' + orgId, date)
.expire('digest_' + orgId, 60)
.exec();
this._workerServiceProducer.emit('sendDigestEmail', {
id: 'digest_' + orgId,
options: {
delay: 60000,
},
payload: {
subject,
org: orgId,
since: date,
},
});
return;
}
await this.sendEmailsToOrg(orgId, subject, message);
}
async sendEmailsToOrg(orgId: string, subject: string, message: string) {
const userOrg = await this._organizationRepository.getAllUsersOrgs(orgId);
for (const user of userOrg?.users || []) {
await this.sendEmail(user.user.email, subject, message);

View File

@ -45,6 +45,17 @@ export class NotificationsRepository {
});
}
async getNotificationsSince(organizationId: string, since: string) {
return this._notifications.model.notifications.findMany({
where: {
organizationId,
createdAt: {
gte: new Date(since),
},
},
});
}
async getNotifications(organizationId: string, userId: string) {
const { lastReadNotifications } = (await this.getLastReadNotification(
userId

View File

@ -46,11 +46,13 @@ export class PostsService {
async getStatistics(orgId: string, id: string) {
const getPost = await this.getPostsRecursively(id, true, orgId, true);
const content = getPost.map((p) => p.content);
const shortLinksTracking = await this._shortLinkService.getStatistics(content);
const shortLinksTracking = await this._shortLinkService.getStatistics(
content
);
return {
clicks: shortLinksTracking
}
clicks: shortLinksTracking,
};
}
async getPostsRecursively(
@ -363,7 +365,10 @@ export class PostsService {
`Your post has been published on ${capitalize(
integration.providerIdentifier
)}`,
`Your post has been published at ${publishedPosts[0].releaseURL}`,
`Your post has been published on ${capitalize(
integration.providerIdentifier
)} at ${publishedPosts[0].releaseURL}`,
true,
true
);
@ -517,10 +522,10 @@ export class PostsService {
const post = await this._postRepository.deletePost(orgId, group);
if (post?.id) {
await this._workerServiceProducer.delete('post', post.id);
return {id: post.id};
return { id: post.id };
}
return {error: true};
return { error: true };
}
async countPostsFromDay(orgId: string, date: Date) {
@ -566,8 +571,10 @@ export class PostsService {
async createPost(orgId: string, body: CreatePostDto) {
const postList = [];
for (const post of body.posts) {
const messages = post.value.map(p => p.content);
const updateContent = !body.shortLink ? messages : await this._shortLinkService.convertTextToShortLinks(orgId, messages);
const messages = post.value.map((p) => p.content);
const updateContent = !body.shortLink
? messages
: await this._shortLinkService.convertTextToShortLinks(orgId, messages);
post.value = post.value.map((p, i) => ({
...p,
@ -624,7 +631,7 @@ export class PostsService {
postList.push({
postId: posts[0].id,
integration: post.integration.id,
})
});
}
return postList;
@ -878,4 +885,23 @@ export class PostsService {
) {
return this._postRepository.createComment(orgId, userId, postId, comment);
}
async sendDigestEmail(subject: string, orgId: string, since: string) {
const getNotificationsForOrgSince =
await this._notificationService.getNotificationsSince(orgId, since);
if (getNotificationsForOrgSince.length === 0) {
return;
}
const message = getNotificationsForOrgSince
.map((p) => p.content)
.join('<br />');
await this._notificationService.sendEmailsToOrg(
orgId,
getNotificationsForOrgSince.length === 1
? subject
: '[Postiz] Your latest notifications',
message
);
}
}

View File

@ -6,7 +6,6 @@ import {
} from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface';
import { makeId } from '@gitroom/nestjs-libraries/services/make.is';
import {
NotEnoughScopes,
RefreshToken,
SocialAbstract,
} from '@gitroom/nestjs-libraries/integrations/social.abstract';