fix(server): dirty data handle (#15034)

#### PR Dependency Tree


* **PR #15034** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

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

* **Refactor**
* Consolidated subscription visibility and “active” selection logic so
all subscription queries use a shared, consistent filter across the
platform.

* **Tests**
* Added a test to ensure expired subscriptions are excluded from active
subscription results.
* Updated test fixtures to differentiate expired, unexpired, and onetime
subscriptions for more accurate coverage.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/toeverything/AFFiNE/pull/15034?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
DarkSky
2026-05-28 15:20:17 +08:00
committed by GitHub
parent 66a6a5fffc
commit 1d08e1d8c0
7 changed files with 102 additions and 49 deletions
@@ -1,4 +1,4 @@
import { PrismaClient, UserStripeCustomer } from '@prisma/client';
import { type Prisma, PrismaClient, UserStripeCustomer } from '@prisma/client';
import Stripe from 'stripe';
import { z } from 'zod';
@@ -13,9 +13,40 @@ import {
LookupKey,
SubscriptionPlan,
SubscriptionRecurring,
SubscriptionStatus,
SubscriptionVariant,
} from '../types';
export function validSubscriptionPeriodWhere(
now = new Date()
): Prisma.SubscriptionWhereInput {
return { OR: [{ end: null }, { end: { gt: now } }] };
}
export function activeSubscriptionWhere(
now = new Date()
): Prisma.SubscriptionWhereInput {
return {
status: { in: [SubscriptionStatus.Active, SubscriptionStatus.Trialing] },
...validSubscriptionPeriodWhere(now),
};
}
export function visibleSubscriptionWhere(
now = new Date()
): Prisma.SubscriptionWhereInput {
return {
status: {
in: [
SubscriptionStatus.Active,
SubscriptionStatus.Trialing,
SubscriptionStatus.PastDue,
],
},
...validSubscriptionPeriodWhere(now),
};
}
export interface Subscription {
stripeSubscriptionId: string | null;
stripeScheduleId: string | null;
@@ -15,9 +15,9 @@ import {
LookupKey,
SubscriptionPlan,
SubscriptionRecurring,
SubscriptionStatus,
} from '../types';
import {
activeSubscriptionWhere,
CheckoutParams,
Invoice,
Subscription,
@@ -199,9 +199,7 @@ export class SelfhostTeamSubscriptionManager extends SubscriptionManager {
where: {
targetId: identity.key,
plan: identity.plan,
status: {
in: [SubscriptionStatus.Active, SubscriptionStatus.Trialing],
},
...activeSubscriptionWhere(),
},
});
}
@@ -34,7 +34,12 @@ import {
SubscriptionStatus,
SubscriptionVariant,
} from '../types';
import { CheckoutParams, Subscription, SubscriptionManager } from './common';
import {
activeSubscriptionWhere,
CheckoutParams,
Subscription,
SubscriptionManager,
} from './common';
interface PriceStrategyStatus {
proEarlyAccess: boolean;
@@ -224,9 +229,7 @@ export class UserSubscriptionManager extends SubscriptionManager {
where: {
targetId: args.userId,
plan: args.plan,
status: {
in: [SubscriptionStatus.Active, SubscriptionStatus.Trialing],
},
...activeSubscriptionWhere(),
},
});
}
@@ -24,6 +24,7 @@ import {
SubscriptionStatus,
} from '../types';
import {
activeSubscriptionWhere,
CheckoutParams,
Invoice,
Subscription,
@@ -225,9 +226,7 @@ export class WorkspaceSubscriptionManager extends SubscriptionManager {
return this.db.subscription.findFirst({
where: {
targetId: identity.workspaceId,
status: {
in: [SubscriptionStatus.Active, SubscriptionStatus.Trialing],
},
...activeSubscriptionWhere(),
},
});
}
@@ -30,7 +30,12 @@ import { CurrentUser, Public } from '../../core/auth';
import { PermissionAccess } from '../../core/permission';
import { UserType } from '../../core/user';
import { WorkspaceType } from '../../core/workspaces';
import { Invoice, Subscription, WorkspaceSubscriptionManager } from './manager';
import {
Invoice,
Subscription,
visibleSubscriptionWhere,
WorkspaceSubscriptionManager,
} from './manager';
import { RevenueCatWebhookHandler } from './revenuecat';
import { CheckoutParams, SubscriptionService } from './service';
import {
@@ -493,13 +498,7 @@ export class UserSubscriptionResolver {
const subscriptions = await this.db.subscription.findMany({
where: {
targetId: user.id,
status: {
in: [
SubscriptionStatus.Active,
SubscriptionStatus.Trialing,
SubscriptionStatus.PastDue,
],
},
...visibleSubscriptionWhere(),
},
});
@@ -577,13 +576,7 @@ export class UserSubscriptionResolver {
current = await this.db.subscription.findMany({
where: {
targetId: user.id,
status: {
in: [
SubscriptionStatus.Active,
SubscriptionStatus.Trialing,
SubscriptionStatus.PastDue,
],
},
...visibleSubscriptionWhere(),
},
});
// ignore errors
@@ -608,13 +601,7 @@ export class UserSubscriptionResolver {
let current = await this.db.subscription.findMany({
where: {
targetId: user.id,
status: {
in: [
SubscriptionStatus.Active,
SubscriptionStatus.Trialing,
SubscriptionStatus.PastDue,
],
},
...visibleSubscriptionWhere(),
},
});
@@ -641,13 +628,7 @@ export class UserSubscriptionResolver {
current = await this.db.subscription.findMany({
where: {
targetId: user.id,
status: {
in: [
SubscriptionStatus.Active,
SubscriptionStatus.Trialing,
SubscriptionStatus.PastDue,
],
},
...visibleSubscriptionWhere(),
},
});
// ignore errors