mirror of
https://github.com/docmost/docmost.git
synced 2025-11-14 07:51:09 +10:00
feat: billing sync (cloud) (#899)
* Set page history to 5 minutes interval * * Configure default queue options * sync * * stripe seats sync (cloud)
This commit is contained in:
@ -48,7 +48,7 @@
|
|||||||
"@nestjs/platform-socket.io": "^11.0.10",
|
"@nestjs/platform-socket.io": "^11.0.10",
|
||||||
"@nestjs/terminus": "^11.0.0",
|
"@nestjs/terminus": "^11.0.0",
|
||||||
"@nestjs/websockets": "^11.0.10",
|
"@nestjs/websockets": "^11.0.10",
|
||||||
"@node-saml/passport-saml": "^5.0.0",
|
"@node-saml/passport-saml": "^5.0.1",
|
||||||
"@react-email/components": "0.0.28",
|
"@react-email/components": "0.0.28",
|
||||||
"@react-email/render": "1.0.2",
|
"@react-email/render": "1.0.2",
|
||||||
"@socket.io/redis-adapter": "^8.3.0",
|
"@socket.io/redis-adapter": "^8.3.0",
|
||||||
|
|||||||
@ -20,9 +20,9 @@ export class HistoryListener {
|
|||||||
|
|
||||||
const pageCreationTime = new Date(page.createdAt).getTime();
|
const pageCreationTime = new Date(page.createdAt).getTime();
|
||||||
const currentTime = Date.now();
|
const currentTime = Date.now();
|
||||||
const TEN_MINUTES = 10 * 60 * 1000;
|
const FIVE_MINUTES = 5 * 60 * 1000;
|
||||||
|
|
||||||
if (currentTime - pageCreationTime < TEN_MINUTES) {
|
if (currentTime - pageCreationTime < FIVE_MINUTES) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,13 +31,13 @@ export class HistoryListener {
|
|||||||
if (
|
if (
|
||||||
!lastHistory ||
|
!lastHistory ||
|
||||||
(!isDeepStrictEqual(lastHistory.content, page.content) &&
|
(!isDeepStrictEqual(lastHistory.content, page.content) &&
|
||||||
currentTime - new Date(lastHistory.createdAt).getTime() >= TEN_MINUTES)
|
currentTime - new Date(lastHistory.createdAt).getTime() >= FIVE_MINUTES)
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
await this.pageHistoryRepo.saveHistory(page);
|
await this.pageHistoryRepo.saveHistory(page);
|
||||||
this.logger.debug(`New history created for: ${page.id}`);
|
this.logger.debug(`New history created for: ${page.id}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.logger.error(`Failed to create history for: ${page.id}`, err);
|
this.logger.error(`Failed to create history for page: ${page.id}`, err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,6 +24,10 @@ import { nanoIdGen } from '../../../common/helpers';
|
|||||||
import { PaginationOptions } from '@docmost/db/pagination/pagination-options';
|
import { PaginationOptions } from '@docmost/db/pagination/pagination-options';
|
||||||
import { executeWithPagination } from '@docmost/db/pagination/pagination';
|
import { executeWithPagination } from '@docmost/db/pagination/pagination';
|
||||||
import { DomainService } from 'src/integrations/environment/domain.service';
|
import { DomainService } from 'src/integrations/environment/domain.service';
|
||||||
|
import { InjectQueue } from '@nestjs/bullmq';
|
||||||
|
import { QueueJob, QueueName } from '../../../integrations/queue/constants';
|
||||||
|
import { Queue } from 'bullmq';
|
||||||
|
import { EnvironmentService } from '../../../integrations/environment/environment.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class WorkspaceInvitationService {
|
export class WorkspaceInvitationService {
|
||||||
@ -35,6 +39,8 @@ export class WorkspaceInvitationService {
|
|||||||
private domainService: DomainService,
|
private domainService: DomainService,
|
||||||
private tokenService: TokenService,
|
private tokenService: TokenService,
|
||||||
@InjectKysely() private readonly db: KyselyDB,
|
@InjectKysely() private readonly db: KyselyDB,
|
||||||
|
@InjectQueue(QueueName.BILLING_QUEUE) private billingQueue: Queue,
|
||||||
|
private readonly environmentService: EnvironmentService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async getInvitations(workspaceId: string, pagination: PaginationOptions) {
|
async getInvitations(workspaceId: string, pagination: PaginationOptions) {
|
||||||
@ -266,6 +272,10 @@ export class WorkspaceInvitationService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.environmentService.isCloud()) {
|
||||||
|
await this.billingQueue.add(QueueJob.STRIPE_SEATS_SYNC, { workspaceId });
|
||||||
|
}
|
||||||
|
|
||||||
return this.tokenService.generateAccessToken(newUser);
|
return this.tokenService.generateAccessToken(newUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Submodule apps/server/src/ee updated: db81c76c70...337854ce96
@ -2,6 +2,7 @@ export enum QueueName {
|
|||||||
EMAIL_QUEUE = '{email-queue}',
|
EMAIL_QUEUE = '{email-queue}',
|
||||||
ATTACHMENT_QUEUE = '{attachment-queue}',
|
ATTACHMENT_QUEUE = '{attachment-queue}',
|
||||||
GENERAL_QUEUE = '{general-queue}',
|
GENERAL_QUEUE = '{general-queue}',
|
||||||
|
BILLING_QUEUE = '{billing-queue}',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum QueueJob {
|
export enum QueueJob {
|
||||||
@ -11,6 +12,6 @@ export enum QueueJob {
|
|||||||
PAGE_CONTENT_UPDATE = 'page-content-update',
|
PAGE_CONTENT_UPDATE = 'page-content-update',
|
||||||
|
|
||||||
PAGE_BACKLINKS = 'page-backlinks',
|
PAGE_BACKLINKS = 'page-backlinks',
|
||||||
|
|
||||||
|
STRIPE_SEATS_SYNC = 'sync-stripe-seats',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -6,3 +6,7 @@ export interface IPageBacklinkJob {
|
|||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
mentions: MentionNode[];
|
mentions: MentionNode[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IStripeSeatsSyncJob {
|
||||||
|
workspaceId: string;
|
||||||
|
}
|
||||||
@ -106,20 +106,26 @@ export class BacklinksProcessor extends WorkerHost implements OnModuleDestroy {
|
|||||||
|
|
||||||
@OnWorkerEvent('active')
|
@OnWorkerEvent('active')
|
||||||
onActive(job: Job) {
|
onActive(job: Job) {
|
||||||
|
if (job.name === QueueJob.PAGE_BACKLINKS) {
|
||||||
this.logger.debug(`Processing ${job.name} job`);
|
this.logger.debug(`Processing ${job.name} job`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@OnWorkerEvent('failed')
|
@OnWorkerEvent('failed')
|
||||||
onError(job: Job) {
|
onError(job: Job) {
|
||||||
|
if (job.name === QueueJob.PAGE_BACKLINKS) {
|
||||||
this.logger.error(
|
this.logger.error(
|
||||||
`Error processing ${job.name} job. Reason: ${job.failedReason}`,
|
`Error processing ${job.name} job. Reason: ${job.failedReason}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@OnWorkerEvent('completed')
|
@OnWorkerEvent('completed')
|
||||||
onCompleted(job: Job) {
|
onCompleted(job: Job) {
|
||||||
|
if (job.name === QueueJob.PAGE_BACKLINKS) {
|
||||||
this.logger.debug(`Completed ${job.name} job`);
|
this.logger.debug(`Completed ${job.name} job`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async onModuleDestroy(): Promise<void> {
|
async onModuleDestroy(): Promise<void> {
|
||||||
if (this.worker) {
|
if (this.worker) {
|
||||||
|
|||||||
@ -24,7 +24,13 @@ import { BacklinksProcessor } from './processors/backlinks.processor';
|
|||||||
attempts: 3,
|
attempts: 3,
|
||||||
backoff: {
|
backoff: {
|
||||||
type: 'exponential',
|
type: 'exponential',
|
||||||
delay: 10000,
|
delay: 20 * 1000,
|
||||||
|
},
|
||||||
|
removeOnComplete: {
|
||||||
|
count: 200,
|
||||||
|
},
|
||||||
|
removeOnFail: {
|
||||||
|
count: 100,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -40,6 +46,9 @@ import { BacklinksProcessor } from './processors/backlinks.processor';
|
|||||||
BullModule.registerQueue({
|
BullModule.registerQueue({
|
||||||
name: QueueName.GENERAL_QUEUE,
|
name: QueueName.GENERAL_QUEUE,
|
||||||
}),
|
}),
|
||||||
|
BullModule.registerQueue({
|
||||||
|
name: QueueName.BILLING_QUEUE,
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
exports: [BullModule],
|
exports: [BullModule],
|
||||||
providers: [BacklinksProcessor],
|
providers: [BacklinksProcessor],
|
||||||
|
|||||||
Reference in New Issue
Block a user