improvement: Nothing done is related to subscriptions!!!

This commit is contained in:
2026-09-05 20:44:16 +03:30
parent 5cd3436d0c
commit 1ca8e6178e
13 changed files with 431 additions and 57 deletions

View File

@@ -49,7 +49,7 @@ export class StaffService {
this.prisma.membership.findMany({
where: { organizationId },
include: {
user: { select: { id: true, email: true, name: true } },
user: { select: { id: true, email: true, name: true, passwordHash: true } },
permissions: { include: { permission: true } },
invitations: {
orderBy: { createdAt: 'desc' },
@@ -80,6 +80,7 @@ export class StaffService {
isOwner: m.isOwner,
isActive: m.isOwner ? true : m.isActive,
invitationStatus: this.getInvitationStatus(m),
hasPassword: Boolean(m.user.passwordHash),
invitedAt: m.invitations[0]?.createdAt?.toISOString() || null,
acceptedAt: m.invitations[0]?.acceptedAt?.toISOString() || null,
permissions: m.isOwner
@@ -310,6 +311,77 @@ export class StaffService {
organizationName: org.name,
expiresAt: invitation.expiresAt.toISOString(),
status: invitation.acceptedAt ? 'ACCEPTED' : 'PENDING',
mode: invitation.membership.isActive ? 'password_setup' : 'join',
},
};
}
async clearPassword(
actorUserId: string,
organizationId: string,
membershipId: string,
) {
const actor = await this.getActorMembership(actorUserId, organizationId);
if (!actor || !this.canEditStaff(actor)) {
throw new AppException(ErrorCode.PERMISSION_EDIT_STAFF, HttpStatus.FORBIDDEN);
}
const membership = await this.prisma.membership.findFirst({
where: { id: membershipId, organizationId },
include: {
user: { select: { id: true, email: true } },
},
});
if (!membership) {
throw new AppException(ErrorCode.STAFF_MEMBER_NOT_FOUND, HttpStatus.NOT_FOUND);
}
if (membership.isOwner) {
throw new AppException(ErrorCode.STAFF_CANNOT_EDIT_OWNER, HttpStatus.FORBIDDEN);
}
if (membership.userId === actorUserId) {
throw new AppException(ErrorCode.STAFF_CANNOT_CLEAR_OWN_PASSWORD, HttpStatus.BAD_REQUEST);
}
if (!membership.isActive) {
throw new AppException(ErrorCode.STAFF_PASSWORD_CLEAR_ACTIVE_ONLY, HttpStatus.BAD_REQUEST);
}
const plainToken = this.generateInviteToken();
const tokenHash = this.hashInviteToken(plainToken);
const invitation = await this.prisma.$transaction(async (tx) => {
await tx.user.update({
where: { id: membership.userId },
data: { passwordHash: null },
});
await tx.session.deleteMany({
where: { userId: membership.userId },
});
await tx.staffInvitation.updateMany({
where: {
membershipId: membership.id,
acceptedAt: null,
revokedAt: null,
},
data: { revokedAt: new Date() },
});
return tx.staffInvitation.create({
data: {
membershipId: membership.id,
invitedById: actorUserId,
tokenHash,
expiresAt: this.getInviteExpiryDate(),
},
});
});
return {
success: true,
data: {
membershipId: membership.id,
invitationId: invitation.id,
email: membership.user.email,
invitationUrl: this.buildInviteUrl(plainToken),
},
};
}