mirror of
https://github.com/Litlyx/litlyx
synced 2025-12-09 23:48:36 +01:00
add pricing
This commit is contained in:
66
shared/data/PREMIUM.ts
Normal file
66
shared/data/PREMIUM.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
|
||||
export type PREMIUM_TAG = typeof PREMIUM_TAGS[number];
|
||||
|
||||
export const PREMIUM_TAGS = [
|
||||
'FREE', 'PLAN_1', 'PLAN_2', 'PLAN_3', 'PLAN_99'
|
||||
] as const;
|
||||
|
||||
|
||||
export type PREMIUM_DATA = {
|
||||
COUNT_LIMIT: number,
|
||||
AI_MESSAGE_LIMIT: number,
|
||||
PRICE: string,
|
||||
ID: number
|
||||
}
|
||||
|
||||
export const PREMIUM_PLAN: Record<PREMIUM_TAG, PREMIUM_DATA> = {
|
||||
FREE: {
|
||||
ID: 0,
|
||||
COUNT_LIMIT: 3_000,
|
||||
AI_MESSAGE_LIMIT: 10,
|
||||
PRICE: 'price_1PNbHYB2lPUiVs9VZP32xglF'
|
||||
},
|
||||
PLAN_1: {
|
||||
ID: 1,
|
||||
COUNT_LIMIT: 150_000,
|
||||
AI_MESSAGE_LIMIT: 100,
|
||||
PRICE: 'price_1PNZjVB2lPUiVs9VrsTbJL04'
|
||||
},
|
||||
PLAN_2: {
|
||||
ID: 2,
|
||||
COUNT_LIMIT: 500_000,
|
||||
AI_MESSAGE_LIMIT: 5_000,
|
||||
PRICE: ''
|
||||
},
|
||||
PLAN_3: {
|
||||
ID: 3,
|
||||
COUNT_LIMIT: 2_000_000,
|
||||
AI_MESSAGE_LIMIT: 10_000,
|
||||
PRICE: ''
|
||||
},
|
||||
PLAN_99: {
|
||||
ID: 99,
|
||||
COUNT_LIMIT: 10_000_000,
|
||||
AI_MESSAGE_LIMIT: 100_000,
|
||||
PRICE: ''
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function getPlanFromTag(tag: PREMIUM_TAG) {
|
||||
return PREMIUM_PLAN[tag];
|
||||
}
|
||||
|
||||
export function getPlanFromId(id: number) {
|
||||
for (const tag of PREMIUM_TAGS) {
|
||||
const plan = getPlanFromTag(tag);
|
||||
if (plan.ID === id) return plan;
|
||||
}
|
||||
}
|
||||
|
||||
export function getPlanFromPrice(price: string) {
|
||||
for (const tag of PREMIUM_TAGS) {
|
||||
const plan = getPlanFromTag(tag);
|
||||
if (plan.PRICE === price) return plan;
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
|
||||
|
||||
export const PREMIUM_PLANS = [
|
||||
{ id: 0, tag: 'FREE', name: 'Free' },
|
||||
{ id: 1, tag: 'PLAN_1', name: 'Premium 1' },
|
||||
{ id: 2, tag: 'PLAN_2', name: 'Premium 2' },
|
||||
{ id: 3, tag: 'PLAN_3', name: 'Premium 3' },
|
||||
{ id: 99, tag: 'PLAN_99', name: 'Premium 99' },
|
||||
] as const;
|
||||
|
||||
export function getPlanFromPremiumType(premium_type?: number) {
|
||||
if (!premium_type) return PREMIUM_PLANS[0];
|
||||
const plan = PREMIUM_PLANS.find(e => e.id === premium_type);
|
||||
if (!plan) return PREMIUM_PLANS[0];
|
||||
return plan;
|
||||
}
|
||||
|
||||
export function getPlanFromPremiumTag(tag: PREMIUM_PLAN_TAG) {
|
||||
const plan = PREMIUM_PLANS.find(e => e.tag === tag);
|
||||
return plan;
|
||||
}
|
||||
|
||||
export type PREMIUM_PLAN_TAG = typeof PREMIUM_PLANS[number]['tag'];
|
||||
|
||||
export type PROJECT_LIMIT = {
|
||||
COUNT_LIMIT: number,
|
||||
AI_MESSAGE_LIMIT: number,
|
||||
}
|
||||
|
||||
export const PREMIUM_LIMITS: Record<PREMIUM_PLAN_TAG, PROJECT_LIMIT> = {
|
||||
FREE: {
|
||||
COUNT_LIMIT: 3_000,
|
||||
AI_MESSAGE_LIMIT: 10
|
||||
},
|
||||
PLAN_1: {
|
||||
COUNT_LIMIT: 150_000,
|
||||
AI_MESSAGE_LIMIT: 100
|
||||
},
|
||||
PLAN_2: {
|
||||
COUNT_LIMIT: 500_000,
|
||||
AI_MESSAGE_LIMIT: 5_000
|
||||
},
|
||||
PLAN_3: {
|
||||
COUNT_LIMIT: 2_000_000,
|
||||
AI_MESSAGE_LIMIT: 10_000
|
||||
},
|
||||
PLAN_99: {
|
||||
COUNT_LIMIT: 10_000_000,
|
||||
AI_MESSAGE_LIMIT: 100_000
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export type STRIPE_PLAN = {
|
||||
price: string
|
||||
}
|
||||
|
||||
export const STRIPE_PLANS: Record<PREMIUM_PLAN_TAG, STRIPE_PLAN> = {
|
||||
FREE: {
|
||||
price: 'price_1PNbHYB2lPUiVs9VZP32xglF'
|
||||
},
|
||||
PLAN_1: {
|
||||
price: 'price_1PNZjVB2lPUiVs9VrsTbJL04'
|
||||
},
|
||||
PLAN_2: {
|
||||
price: ''
|
||||
},
|
||||
PLAN_3: {
|
||||
price: ''
|
||||
},
|
||||
PLAN_99: {
|
||||
price: ''
|
||||
}
|
||||
}
|
||||
|
||||
export function getPlanTagFromStripePrice(price: string): PREMIUM_PLAN_TAG | undefined {
|
||||
for (const plan of PREMIUM_PLANS.map(e => e.tag)) {
|
||||
const stripePrice = STRIPE_PLANS[plan].price;
|
||||
if (stripePrice === price) return plan;
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
import { ProjectCountModel } from '../schema/ProjectsCounts';
|
||||
import { ProjectModel } from '../schema/ProjectSchema';
|
||||
import { LimitNotifyModel } from '../schema/broker/LimitNotifySchema';
|
||||
import { PREMIUM_LIMITS, getPlanFromPremiumType } from '../data/PREMIUM_LIMITS';
|
||||
import { MONTH } from '../utilts/TIME';
|
||||
|
||||
|
||||
export async function getCurrentProjectCountId(project_id: string) {
|
||||
const projectCount = await ProjectCountModel.findOne({ project_id }, { _id: 1 }, { sort: { billing_expire_at: -1 } });
|
||||
return projectCount?._id.toString();
|
||||
}
|
||||
|
||||
export async function getAllLimitsFromProjectId(project_id: string) {
|
||||
const targetProject = await ProjectModel.findById(project_id, {
|
||||
premium: 1, premium_type: 1, premium_expire_at: 1
|
||||
});
|
||||
if (!targetProject) return PREMIUM_LIMITS.FREE;
|
||||
if (!targetProject.premium) return PREMIUM_LIMITS.FREE;
|
||||
const plan = getPlanFromPremiumType(targetProject.premium_type);
|
||||
return PREMIUM_LIMITS[plan.tag];
|
||||
}
|
||||
|
||||
export async function checkProjectCount(project_id: string) {
|
||||
|
||||
const targetProject = await ProjectModel.findById(project_id, {
|
||||
premium: 1, premium_type: 1, premium_expire_at: 1
|
||||
});
|
||||
|
||||
if (!targetProject) return;
|
||||
|
||||
if (new Date(targetProject.premium_expire_at).getTime() < Date.now()) {
|
||||
await ProjectModel.updateOne({ _id: project_id }, {
|
||||
premium: false,
|
||||
$unset: {
|
||||
premium_type: 1,
|
||||
premium_expire_at: 1
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const limits = await getAllLimitsFromProjectId(project_id);
|
||||
|
||||
const projectCounts = await ProjectCountModel.findOne({ project_id }, {}, { sort: { billing_expire_at: -1 } });
|
||||
|
||||
const billingExpireAt = projectCounts ? new Date(projectCounts.billing_expire_at).getTime() : -1;
|
||||
|
||||
if (projectCounts && Date.now() < billingExpireAt) {
|
||||
if (projectCounts.ai_limit) return projectCounts.toJSON();
|
||||
projectCounts.ai_limit = limits.AI_MESSAGE_LIMIT;
|
||||
const saved = await projectCounts.save();
|
||||
return saved.toJSON();
|
||||
}
|
||||
|
||||
const newProjectCounts = await ProjectCountModel.create({
|
||||
project_id,
|
||||
events: 0,
|
||||
visits: 0,
|
||||
limit: limits.COUNT_LIMIT,
|
||||
ai_messages: 0,
|
||||
ai_limit: limits.AI_MESSAGE_LIMIT,
|
||||
billing_start_at: projectCounts ? billingExpireAt : Date.now(),
|
||||
billing_expire_at: (projectCounts ? billingExpireAt : Date.now()) + MONTH
|
||||
});
|
||||
|
||||
await LimitNotifyModel.updateOne({ project_id }, { limit1: false, limit2: false, limit3: false });
|
||||
|
||||
return newProjectCounts.toJSON();
|
||||
|
||||
|
||||
}
|
||||
@@ -5,8 +5,9 @@ export type TProject = {
|
||||
owner: Schema.Types.ObjectId,
|
||||
name: string,
|
||||
premium: boolean,
|
||||
premium_type?: number,
|
||||
customer_id?: string,
|
||||
premium_type: number,
|
||||
customer_id: string,
|
||||
subscription_id: string,
|
||||
premium_expire_at: Date,
|
||||
created_at: Date
|
||||
}
|
||||
@@ -15,9 +16,10 @@ const ProjectSchema = new Schema<TProject>({
|
||||
owner: { type: Types.ObjectId, index: 1 },
|
||||
name: { type: String, required: true },
|
||||
premium: { type: Boolean, default: false },
|
||||
premium_type: { type: Number },
|
||||
customer_id: { type: String },
|
||||
premium_expire_at: { type: Date },
|
||||
premium_type: { type: Number, default: 0 },
|
||||
customer_id: { type: String, required: true },
|
||||
subscription_id: { type: String, required: true },
|
||||
premium_expire_at: { type: Date, required: true },
|
||||
created_at: { type: Date, default: () => Date.now() },
|
||||
})
|
||||
|
||||
|
||||
@@ -5,22 +5,12 @@ export type TProjectCount = {
|
||||
project_id: Schema.Types.ObjectId,
|
||||
events: number,
|
||||
visits: number,
|
||||
ai_messages: number,
|
||||
limit: number,
|
||||
ai_limit: number,
|
||||
billing_expire_at: Date,
|
||||
billing_start_at: Date,
|
||||
}
|
||||
|
||||
const ProjectCountSchema = new Schema<TProjectCount>({
|
||||
project_id: { type: Types.ObjectId, index: 1 },
|
||||
events: { type: Number, required: true, default: 0 },
|
||||
visits: { type: Number, required: true, default: 0 },
|
||||
ai_messages: { type: Number, required: true, default: 0 },
|
||||
limit: { type: Number, required: true },
|
||||
ai_limit: { type: Number, required: true },
|
||||
billing_start_at: { type: Date, required: true },
|
||||
billing_expire_at: { type: Date, required: true },
|
||||
});
|
||||
|
||||
export const ProjectCountModel = model<TProjectCount>('project_counts', ProjectCountSchema);
|
||||
26
shared/schema/ProjectsLimits.ts
Normal file
26
shared/schema/ProjectsLimits.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { model, Schema, Types } from 'mongoose';
|
||||
|
||||
export type TProjectLimit = {
|
||||
_id: Schema.Types.ObjectId,
|
||||
project_id: Schema.Types.ObjectId,
|
||||
events: number,
|
||||
visits: number,
|
||||
ai_messages: number,
|
||||
limit: number,
|
||||
ai_limit: number,
|
||||
billing_expire_at: Date,
|
||||
billing_start_at: Date,
|
||||
}
|
||||
|
||||
const ProjectLimitSchema = new Schema<TProjectLimit>({
|
||||
project_id: { type: Types.ObjectId, index: true, unique: true },
|
||||
events: { type: Number, required: true, default: 0 },
|
||||
visits: { type: Number, required: true, default: 0 },
|
||||
ai_messages: { type: Number, required: true, default: 0 },
|
||||
limit: { type: Number, required: true },
|
||||
ai_limit: { type: Number, required: true },
|
||||
billing_start_at: { type: Date, required: true },
|
||||
billing_expire_at: { type: Date, required: true },
|
||||
});
|
||||
|
||||
export const ProjectLimitModel = model<TProjectLimit>('project_limits', ProjectLimitSchema);
|
||||
Reference in New Issue
Block a user