mirror of
https://github.com/Litlyx/litlyx
synced 2025-12-09 23:48:36 +01:00
add shared
This commit is contained in:
1
shared/.gitignore
vendored
Normal file
1
shared/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
node_modules
|
||||||
4
shared/data/ADMINS.ts
Normal file
4
shared/data/ADMINS.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export const ADMIN_EMAILS = [
|
||||||
|
'laura.emily.lovi@gmail.com',
|
||||||
|
'mangaiomaster@gmail.com'
|
||||||
|
]
|
||||||
1
shared/data/LITLYX.ts
Normal file
1
shared/data/LITLYX.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const LITLYX_PROJECT_ID = '6643cd08a1854e3b81722ab5';
|
||||||
46
shared/data/PREMIUM_LIMITS.ts
Normal file
46
shared/data/PREMIUM_LIMITS.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
|
||||||
|
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 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
|
||||||
|
}
|
||||||
|
}
|
||||||
5
shared/data/broker/EventType.ts
Normal file
5
shared/data/broker/EventType.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
export enum EventType {
|
||||||
|
VISIT = 1,
|
||||||
|
EVENT = 2
|
||||||
|
}
|
||||||
5
shared/data/broker/Limits.ts
Normal file
5
shared/data/broker/Limits.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
|
||||||
|
// Default: 1.1
|
||||||
|
// ((events + visits) * VALUE) > limit
|
||||||
|
export const EVENT_LOG_LIMIT_PERCENT = 1.1;
|
||||||
70
shared/functions/UtilsProjectCounts.ts
Normal file
70
shared/functions/UtilsProjectCounts.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
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 = new Date(projectCounts.billing_expire_at).getTime();
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
10
shared/package.json
Normal file
10
shared/package.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"mongoose": "^8.4.0",
|
||||||
|
"nodemailer": "^6.9.13"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.12.13",
|
||||||
|
"@types/nodemailer": "^6.4.15"
|
||||||
|
}
|
||||||
|
}
|
||||||
232
shared/pnpm-lock.yaml
generated
Normal file
232
shared/pnpm-lock.yaml
generated
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
lockfileVersion: '9.0'
|
||||||
|
|
||||||
|
settings:
|
||||||
|
autoInstallPeers: true
|
||||||
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
|
importers:
|
||||||
|
|
||||||
|
.:
|
||||||
|
dependencies:
|
||||||
|
mongoose:
|
||||||
|
specifier: ^8.4.0
|
||||||
|
version: 8.4.0
|
||||||
|
nodemailer:
|
||||||
|
specifier: ^6.9.13
|
||||||
|
version: 6.9.13
|
||||||
|
devDependencies:
|
||||||
|
'@types/node':
|
||||||
|
specifier: ^20.12.13
|
||||||
|
version: 20.12.13
|
||||||
|
'@types/nodemailer':
|
||||||
|
specifier: ^6.4.15
|
||||||
|
version: 6.4.15
|
||||||
|
|
||||||
|
packages:
|
||||||
|
|
||||||
|
'@mongodb-js/saslprep@1.1.7':
|
||||||
|
resolution: {integrity: sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==}
|
||||||
|
|
||||||
|
'@types/node@20.12.13':
|
||||||
|
resolution: {integrity: sha512-gBGeanV41c1L171rR7wjbMiEpEI/l5XFQdLLfhr/REwpgDy/4U8y89+i8kRiLzDyZdOkXh+cRaTetUnCYutoXA==}
|
||||||
|
|
||||||
|
'@types/nodemailer@6.4.15':
|
||||||
|
resolution: {integrity: sha512-0EBJxawVNjPkng1zm2vopRctuWVCxk34JcIlRuXSf54habUWdz1FB7wHDqOqvDa8Mtpt0Q3LTXQkAs2LNyK5jQ==}
|
||||||
|
|
||||||
|
'@types/webidl-conversions@7.0.3':
|
||||||
|
resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==}
|
||||||
|
|
||||||
|
'@types/whatwg-url@11.0.5':
|
||||||
|
resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==}
|
||||||
|
|
||||||
|
bson@6.7.0:
|
||||||
|
resolution: {integrity: sha512-w2IquM5mYzYZv6rs3uN2DZTOBe2a0zXLj53TGDqwF4l6Sz/XsISrisXOJihArF9+BZ6Cq/GjVht7Sjfmri7ytQ==}
|
||||||
|
engines: {node: '>=16.20.1'}
|
||||||
|
|
||||||
|
debug@4.3.4:
|
||||||
|
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
||||||
|
engines: {node: '>=6.0'}
|
||||||
|
peerDependencies:
|
||||||
|
supports-color: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
supports-color:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
kareem@2.6.3:
|
||||||
|
resolution: {integrity: sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==}
|
||||||
|
engines: {node: '>=12.0.0'}
|
||||||
|
|
||||||
|
memory-pager@1.5.0:
|
||||||
|
resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==}
|
||||||
|
|
||||||
|
mongodb-connection-string-url@3.0.1:
|
||||||
|
resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==}
|
||||||
|
|
||||||
|
mongodb@6.6.2:
|
||||||
|
resolution: {integrity: sha512-ZF9Ugo2JCG/GfR7DEb4ypfyJJyiKbg5qBYKRintebj8+DNS33CyGMkWbrS9lara+u+h+yEOGSRiLhFO/g1s1aw==}
|
||||||
|
engines: {node: '>=16.20.1'}
|
||||||
|
peerDependencies:
|
||||||
|
'@aws-sdk/credential-providers': ^3.188.0
|
||||||
|
'@mongodb-js/zstd': ^1.1.0
|
||||||
|
gcp-metadata: ^5.2.0
|
||||||
|
kerberos: ^2.0.1
|
||||||
|
mongodb-client-encryption: '>=6.0.0 <7'
|
||||||
|
snappy: ^7.2.2
|
||||||
|
socks: ^2.7.1
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@aws-sdk/credential-providers':
|
||||||
|
optional: true
|
||||||
|
'@mongodb-js/zstd':
|
||||||
|
optional: true
|
||||||
|
gcp-metadata:
|
||||||
|
optional: true
|
||||||
|
kerberos:
|
||||||
|
optional: true
|
||||||
|
mongodb-client-encryption:
|
||||||
|
optional: true
|
||||||
|
snappy:
|
||||||
|
optional: true
|
||||||
|
socks:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
mongoose@8.4.0:
|
||||||
|
resolution: {integrity: sha512-fgqRMwVEP1qgRYfh+tUe2YBBFnPO35FIg2lfFH+w9IhRGg1/ataWGIqvf/MjwM29cZ60D5vSnqtN2b8Qp0sOZA==}
|
||||||
|
engines: {node: '>=16.20.1'}
|
||||||
|
|
||||||
|
mpath@0.9.0:
|
||||||
|
resolution: {integrity: sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==}
|
||||||
|
engines: {node: '>=4.0.0'}
|
||||||
|
|
||||||
|
mquery@5.0.0:
|
||||||
|
resolution: {integrity: sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
||||||
|
ms@2.1.2:
|
||||||
|
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
||||||
|
|
||||||
|
ms@2.1.3:
|
||||||
|
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||||
|
|
||||||
|
nodemailer@6.9.13:
|
||||||
|
resolution: {integrity: sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA==}
|
||||||
|
engines: {node: '>=6.0.0'}
|
||||||
|
|
||||||
|
punycode@2.3.1:
|
||||||
|
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
sift@17.1.3:
|
||||||
|
resolution: {integrity: sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==}
|
||||||
|
|
||||||
|
sparse-bitfield@3.0.3:
|
||||||
|
resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==}
|
||||||
|
|
||||||
|
tr46@4.1.1:
|
||||||
|
resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==}
|
||||||
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
|
undici-types@5.26.5:
|
||||||
|
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
|
||||||
|
|
||||||
|
webidl-conversions@7.0.0:
|
||||||
|
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
whatwg-url@13.0.0:
|
||||||
|
resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==}
|
||||||
|
engines: {node: '>=16'}
|
||||||
|
|
||||||
|
snapshots:
|
||||||
|
|
||||||
|
'@mongodb-js/saslprep@1.1.7':
|
||||||
|
dependencies:
|
||||||
|
sparse-bitfield: 3.0.3
|
||||||
|
|
||||||
|
'@types/node@20.12.13':
|
||||||
|
dependencies:
|
||||||
|
undici-types: 5.26.5
|
||||||
|
|
||||||
|
'@types/nodemailer@6.4.15':
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 20.12.13
|
||||||
|
|
||||||
|
'@types/webidl-conversions@7.0.3': {}
|
||||||
|
|
||||||
|
'@types/whatwg-url@11.0.5':
|
||||||
|
dependencies:
|
||||||
|
'@types/webidl-conversions': 7.0.3
|
||||||
|
|
||||||
|
bson@6.7.0: {}
|
||||||
|
|
||||||
|
debug@4.3.4:
|
||||||
|
dependencies:
|
||||||
|
ms: 2.1.2
|
||||||
|
|
||||||
|
kareem@2.6.3: {}
|
||||||
|
|
||||||
|
memory-pager@1.5.0: {}
|
||||||
|
|
||||||
|
mongodb-connection-string-url@3.0.1:
|
||||||
|
dependencies:
|
||||||
|
'@types/whatwg-url': 11.0.5
|
||||||
|
whatwg-url: 13.0.0
|
||||||
|
|
||||||
|
mongodb@6.6.2:
|
||||||
|
dependencies:
|
||||||
|
'@mongodb-js/saslprep': 1.1.7
|
||||||
|
bson: 6.7.0
|
||||||
|
mongodb-connection-string-url: 3.0.1
|
||||||
|
|
||||||
|
mongoose@8.4.0:
|
||||||
|
dependencies:
|
||||||
|
bson: 6.7.0
|
||||||
|
kareem: 2.6.3
|
||||||
|
mongodb: 6.6.2
|
||||||
|
mpath: 0.9.0
|
||||||
|
mquery: 5.0.0
|
||||||
|
ms: 2.1.3
|
||||||
|
sift: 17.1.3
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@aws-sdk/credential-providers'
|
||||||
|
- '@mongodb-js/zstd'
|
||||||
|
- gcp-metadata
|
||||||
|
- kerberos
|
||||||
|
- mongodb-client-encryption
|
||||||
|
- snappy
|
||||||
|
- socks
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
mpath@0.9.0: {}
|
||||||
|
|
||||||
|
mquery@5.0.0:
|
||||||
|
dependencies:
|
||||||
|
debug: 4.3.4
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
ms@2.1.2: {}
|
||||||
|
|
||||||
|
ms@2.1.3: {}
|
||||||
|
|
||||||
|
nodemailer@6.9.13: {}
|
||||||
|
|
||||||
|
punycode@2.3.1: {}
|
||||||
|
|
||||||
|
sift@17.1.3: {}
|
||||||
|
|
||||||
|
sparse-bitfield@3.0.3:
|
||||||
|
dependencies:
|
||||||
|
memory-pager: 1.5.0
|
||||||
|
|
||||||
|
tr46@4.1.1:
|
||||||
|
dependencies:
|
||||||
|
punycode: 2.3.1
|
||||||
|
|
||||||
|
undici-types@5.26.5: {}
|
||||||
|
|
||||||
|
webidl-conversions@7.0.0: {}
|
||||||
|
|
||||||
|
whatwg-url@13.0.0:
|
||||||
|
dependencies:
|
||||||
|
tr46: 4.1.1
|
||||||
|
webidl-conversions: 7.0.0
|
||||||
22
shared/schema/ProjectSchema.ts
Normal file
22
shared/schema/ProjectSchema.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { model, Schema, Types } from 'mongoose';
|
||||||
|
|
||||||
|
export type TProject = {
|
||||||
|
_id: Schema.Types.ObjectId,
|
||||||
|
owner: Schema.Types.ObjectId,
|
||||||
|
name: string,
|
||||||
|
premium: boolean,
|
||||||
|
premium_type?: number,
|
||||||
|
premium_expire_at: Date,
|
||||||
|
created_at: Date
|
||||||
|
}
|
||||||
|
|
||||||
|
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 },
|
||||||
|
premium_expire_at: { type: Date },
|
||||||
|
created_at: { type: Date, default: () => Date.now() },
|
||||||
|
})
|
||||||
|
|
||||||
|
export const ProjectModel = model<TProject>('projects', ProjectSchema);
|
||||||
26
shared/schema/ProjectsCounts.ts
Normal file
26
shared/schema/ProjectsCounts.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { model, Schema, Types } from 'mongoose';
|
||||||
|
|
||||||
|
export type TProjectCount = {
|
||||||
|
_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 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);
|
||||||
22
shared/schema/UserSchema.ts
Normal file
22
shared/schema/UserSchema.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { model, Schema } from 'mongoose';
|
||||||
|
|
||||||
|
export type TUser = {
|
||||||
|
email: string,
|
||||||
|
name: string,
|
||||||
|
given_name: string,
|
||||||
|
locale: string,
|
||||||
|
picture: string,
|
||||||
|
created_at: Date
|
||||||
|
}
|
||||||
|
|
||||||
|
const UserSchema = new Schema<TUser>({
|
||||||
|
email: { type: String, unique: true, index: 1 },
|
||||||
|
name: String,
|
||||||
|
given_name: String,
|
||||||
|
locale: String,
|
||||||
|
picture: String,
|
||||||
|
created_at: { type: Date, default: () => Date.now() }
|
||||||
|
})
|
||||||
|
|
||||||
|
export const UserModel = model<TUser>('users', UserSchema);
|
||||||
|
|
||||||
14
shared/schema/UserSettings.ts
Normal file
14
shared/schema/UserSettings.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { model, Schema, Types } from 'mongoose';
|
||||||
|
|
||||||
|
export type TUserSettings = {
|
||||||
|
user_id: Schema.Types.ObjectId,
|
||||||
|
active_project_id: Schema.Types.ObjectId
|
||||||
|
}
|
||||||
|
|
||||||
|
const UserSettingsSchema = new Schema<TUserSettings>({
|
||||||
|
user_id: { type: Types.ObjectId, unique: true, index: 1 },
|
||||||
|
active_project_id: Schema.Types.ObjectId,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const UserSettingsModel = model<TUserSettings>('user_settings', UserSettingsSchema);
|
||||||
|
|
||||||
20
shared/schema/ai/AiChatSchema.ts
Normal file
20
shared/schema/ai/AiChatSchema.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { model, Schema } from 'mongoose';
|
||||||
|
|
||||||
|
export type TAiChatSchema = {
|
||||||
|
_id: Schema.Types.ObjectId,
|
||||||
|
project_id: Schema.Types.ObjectId,
|
||||||
|
messages: any[],
|
||||||
|
title: string,
|
||||||
|
created_at: Date,
|
||||||
|
updated_at: Date
|
||||||
|
}
|
||||||
|
|
||||||
|
const AiChatSchema = new Schema<TAiChatSchema>({
|
||||||
|
project_id: { type: Schema.Types.ObjectId, index: 1 },
|
||||||
|
messages: [{ _id: false, type: Schema.Types.Mixed }],
|
||||||
|
title: { type: String, required: true },
|
||||||
|
created_at: { type: Date, default: () => Date.now() },
|
||||||
|
updated_at: { type: Date, default: () => Date.now() },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const AiChatModel = model<TAiChatSchema>('ai_chats', AiChatSchema);
|
||||||
18
shared/schema/broker/LimitNotifySchema.ts
Normal file
18
shared/schema/broker/LimitNotifySchema.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { model, Schema, Types } from 'mongoose';
|
||||||
|
|
||||||
|
export type TLimitNotify = {
|
||||||
|
_id: Schema.Types.ObjectId,
|
||||||
|
project_id: Schema.Types.ObjectId,
|
||||||
|
limit1: boolean,
|
||||||
|
limit2: boolean,
|
||||||
|
limit3: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const LimitNotifySchema = new Schema<TLimitNotify>({
|
||||||
|
project_id: { type: Types.ObjectId, index: 1 },
|
||||||
|
limit1: { type: Boolean },
|
||||||
|
limit2: { type: Boolean },
|
||||||
|
limit3: { type: Boolean }
|
||||||
|
});
|
||||||
|
|
||||||
|
export const LimitNotifyModel = model<TLimitNotify>('limit_notifies', LimitNotifySchema);
|
||||||
18
shared/schema/metrics/EventSchema.ts
Normal file
18
shared/schema/metrics/EventSchema.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { model, Schema, Types } from 'mongoose';
|
||||||
|
|
||||||
|
export type TEvent = {
|
||||||
|
project_id: Schema.Types.ObjectId,
|
||||||
|
name: string,
|
||||||
|
metadata: Record<string, string>,
|
||||||
|
created_at: Date
|
||||||
|
}
|
||||||
|
|
||||||
|
const EventSchema = new Schema<TEvent>({
|
||||||
|
project_id: { type: Types.ObjectId, index: 1 },
|
||||||
|
name: { type: String, required: true },
|
||||||
|
metadata: Schema.Types.Mixed,
|
||||||
|
created_at: { type: Date, default: () => Date.now() },
|
||||||
|
})
|
||||||
|
|
||||||
|
export const EventModel = model<TEvent>('events', EventSchema);
|
||||||
|
|
||||||
21
shared/schema/metrics/SessionSchema.ts
Normal file
21
shared/schema/metrics/SessionSchema.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { model, Schema, Types } from 'mongoose';
|
||||||
|
|
||||||
|
|
||||||
|
export type TSession = {
|
||||||
|
project_id: Schema.Types.ObjectId,
|
||||||
|
session: string,
|
||||||
|
duration: number,
|
||||||
|
updated_at: Date,
|
||||||
|
created_at: Date,
|
||||||
|
}
|
||||||
|
|
||||||
|
const SessionSchema = new Schema<TSession>({
|
||||||
|
project_id: { type: Types.ObjectId, index: 1 },
|
||||||
|
session: { type: String, required: true },
|
||||||
|
duration: { type: Number, required: true, default: 0 },
|
||||||
|
updated_at: { type: Date, default: () => Date.now() },
|
||||||
|
created_at: { type: Date, default: () => Date.now() },
|
||||||
|
})
|
||||||
|
|
||||||
|
export const SessionModel = model<TSession>('sessions', SessionSchema);
|
||||||
|
|
||||||
39
shared/schema/metrics/VisitSchema.ts
Normal file
39
shared/schema/metrics/VisitSchema.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { model, Schema } from 'mongoose';
|
||||||
|
|
||||||
|
export type TVisit = {
|
||||||
|
project_id: Schema.Types.ObjectId,
|
||||||
|
|
||||||
|
browser: string,
|
||||||
|
os: string,
|
||||||
|
|
||||||
|
continent: string,
|
||||||
|
country: string,
|
||||||
|
|
||||||
|
device: string,
|
||||||
|
|
||||||
|
website: string,
|
||||||
|
page: string,
|
||||||
|
referrer: string,
|
||||||
|
|
||||||
|
created_at: Date
|
||||||
|
}
|
||||||
|
|
||||||
|
const VisitSchema = new Schema<TVisit>({
|
||||||
|
project_id: { type: Schema.Types.ObjectId, index: 1 },
|
||||||
|
|
||||||
|
browser: { type: String, required: true },
|
||||||
|
os: { type: String, required: true },
|
||||||
|
|
||||||
|
continent: { type: String },
|
||||||
|
country: { type: String },
|
||||||
|
|
||||||
|
device: { type: String },
|
||||||
|
|
||||||
|
website: { type: String, required: true },
|
||||||
|
page: { type: String, required: true },
|
||||||
|
referrer: { type: String, required: true },
|
||||||
|
created_at: { type: Date, default: () => Date.now() },
|
||||||
|
})
|
||||||
|
|
||||||
|
export const VisitModel = model<TVisit>('visits', VisitSchema);
|
||||||
|
|
||||||
6
shared/services/DatabaseService.ts
Normal file
6
shared/services/DatabaseService.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
import mongoose from "mongoose";
|
||||||
|
|
||||||
|
export async function connectDatabase(connectionString: string) {
|
||||||
|
await mongoose.connect(connectionString);
|
||||||
|
}
|
||||||
259
shared/services/EmailService.ts
Normal file
259
shared/services/EmailService.ts
Normal file
File diff suppressed because one or more lines are too long
10
shared/tsconfig.json
Normal file
10
shared/tsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "NodeNext",
|
||||||
|
"target": "ESNext"
|
||||||
|
},
|
||||||
|
"include": ["**/*.ts"],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
|
}
|
||||||
9
shared/utilts/TIME.ts
Normal file
9
shared/utilts/TIME.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
|
||||||
|
export const SECOND = 1000;
|
||||||
|
export const MINUTE = SECOND * 60;
|
||||||
|
export const HOUR = MINUTE * 60;
|
||||||
|
export const DAY = HOUR * 24;
|
||||||
|
export const WEEK = HOUR * 7;
|
||||||
|
export const MONTH = DAY * 30;
|
||||||
|
export const YEAR = MONTH * 12;
|
||||||
8
shared/utilts/requireEnv.ts
Normal file
8
shared/utilts/requireEnv.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
export function requireEnv(name: string, errorMessage?: string) {
|
||||||
|
if (!process.env[name]) {
|
||||||
|
console.error(errorMessage || `ENV variable ${name} is required`);
|
||||||
|
return process.exit(1);
|
||||||
|
}
|
||||||
|
return process.env[name] as string;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user