| import { PrismaRepository } from '@gitroom/nestjs-libraries/database/prisma/prisma.service'; |
| import { Injectable } from '@nestjs/common'; |
| import { Post as PostBody } from '@gitroom/nestjs-libraries/dtos/posts/create.post.dto'; |
| import { |
| APPROVED_SUBMIT_FOR_ORDER, |
| CreationMethod, |
| Post, |
| State, |
| } from '@prisma/client'; |
| import { GetPostsDto } from '@gitroom/nestjs-libraries/dtos/posts/get.posts.dto'; |
| import { GetPostsListDto } from '@gitroom/nestjs-libraries/dtos/posts/get.posts.list.dto'; |
| import dayjs from 'dayjs'; |
| import isoWeek from 'dayjs/plugin/isoWeek'; |
| import weekOfYear from 'dayjs/plugin/weekOfYear'; |
| import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'; |
| import utc from 'dayjs/plugin/utc'; |
| import { v4 as uuidv4 } from 'uuid'; |
| import { CreateTagDto } from '@gitroom/nestjs-libraries/dtos/posts/create.tag.dto'; |
|
|
| dayjs.extend(isoWeek); |
| dayjs.extend(weekOfYear); |
| dayjs.extend(isSameOrAfter); |
| dayjs.extend(utc); |
|
|
| @Injectable() |
| export class PostsRepository { |
| constructor( |
| private _post: PrismaRepository<'post'>, |
| private _popularPosts: PrismaRepository<'popularPosts'>, |
| private _comments: PrismaRepository<'comments'>, |
| private _tags: PrismaRepository<'tags'>, |
| private _tagsPosts: PrismaRepository<'tagsPosts'>, |
| private _errors: PrismaRepository<'errors'> |
| ) {} |
|
|
| searchForMissingThreeHoursPosts() { |
| return this._post.model.post.findMany({ |
| where: { |
| integration: { |
| refreshNeeded: false, |
| inBetweenSteps: false, |
| disabled: false, |
| deletedAt: null, |
| }, |
| publishDate: { |
| gte: dayjs.utc().subtract(2, 'day').toDate(), |
| lt: dayjs.utc().toDate(), |
| }, |
| state: 'QUEUE', |
| deletedAt: null, |
| parentPostId: null, |
| }, |
| select: { |
| id: true, |
| organizationId: true, |
| integration: { |
| select: { |
| providerIdentifier: true, |
| }, |
| }, |
| publishDate: true, |
| }, |
| }); |
| } |
|
|
| getOldPosts(orgId: string, date: string) { |
| return this._post.model.post.findMany({ |
| where: { |
| integration: { |
| refreshNeeded: false, |
| inBetweenSteps: false, |
| disabled: false, |
| }, |
| organizationId: orgId, |
| publishDate: { |
| lte: dayjs(date).toDate(), |
| }, |
| deletedAt: null, |
| parentPostId: null, |
| }, |
| orderBy: { |
| publishDate: 'desc', |
| }, |
| select: { |
| id: true, |
| content: true, |
| publishDate: true, |
| releaseURL: true, |
| state: true, |
| integration: { |
| select: { |
| id: true, |
| name: true, |
| providerIdentifier: true, |
| picture: true, |
| type: true, |
| }, |
| }, |
| }, |
| }); |
| } |
|
|
| updateImages(id: string, images: string) { |
| return this._post.model.post.update({ |
| where: { |
| id, |
| }, |
| data: { |
| image: images, |
| }, |
| }); |
| } |
|
|
| getPostUrls(orgId: string, ids: string[]) { |
| return this._post.model.post.findMany({ |
| where: { |
| organizationId: orgId, |
| id: { |
| in: ids, |
| }, |
| }, |
| select: { |
| id: true, |
| releaseURL: true, |
| }, |
| }); |
| } |
|
|
| async getPosts(orgId: string, query: GetPostsDto) { |
| |
| const startDate = dayjs.utc(query.startDate).toDate(); |
| const endDate = dayjs.utc(query.endDate).toDate(); |
|
|
| const list = await this._post.model.post.findMany({ |
| where: { |
| AND: [ |
| { |
| OR: [ |
| { |
| organizationId: orgId, |
| }, |
| ], |
| }, |
| { |
| OR: [ |
| { |
| publishDate: { |
| gte: startDate, |
| lte: endDate, |
| }, |
| }, |
| { |
| intervalInDays: { |
| not: null, |
| }, |
| }, |
| ], |
| }, |
| ], |
| integration: { |
| deletedAt: null, |
| organizationId: orgId, |
| }, |
| deletedAt: null, |
| parentPostId: null, |
| ...(query.customer |
| ? { |
| integration: { |
| customerId: query.customer, |
| }, |
| } |
| : {}), |
| }, |
| select: { |
| id: true, |
| content: true, |
| publishDate: true, |
| releaseURL: true, |
| releaseId: true, |
| state: true, |
| intervalInDays: true, |
| group: true, |
| creationMethod: true, |
| tags: { |
| select: { |
| tag: true, |
| }, |
| }, |
| integration: { |
| select: { |
| id: true, |
| providerIdentifier: true, |
| name: true, |
| picture: true, |
| }, |
| }, |
| }, |
| }); |
|
|
| return list.reduce((all, post) => { |
| if (!post.intervalInDays) { |
| return [...all, post]; |
| } |
|
|
| const addMorePosts = []; |
| let startingDate = dayjs.utc(post.publishDate); |
| while (dayjs.utc(endDate).isSameOrAfter(startingDate)) { |
| if (dayjs(startingDate).isSameOrAfter(dayjs.utc(post.publishDate))) { |
| addMorePosts.push({ |
| ...post, |
| publishDate: startingDate.toDate(), |
| actualDate: post.publishDate, |
| }); |
| } |
|
|
| startingDate = startingDate.add(post.intervalInDays, 'days'); |
| } |
|
|
| return [...all, ...addMorePosts]; |
| }, [] as any[]); |
| } |
|
|
| async getPostsList(orgId: string, query: GetPostsListDto) { |
| const page = query.page || 0; |
| const limit = query.limit || 20; |
| const skip = page * limit; |
|
|
| const stateFilter = query.state || 'all'; |
| const stateAndDate = |
| stateFilter === 'scheduled' |
| ? { |
| state: State.QUEUE, |
| } |
| : stateFilter === 'draft' |
| ? { state: State.DRAFT } |
| : stateFilter === 'published' |
| ? { state: State.PUBLISHED } |
| : { |
| state: { |
| in: [State.QUEUE, State.DRAFT, State.PUBLISHED, State.ERROR], |
| }, |
| }; |
|
|
| const orderDirection: 'asc' | 'desc' = |
| stateFilter === 'published' ? 'desc' : 'asc'; |
|
|
| const where = { |
| AND: [ |
| { |
| OR: [ |
| { |
| organizationId: orgId, |
| }, |
| ], |
| }, |
| ], |
| ...stateAndDate, |
| |
| |
| ...(stateFilter === 'published' |
| ? {} |
| : { publishDate: { gte: dayjs.utc().toDate() } }), |
| deletedAt: null as Date | null, |
| parentPostId: null as string | null, |
| intervalInDays: null as number | null, |
|
|
| integration: { |
| deletedAt: null as any, |
| organizationId: orgId, |
| ...(query.customer |
| ? { |
| customerId: query.customer, |
| } |
| : {}), |
| }, |
| }; |
|
|
| const [posts, total] = await Promise.all([ |
| this._post.model.post.findMany({ |
| where, |
| skip, |
| take: limit, |
| orderBy: { |
| publishDate: orderDirection, |
| }, |
| select: { |
| id: true, |
| content: true, |
| publishDate: true, |
| releaseURL: true, |
| releaseId: true, |
| state: true, |
| intervalInDays: true, |
| group: true, |
| creationMethod: true, |
| tags: { |
| select: { |
| tag: true, |
| }, |
| }, |
| integration: { |
| select: { |
| id: true, |
| providerIdentifier: true, |
| name: true, |
| picture: true, |
| }, |
| }, |
| }, |
| }), |
| this._post.model.post.count({ where }), |
| ]); |
|
|
| return { |
| posts, |
| total, |
| page, |
| limit, |
| hasMore: skip + posts.length < total, |
| }; |
| } |
|
|
| async deletePost(orgId: string, group: string) { |
| await this._post.model.post.updateMany({ |
| where: { |
| organizationId: orgId, |
| group, |
| }, |
| data: { |
| deletedAt: new Date(), |
| }, |
| }); |
|
|
| return this._post.model.post.findFirst({ |
| where: { |
| organizationId: orgId, |
| group, |
| parentPostId: null, |
| }, |
| select: { |
| id: true, |
| }, |
| }); |
| } |
|
|
| getPostsByGroup(orgId: string, group: string) { |
| return this._post.model.post.findMany({ |
| where: { |
| group, |
| ...(orgId ? { organizationId: orgId } : {}), |
| deletedAt: null, |
| }, |
| include: { |
| integration: true, |
| tags: { |
| select: { |
| tag: true, |
| }, |
| }, |
| }, |
| }); |
| } |
|
|
| getPost( |
| id: string, |
| includeIntegration = false, |
| orgId?: string, |
| isFirst?: boolean |
| ) { |
| return this._post.model.post.findUnique({ |
| where: { |
| id, |
| ...(orgId ? { organizationId: orgId } : {}), |
| deletedAt: null, |
| }, |
| include: { |
| ...(includeIntegration |
| ? { |
| integration: true, |
| tags: { |
| select: { |
| tag: true, |
| }, |
| }, |
| } |
| : {}), |
| childrenPost: true, |
| }, |
| }); |
| } |
|
|
| updatePost(id: string, postId: string, releaseURL: string) { |
| return this._post.model.post.update({ |
| where: { |
| id, |
| }, |
| data: { |
| state: 'PUBLISHED', |
| releaseURL, |
| releaseId: postId, |
| }, |
| }); |
| } |
|
|
| updateReleaseId(id: string, orgId: string, releaseId: string) { |
| return this._post.model.post.update({ |
| where: { |
| id, |
| organizationId: orgId, |
| releaseId: 'missing', |
| }, |
| data: { |
| releaseId: String(releaseId), |
| }, |
| }); |
| } |
|
|
| async changeState(id: string, state: State, err?: any, body?: any) { |
| const update = await this._post.model.post.update({ |
| where: { |
| id, |
| }, |
| data: { |
| state, |
| ...(err |
| ? { error: typeof err === 'string' ? err : JSON.stringify(err) } |
| : {}), |
| }, |
| include: { |
| integration: { |
| select: { |
| providerIdentifier: true, |
| }, |
| }, |
| }, |
| }); |
|
|
| if (state === 'ERROR' && err && body) { |
| try { |
| await this._errors.model.errors.create({ |
| data: { |
| message: typeof err === 'string' ? err : JSON.stringify(err), |
| organizationId: update.organizationId, |
| platform: update.integration.providerIdentifier, |
| postId: update.id, |
| body: typeof body === 'string' ? body : JSON.stringify(body), |
| }, |
| }); |
| } catch (err) {} |
| } |
|
|
| return update; |
| } |
|
|
| getErrorsByPostIds(postIds: string[]) { |
| return this._errors.model.errors.findMany({ |
| where: { |
| postId: { in: postIds }, |
| }, |
| orderBy: { createdAt: 'desc' }, |
| }); |
| } |
|
|
| async changeDate( |
| orgId: string, |
| id: string, |
| date: string, |
| isDraft: boolean, |
| action: 'schedule' | 'update' = 'schedule' |
| ) { |
| return this._post.model.post.update({ |
| where: { |
| organizationId: orgId, |
| id, |
| }, |
| data: { |
| publishDate: dayjs(date).toDate(), |
| |
| |
| ...(action === 'schedule' |
| ? { |
| state: isDraft ? 'DRAFT' : 'QUEUE', |
| releaseId: null, |
| releaseURL: null, |
| } |
| : {}), |
| }, |
| }); |
| } |
|
|
| countPostsFromDay(orgId: string, date: Date) { |
| return this._post.model.post.count({ |
| where: { |
| organizationId: orgId, |
| publishDate: { |
| gte: date, |
| }, |
| OR: [ |
| { |
| deletedAt: null, |
| state: { |
| in: ['QUEUE'], |
| }, |
| }, |
| { |
| state: 'PUBLISHED', |
| }, |
| ], |
| }, |
| }); |
| } |
|
|
| async createOrUpdatePost( |
| state: 'draft' | 'schedule' | 'now' | 'update', |
| orgId: string, |
| date: string, |
| body: PostBody, |
| tags: { value: string; label: string }[], |
| creationMethod: CreationMethod, |
| inter?: number |
| ) { |
| const posts: Post[] = []; |
| const uuid = uuidv4(); |
|
|
| for (const value of body.value) { |
| const updateData = (type: 'create' | 'update') => ({ |
| publishDate: dayjs(date).toDate(), |
| integration: { |
| connect: { |
| id: body.integration.id, |
| organizationId: orgId, |
| }, |
| }, |
| ...(posts?.[posts.length - 1]?.id |
| ? { |
| parentPost: { |
| connect: { |
| id: posts[posts.length - 1]?.id, |
| }, |
| }, |
| } |
| : type === 'update' |
| ? { |
| parentPost: { |
| disconnect: true, |
| }, |
| } |
| : {}), |
| content: value.content, |
| delay: value.delay || 0, |
| group: uuid, |
| intervalInDays: inter ? +inter : null, |
| approvedSubmitForOrder: APPROVED_SUBMIT_FOR_ORDER.NO, |
| ...(type === 'create' ? { creationMethod } : {}), |
| ...(state === 'update' |
| ? {} |
| : { |
| state: |
| state === 'draft' ? ('DRAFT' as const) : ('QUEUE' as const), |
| }), |
| image: JSON.stringify(value.image), |
| settings: JSON.stringify(body.settings), |
| organization: { |
| connect: { |
| id: orgId, |
| }, |
| }, |
| }); |
|
|
| posts.push( |
| await this._post.model.post.upsert({ |
| where: { |
| id: value.id || uuidv4(), |
| }, |
| create: { ...updateData('create') }, |
| update: { |
| ...updateData('update'), |
| lastMessage: { |
| disconnect: true, |
| }, |
| submittedForOrder: { |
| disconnect: true, |
| }, |
| }, |
| }) |
| ); |
|
|
| if (posts.length === 1) { |
| await this._tagsPosts.model.tagsPosts.deleteMany({ |
| where: { |
| post: { |
| id: posts[0].id, |
| }, |
| }, |
| }); |
|
|
| if (tags.length) { |
| const tagsList = await this._tags.model.tags.findMany({ |
| where: { |
| orgId: orgId, |
| name: { |
| in: tags.map((tag) => tag.label).filter((f) => f), |
| }, |
| }, |
| }); |
|
|
| if (tagsList.length) { |
| await this._post.model.post.update({ |
| where: { |
| id: posts[posts.length - 1].id, |
| }, |
| data: { |
| tags: { |
| createMany: { |
| data: tagsList.map((tag) => ({ |
| tagId: tag.id, |
| })), |
| }, |
| }, |
| }, |
| }); |
| } |
| } |
| } |
| } |
|
|
| const previousPost = body.group |
| ? ( |
| await this._post.model.post.findFirst({ |
| where: { |
| group: body.group, |
| deletedAt: null, |
| parentPostId: null, |
| }, |
| select: { |
| id: true, |
| }, |
| }) |
| )?.id! |
| : undefined; |
|
|
| if (body.group) { |
| await this._post.model.post.updateMany({ |
| where: { |
| group: body.group, |
| deletedAt: null, |
| }, |
| data: { |
| parentPostId: null, |
| deletedAt: new Date(), |
| }, |
| }); |
| } |
|
|
| return { previousPost, posts }; |
| } |
|
|
| async submit(id: string, order: string, buyerOrganizationId: string) { |
| return this._post.model.post.update({ |
| where: { |
| id, |
| }, |
| data: { |
| submittedForOrderId: order, |
| approvedSubmitForOrder: 'WAITING_CONFIRMATION', |
| submittedForOrganizationId: buyerOrganizationId, |
| }, |
| select: { |
| id: true, |
| description: true, |
| submittedForOrder: { |
| select: { |
| messageGroupId: true, |
| }, |
| }, |
| }, |
| }); |
| } |
|
|
| updateMessage(id: string, messageId: string) { |
| return this._post.model.post.update({ |
| where: { |
| id, |
| }, |
| data: { |
| lastMessageId: messageId, |
| }, |
| }); |
| } |
|
|
| getPostById(id: string, org?: string) { |
| return this._post.model.post.findUnique({ |
| where: { |
| id, |
| ...(org ? { organizationId: org } : {}), |
| }, |
| include: { |
| integration: true, |
| submittedForOrder: { |
| include: { |
| posts: { |
| where: { |
| state: 'PUBLISHED', |
| }, |
| }, |
| ordersItems: true, |
| seller: { |
| select: { |
| id: true, |
| account: true, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }); |
| } |
|
|
| findAllExistingCategories() { |
| return this._popularPosts.model.popularPosts.findMany({ |
| select: { |
| category: true, |
| }, |
| distinct: ['category'], |
| }); |
| } |
|
|
| findAllExistingTopicsOfCategory(category: string) { |
| return this._popularPosts.model.popularPosts.findMany({ |
| where: { |
| category, |
| }, |
| select: { |
| topic: true, |
| }, |
| distinct: ['topic'], |
| }); |
| } |
|
|
| findPopularPosts(category: string, topic?: string) { |
| return this._popularPosts.model.popularPosts.findMany({ |
| where: { |
| category, |
| ...(topic ? { topic } : {}), |
| }, |
| select: { |
| content: true, |
| hook: true, |
| }, |
| }); |
| } |
|
|
| createPopularPosts(post: { |
| category: string; |
| topic: string; |
| content: string; |
| hook: string; |
| }) { |
| return this._popularPosts.model.popularPosts.create({ |
| data: { |
| category: 'category', |
| topic: 'topic', |
| content: 'content', |
| hook: 'hook', |
| }, |
| }); |
| } |
|
|
| async getPostsCountsByDates( |
| orgId: string, |
| times: number[], |
| date: dayjs.Dayjs |
| ) { |
| const dates = await this._post.model.post.findMany({ |
| where: { |
| deletedAt: null, |
| organizationId: orgId, |
| publishDate: { |
| in: times.map((time) => { |
| return date.clone().add(time, 'minutes').toDate(); |
| }), |
| }, |
| }, |
| }); |
|
|
| return times.filter( |
| (time) => |
| date.clone().add(time, 'minutes').isAfter(dayjs.utc()) && |
| !dates.find((dateFind) => { |
| return ( |
| dayjs |
| .utc(dateFind.publishDate) |
| .diff(date.clone().startOf('day'), 'minutes') == time |
| ); |
| }) |
| ); |
| } |
|
|
| async getComments(postId: string) { |
| return this._comments.model.comments.findMany({ |
| where: { |
| postId, |
| }, |
| orderBy: { |
| createdAt: 'asc', |
| }, |
| }); |
| } |
|
|
| async getTags(orgId: string) { |
| return this._tags.model.tags.findMany({ |
| where: { |
| orgId, |
| deletedAt: null, |
| }, |
| }); |
| } |
|
|
| createTag(orgId: string, body: CreateTagDto) { |
| return this._tags.model.tags.create({ |
| data: { |
| orgId, |
| name: body.name, |
| color: body.color, |
| }, |
| }); |
| } |
|
|
| editTag(id: string, orgId: string, body: CreateTagDto) { |
| return this._tags.model.tags.update({ |
| where: { |
| id, |
| }, |
| data: { |
| name: body.name, |
| color: body.color, |
| }, |
| }); |
| } |
|
|
| deleteTag(id: string, orgId: string) { |
| return this._tags.model.tags.update({ |
| where: { |
| id, |
| orgId, |
| }, |
| data: { |
| deletedAt: new Date(), |
| }, |
| }); |
| } |
|
|
| createComment( |
| orgId: string, |
| userId: string, |
| postId: string, |
| content: string |
| ) { |
| return this._comments.model.comments.create({ |
| data: { |
| organizationId: orgId, |
| userId, |
| postId, |
| content, |
| }, |
| }); |
| } |
|
|
| async getPostByForWebhookId(postId: string) { |
| return this._post.model.post.findMany({ |
| where: { |
| id: postId, |
| deletedAt: null, |
| parentPostId: null, |
| }, |
| select: { |
| id: true, |
| content: true, |
| publishDate: true, |
| releaseURL: true, |
| state: true, |
| integration: { |
| select: { |
| id: true, |
| name: true, |
| providerIdentifier: true, |
| picture: true, |
| type: true, |
| }, |
| }, |
| }, |
| }); |
| } |
|
|
| async getPostsSince(orgId: string, since: string) { |
| return this._post.model.post.findMany({ |
| where: { |
| organizationId: orgId, |
| publishDate: { |
| gte: new Date(since), |
| }, |
| deletedAt: null, |
| parentPostId: null, |
| }, |
| select: { |
| id: true, |
| content: true, |
| publishDate: true, |
| releaseURL: true, |
| state: true, |
| integration: { |
| select: { |
| id: true, |
| name: true, |
| providerIdentifier: true, |
| picture: true, |
| type: true, |
| }, |
| }, |
| }, |
| }); |
| } |
| } |
|
|