mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-16 01:26:37 +08:00
feat(server): introduce user friendly server errors (#7111)
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import { BadGatewayException, ForbiddenException } from '@nestjs/common';
|
||||
import {
|
||||
Args,
|
||||
Context,
|
||||
@@ -19,7 +18,12 @@ import { groupBy } from 'lodash-es';
|
||||
|
||||
import { CurrentUser, Public } from '../../core/auth';
|
||||
import { UserType } from '../../core/user';
|
||||
import { Config, URLHelper } from '../../fundamentals';
|
||||
import {
|
||||
AccessDenied,
|
||||
Config,
|
||||
FailedToCheckout,
|
||||
URLHelper,
|
||||
} from '../../fundamentals';
|
||||
import { decodeLookupKey, SubscriptionService } from './service';
|
||||
import {
|
||||
InvoiceStatus,
|
||||
@@ -227,7 +231,7 @@ export class SubscriptionResolver {
|
||||
});
|
||||
|
||||
if (!session.url) {
|
||||
throw new BadGatewayException('Failed to create checkout session.');
|
||||
throw new FailedToCheckout();
|
||||
}
|
||||
|
||||
return session.url;
|
||||
@@ -322,9 +326,7 @@ export class UserSubscriptionResolver {
|
||||
) {
|
||||
// allow admin to query other user's subscription
|
||||
if (!ctx.isAdminQuery && me.id !== user.id) {
|
||||
throw new ForbiddenException(
|
||||
'You are not allowed to access this subscription.'
|
||||
);
|
||||
throw new AccessDenied();
|
||||
}
|
||||
|
||||
// @FIXME(@forehalo): should not mock any api for selfhosted server
|
||||
@@ -363,9 +365,7 @@ export class UserSubscriptionResolver {
|
||||
@Parent() user: User
|
||||
): Promise<UserSubscription[]> {
|
||||
if (me.id !== user.id) {
|
||||
throw new ForbiddenException(
|
||||
'You are not allowed to access this subscription.'
|
||||
);
|
||||
throw new AccessDenied();
|
||||
}
|
||||
|
||||
return this.db.userSubscription.findMany({
|
||||
@@ -385,9 +385,7 @@ export class UserSubscriptionResolver {
|
||||
@Args('skip', { type: () => Int, nullable: true }) skip?: number
|
||||
) {
|
||||
if (me.id !== user.id) {
|
||||
throw new ForbiddenException(
|
||||
'You are not allowed to access this invoices'
|
||||
);
|
||||
throw new AccessDenied();
|
||||
}
|
||||
|
||||
return this.db.userInvoice.findMany({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { randomUUID } from 'node:crypto';
|
||||
|
||||
import { BadRequestException, Injectable, Logger } from '@nestjs/common';
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { OnEvent as RawOnEvent } from '@nestjs/event-emitter';
|
||||
import type {
|
||||
Prisma,
|
||||
@@ -14,7 +14,20 @@ import Stripe from 'stripe';
|
||||
|
||||
import { CurrentUser } from '../../core/auth';
|
||||
import { EarlyAccessType, FeatureManagementService } from '../../core/features';
|
||||
import { Config, EventEmitter, OnEvent } from '../../fundamentals';
|
||||
import {
|
||||
ActionForbidden,
|
||||
Config,
|
||||
CustomerPortalCreateFailed,
|
||||
EventEmitter,
|
||||
OnEvent,
|
||||
SameSubscriptionRecurring,
|
||||
SubscriptionAlreadyExists,
|
||||
SubscriptionExpired,
|
||||
SubscriptionHasBeenCanceled,
|
||||
SubscriptionNotExists,
|
||||
SubscriptionPlanNotFound,
|
||||
UserNotFound,
|
||||
} from '../../fundamentals';
|
||||
import { ScheduleManager } from './schedule';
|
||||
import {
|
||||
InvoiceStatus,
|
||||
@@ -160,7 +173,7 @@ export class SubscriptionService {
|
||||
this.config.affine.canary &&
|
||||
!this.features.isStaff(user.email)
|
||||
) {
|
||||
throw new BadRequestException('You are not allowed to do this.');
|
||||
throw new ActionForbidden();
|
||||
}
|
||||
|
||||
const currentSubscription = await this.db.userSubscription.findFirst({
|
||||
@@ -172,9 +185,7 @@ export class SubscriptionService {
|
||||
});
|
||||
|
||||
if (currentSubscription) {
|
||||
throw new BadRequestException(
|
||||
`You've already subscribed to the ${plan} plan`
|
||||
);
|
||||
throw new SubscriptionAlreadyExists({ plan });
|
||||
}
|
||||
|
||||
const customer = await this.getOrCreateCustomer(
|
||||
@@ -245,18 +256,16 @@ export class SubscriptionService {
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new BadRequestException('Unknown user');
|
||||
throw new UserNotFound();
|
||||
}
|
||||
|
||||
const subscriptionInDB = user?.subscriptions.find(s => s.plan === plan);
|
||||
if (!subscriptionInDB) {
|
||||
throw new BadRequestException(`You didn't subscribe to the ${plan} plan`);
|
||||
throw new SubscriptionNotExists({ plan });
|
||||
}
|
||||
|
||||
if (subscriptionInDB.canceledAt) {
|
||||
throw new BadRequestException(
|
||||
'Your subscription has already been canceled'
|
||||
);
|
||||
throw new SubscriptionHasBeenCanceled();
|
||||
}
|
||||
|
||||
// should release the schedule first
|
||||
@@ -298,22 +307,20 @@ export class SubscriptionService {
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new BadRequestException('Unknown user');
|
||||
throw new UserNotFound();
|
||||
}
|
||||
|
||||
const subscriptionInDB = user?.subscriptions.find(s => s.plan === plan);
|
||||
if (!subscriptionInDB) {
|
||||
throw new BadRequestException(`You didn't subscribe to the ${plan} plan`);
|
||||
throw new SubscriptionNotExists({ plan });
|
||||
}
|
||||
|
||||
if (!subscriptionInDB.canceledAt) {
|
||||
throw new BadRequestException('Your subscription has not been canceled');
|
||||
throw new SubscriptionHasBeenCanceled();
|
||||
}
|
||||
|
||||
if (subscriptionInDB.end < new Date()) {
|
||||
throw new BadRequestException(
|
||||
'Your subscription is expired, please checkout again.'
|
||||
);
|
||||
throw new SubscriptionExpired();
|
||||
}
|
||||
|
||||
if (subscriptionInDB.stripeScheduleId) {
|
||||
@@ -354,23 +361,19 @@ export class SubscriptionService {
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new BadRequestException('Unknown user');
|
||||
throw new UserNotFound();
|
||||
}
|
||||
const subscriptionInDB = user?.subscriptions.find(s => s.plan === plan);
|
||||
if (!subscriptionInDB) {
|
||||
throw new BadRequestException(`You didn't subscribe to the ${plan} plan`);
|
||||
throw new SubscriptionNotExists({ plan });
|
||||
}
|
||||
|
||||
if (subscriptionInDB.canceledAt) {
|
||||
throw new BadRequestException(
|
||||
'Your subscription has already been canceled'
|
||||
);
|
||||
throw new SubscriptionHasBeenCanceled();
|
||||
}
|
||||
|
||||
if (subscriptionInDB.recurring === recurring) {
|
||||
throw new BadRequestException(
|
||||
`You are already in ${recurring} recurring`
|
||||
);
|
||||
throw new SameSubscriptionRecurring({ recurring });
|
||||
}
|
||||
|
||||
const price = await this.getPrice(
|
||||
@@ -404,7 +407,7 @@ export class SubscriptionService {
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new BadRequestException('Unknown user');
|
||||
throw new UserNotFound();
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -415,7 +418,7 @@ export class SubscriptionService {
|
||||
return portal.url;
|
||||
} catch (e) {
|
||||
this.logger.error('Failed to create customer portal.', e);
|
||||
throw new BadRequestException('Failed to create customer portal');
|
||||
throw new CustomerPortalCreateFailed();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -751,9 +754,10 @@ export class SubscriptionService {
|
||||
});
|
||||
|
||||
if (!prices.data.length) {
|
||||
throw new BadRequestException(
|
||||
`Unknown subscription plan ${plan} with ${recurring} recurring`
|
||||
);
|
||||
throw new SubscriptionPlanNotFound({
|
||||
plan,
|
||||
recurring,
|
||||
});
|
||||
}
|
||||
|
||||
return prices.data[0].id;
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
import assert from 'node:assert';
|
||||
|
||||
import type { RawBodyRequest } from '@nestjs/common';
|
||||
import {
|
||||
Controller,
|
||||
Logger,
|
||||
NotAcceptableException,
|
||||
Post,
|
||||
Req,
|
||||
} from '@nestjs/common';
|
||||
import { Controller, Logger, Post, Req } from '@nestjs/common';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import type { Request } from 'express';
|
||||
import Stripe from 'stripe';
|
||||
|
||||
import { Public } from '../../core/auth';
|
||||
import { Config } from '../../fundamentals';
|
||||
import { Config, InternalServerError } from '../../fundamentals';
|
||||
|
||||
@Controller('/api/stripe')
|
||||
export class StripeWebhook {
|
||||
@@ -55,9 +49,8 @@ export class StripeWebhook {
|
||||
this.logger.error('Failed to handle Stripe Webhook event.', e);
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
this.logger.error('Stripe Webhook error', err);
|
||||
throw new NotAcceptableException();
|
||||
} catch (err: any) {
|
||||
throw new InternalServerError(err.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user