feat(server): handle account deleting properly (#12399)

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Users are now prevented from deleting their account if they own one or more team workspaces. A clear error message instructs users to transfer ownership or delete those workspaces first.
  - Disabled (banned) users are explicitly prevented from signing in or re-registering.
  - Added new error messages and translations to improve clarity around account deletion restrictions.

- **Bug Fixes**
  - Disabled users are now explicitly handled to prevent sign-in attempts.

- **Tests**
  - Introduced comprehensive end-to-end tests covering account deletion, banning, and re-registration scenarios.

- **Chores**
  - Improved event handling for user deletion and subscription cancellation.
  - Updated localization resources with new error messages.
  - Renamed payment event handler class for clarity.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
forehalo
2025-05-23 03:57:29 +00:00
parent f99b143bf9
commit f38b8fef4d
15 changed files with 235 additions and 23 deletions
@@ -2,7 +2,7 @@ import { faker } from '@faker-js/faker';
import { hashSync } from '@node-rs/argon2';
import type { Prisma, User } from '@prisma/client';
import type { UserFeatureName } from '../../models';
import { FeatureConfigs, type UserFeatureName } from '../../models';
import { Mocker } from './factory';
export type MockUserInput = Prisma.UserCreateInput & {
@@ -15,33 +15,37 @@ export type MockedUser = Omit<User, 'password'> & {
export class MockUser extends Mocker<MockUserInput, MockedUser> {
override async create(input?: Partial<MockUserInput>) {
const { feature, ...userInput } = input ?? {};
const password = input?.password ?? faker.internet.password();
const user = await this.db.user.create({
data: {
email: faker.internet.email(),
name: faker.person.fullName(),
password: password ? hashSync(password) : undefined,
...input,
...userInput,
},
});
if (input?.feature) {
const feature = await this.db.feature.findFirst({
if (feature) {
const featureRecord = await this.db.feature.findFirst({
where: {
name: input.feature,
name: feature,
},
});
if (!feature) {
if (!featureRecord) {
throw new Error(
`Feature ${input.feature} does not exist in DB. You might forgot to run data-migration first.`
`Feature ${feature} does not exist in DB. You might forgot to run data-migration first.`
);
}
const config = FeatureConfigs[feature];
await this.db.userFeature.create({
data: {
userId: user.id,
featureId: feature.id,
featureId: featureRecord.id,
name: feature,
type: config.type,
reason: 'test',
activated: true,
},