fix: stuff

This commit is contained in:
David Nguyen
2026-06-19 14:39:08 +10:00
parent 70cdef5a6d
commit d92aa6ee93
5 changed files with 138 additions and 29 deletions
+2
View File
@@ -14,6 +14,7 @@ import { SEND_SIGNING_REJECTION_EMAILS_JOB_DEFINITION } from './definitions/emai
import { SEND_SIGNING_EMAIL_JOB_DEFINITION } from './definitions/emails/send-signing-email';
import { SEND_TEAM_DELETED_EMAIL_JOB_DEFINITION } from './definitions/emails/send-team-deleted-email';
import { ADMIN_DELETE_ORGANISATION_JOB_DEFINITION } from './definitions/internal/admin-delete-organisation';
import { ALERT_ORGANISATION_SEAT_DRIFT_JOB_DEFINITION } from './definitions/internal/alert-organisation-seat-drift';
import { BACKPORT_SUBSCRIPTION_CLAIM_JOB_DEFINITION } from './definitions/internal/backport-subscription-claims';
import { BULK_SEND_TEMPLATE_JOB_DEFINITION } from './definitions/internal/bulk-send-template';
import { CANCEL_ORGANISATION_SUBSCRIPTION_JOB_DEFINITION } from './definitions/internal/cancel-organisation-subscription';
@@ -59,6 +60,7 @@ export const jobsClient = new JobClient([
CLEANUP_RATE_LIMITS_JOB_DEFINITION,
SYNC_EMAIL_DOMAINS_JOB_DEFINITION,
ADMIN_DELETE_ORGANISATION_JOB_DEFINITION,
ALERT_ORGANISATION_SEAT_DRIFT_JOB_DEFINITION,
CANCEL_ORGANISATION_SUBSCRIPTION_JOB_DEFINITION,
SYNC_ORGANISATION_SEATS_JOB_DEFINITION,
] as const);
@@ -0,0 +1,67 @@
import { mailer } from '@documenso/email/mailer';
import { prisma } from '@documenso/prisma';
import { IS_BILLING_ENABLED, SUPPORT_EMAIL } from '../../../constants/app';
import { DOCUMENSO_INTERNAL_EMAIL } from '../../../constants/email';
import type { JobRunIO } from '../../client/_internal/job';
import type { TAlertOrganisationSeatDriftJobDefinition } from './alert-organisation-seat-drift';
/**
* Daily check for organisations whose member count exceeds their paid seat
* count (`organisationClaim.memberCount`, where `0` means unlimited).
*/
export const run = async ({ io }: { payload: TAlertOrganisationSeatDriftJobDefinition; io: JobRunIO }) => {
if (!IS_BILLING_ENABLED()) {
return;
}
const organisations = await prisma.organisation.findMany({
where: {
// Exclude unlimited-seat plans (memberCount === 0).
organisationClaim: {
memberCount: {
not: 0,
},
},
},
select: {
id: true,
name: true,
organisationClaim: {
select: {
memberCount: true,
},
},
_count: {
select: {
members: true,
},
},
},
});
const driftedOrganisations = organisations.filter(
(organisation) =>
organisation.organisationClaim !== null &&
organisation._count.members > organisation.organisationClaim.memberCount,
);
if (driftedOrganisations.length === 0) {
io.logger.info('No organisations exceed their paid seat count');
return;
}
await mailer.sendMail({
to: SUPPORT_EMAIL,
from: DOCUMENSO_INTERNAL_EMAIL,
subject: `[Billing] ${driftedOrganisations.length} organisation(s) exceed their paid seat count`,
text: [
`${driftedOrganisations.length} organisation(s) have more members than their paid seat count:`,
'',
...driftedOrganisations.map(
(organisation) =>
`- ${organisation.name} (${organisation.id}): ${organisation._count.members} members vs ${organisation.organisationClaim?.memberCount ?? 0} paid seats`,
),
].join('\n'),
});
};
@@ -0,0 +1,30 @@
import { z } from 'zod';
import type { JobDefinition } from '../../client/_internal/job';
const ALERT_ORGANISATION_SEAT_DRIFT_JOB_DEFINITION_ID = 'internal.alert-organisation-seat-drift';
const ALERT_ORGANISATION_SEAT_DRIFT_JOB_DEFINITION_SCHEMA = z.object({});
export type TAlertOrganisationSeatDriftJobDefinition = z.infer<
typeof ALERT_ORGANISATION_SEAT_DRIFT_JOB_DEFINITION_SCHEMA
>;
export const ALERT_ORGANISATION_SEAT_DRIFT_JOB_DEFINITION = {
id: ALERT_ORGANISATION_SEAT_DRIFT_JOB_DEFINITION_ID,
name: 'Alert Organisation Seat Drift',
version: '1.0.0',
trigger: {
name: ALERT_ORGANISATION_SEAT_DRIFT_JOB_DEFINITION_ID,
schema: ALERT_ORGANISATION_SEAT_DRIFT_JOB_DEFINITION_SCHEMA,
cron: '0 0 * * *', // Once a day at midnight.
},
handler: async ({ payload, io }) => {
const handler = await import('./alert-organisation-seat-drift.handler');
await handler.run({ payload, io });
},
} as const satisfies JobDefinition<
typeof ALERT_ORGANISATION_SEAT_DRIFT_JOB_DEFINITION_ID,
TAlertOrganisationSeatDriftJobDefinition
>;