feat(server): align pro plan for free in selfhost (#9973)

close AF-2099
This commit is contained in:
forehalo
2025-02-06 05:25:06 +00:00
parent f00fb327e2
commit d873a78534
6 changed files with 109 additions and 43 deletions

View File

@@ -42,3 +42,16 @@ Generated by [AVA](https://avajs.dev).
name: 'Free',
storageQuota: 10737418240,
}
## should use pro plan as free for selfhost instance
> use pro plan as free plan for selfhosted instance
{
blobLimit: 104857600,
copilotActionLimit: 10,
historyPeriod: 2592000000,
memberLimit: 10,
name: 'Pro',
storageQuota: 107374182400,
}

View File

@@ -1,7 +1,8 @@
import { User } from '@prisma/client';
import ava, { TestFn } from 'ava';
import { FeatureType, UserFeatureModel, UserModel } from '../../models';
import { ConfigModule } from '../../base/config';
import { FeatureType, Models, UserFeatureModel, UserModel } from '../../models';
import { createTestingModule, TestingModule } from '../utils';
interface Context {
@@ -123,3 +124,25 @@ test('should not switch user quota if the new quota is the same as the current o
t.not(quota?.reason, 'test not switch');
});
test('should use pro plan as free for selfhost instance', async t => {
await using module = await createTestingModule({
imports: [
ConfigModule.forRoot({
isSelfhosted: true,
}),
],
});
const models = module.get(Models);
const u1 = await models.user.create({
email: 'u1@affine.pro',
registered: true,
});
const quota = await models.userFeature.getQuota(u1.id);
t.snapshot(
quota?.configs,
'use pro plan as free plan for selfhosted instance'
);
});

View File

@@ -90,6 +90,7 @@ test('should get feature if extra fields exist in feature config', async t => {
test('should create feature', async t => {
const { feature } = t.context;
// @ts-expect-error internal
const newFeature = await feature.upsert(
'new_feature' as any,
{},
@@ -104,6 +105,7 @@ test('should update feature', async t => {
const { feature } = t.context;
const freePlanFeature = await feature.get('free_plan_v1');
// @ts-expect-error internal
const newFreePlanFeature = await feature.upsert(
'free_plan_v1',
{
@@ -123,6 +125,7 @@ test('should update feature', async t => {
test('should throw if feature config is invalid when updating', async t => {
const { feature } = t.context;
await t.throwsAsync(
// @ts-expect-error internal
feature.upsert('free_plan_v1', {} as any, FeatureType.Quota, 1),
{
message: 'Invalid feature config for free_plan_v1',

View File

@@ -52,10 +52,12 @@ const initTestingDB = async (ref: ModuleRef) => {
export type TestingModule = BaseTestingModule & {
initTestingDB(): Promise<void>;
[Symbol.asyncDispose](): Promise<void>;
};
export type TestingApp = INestApplication & {
initTestingDB(): Promise<void>;
[Symbol.asyncDispose](): Promise<void>;
};
function dedupeModules(modules: NonNullable<ModuleMetadata['imports']>) {
@@ -83,7 +85,7 @@ class MockResolver {
export async function createTestingModule(
moduleDef: TestingModuleMeatdata = {},
autoInitialize = true
) {
): Promise<TestingModule> {
// setting up
let imports = moduleDef.imports ?? [];
imports =
@@ -129,6 +131,9 @@ export async function createTestingModule(
// by pass password min length validation
await runtime.set('auth/password.min', 1);
};
testingModule[Symbol.asyncDispose] = async () => {
await m.close();
};
if (autoInitialize) {
await testingModule.initTestingDB();
@@ -138,7 +143,9 @@ export async function createTestingModule(
return testingModule;
}
export async function createTestingApp(moduleDef: TestingModuleMeatdata = {}) {
export async function createTestingApp(
moduleDef: TestingModuleMeatdata = {}
): Promise<{ module: TestingModule; app: TestingApp }> {
const m = await createTestingModule(moduleDef, false);
const app = m.createNestApplication({
@@ -169,7 +176,10 @@ export async function createTestingApp(moduleDef: TestingModuleMeatdata = {}) {
await app.init();
app.initTestingDB = m.initTestingDB.bind(m);
app[Symbol.asyncDispose] = async () => {
await m[Symbol.asyncDispose]();
await app.close();
};
return {
module: m,
app: app,