From 7b966f82829ea1d1d5efd3c94ef91143554df2b7 Mon Sep 17 00:00:00 2001 From: Rajat Date: Fri, 28 Nov 2025 09:48:06 +0000 Subject: [PATCH 1/4] WIP: cleanup school resource script --- apps/web/next-env.d.ts | 2 +- packages/orm-models/.eslintrc.js | 16 + packages/orm-models/.gitignore | 3 + packages/orm-models/README.md | 3 + packages/orm-models/additional.d.ts | 1 + packages/orm-models/package.json | 50 +++ packages/orm-models/src/index.ts | 14 + packages/orm-models/src/models/course.ts | 116 ++++++ packages/orm-models/src/models/domain.ts | 44 +++ .../orm-models/src/models/email-delivery.ts | 13 + packages/orm-models/src/models/email-event.ts | 23 ++ packages/orm-models/src/models/email.ts | 55 +++ packages/orm-models/src/models/media.ts | 19 + packages/orm-models/src/models/membership.ts | 57 +++ packages/orm-models/src/models/rule.ts | 24 ++ .../orm-models/src/models/sequence/index.ts | 73 ++++ .../src/models/sequence/sequence-report.ts | 14 + packages/orm-models/src/models/site-info.ts | 28 ++ packages/orm-models/src/models/subscriber.ts | 13 + packages/orm-models/src/models/user-filter.ts | 27 ++ .../orm-models/src/models/user-segment.ts | 20 ++ packages/orm-models/src/models/user/index.ts | 54 +++ .../orm-models/src/models/user/progress.ts | 16 + packages/orm-models/tsconfig.json | 22 ++ packages/orm-models/tsup.config.ts | 18 + packages/scripts/.gitignore | 3 + packages/scripts/package.json | 24 ++ packages/scripts/src/cleanup-domain.ts | 28 ++ packages/scripts/tsconfig.json | 24 ++ pnpm-lock.yaml | 329 ++++++++++++++---- 30 files changed, 1065 insertions(+), 68 deletions(-) create mode 100644 packages/orm-models/.eslintrc.js create mode 100644 packages/orm-models/.gitignore create mode 100644 packages/orm-models/README.md create mode 100644 packages/orm-models/additional.d.ts create mode 100644 packages/orm-models/package.json create mode 100644 packages/orm-models/src/index.ts create mode 100644 packages/orm-models/src/models/course.ts create mode 100644 packages/orm-models/src/models/domain.ts create mode 100644 packages/orm-models/src/models/email-delivery.ts create mode 100644 packages/orm-models/src/models/email-event.ts create mode 100644 packages/orm-models/src/models/email.ts create mode 100644 packages/orm-models/src/models/media.ts create mode 100644 packages/orm-models/src/models/membership.ts create mode 100644 packages/orm-models/src/models/rule.ts create mode 100644 packages/orm-models/src/models/sequence/index.ts create mode 100644 packages/orm-models/src/models/sequence/sequence-report.ts create mode 100644 packages/orm-models/src/models/site-info.ts create mode 100644 packages/orm-models/src/models/subscriber.ts create mode 100644 packages/orm-models/src/models/user-filter.ts create mode 100644 packages/orm-models/src/models/user-segment.ts create mode 100644 packages/orm-models/src/models/user/index.ts create mode 100644 packages/orm-models/src/models/user/progress.ts create mode 100644 packages/orm-models/tsconfig.json create mode 100644 packages/orm-models/tsup.config.ts create mode 100644 packages/scripts/.gitignore create mode 100644 packages/scripts/package.json create mode 100644 packages/scripts/src/cleanup-domain.ts create mode 100644 packages/scripts/tsconfig.json diff --git a/apps/web/next-env.d.ts b/apps/web/next-env.d.ts index c4b7818fb..9edff1c7c 100644 --- a/apps/web/next-env.d.ts +++ b/apps/web/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/dev/types/routes.d.ts"; +import "./.next/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/packages/orm-models/.eslintrc.js b/packages/orm-models/.eslintrc.js new file mode 100644 index 000000000..63a8163a7 --- /dev/null +++ b/packages/orm-models/.eslintrc.js @@ -0,0 +1,16 @@ +module.exports = { + parser: "@typescript-eslint/parser", + plugins: ["@typescript-eslint"], + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + ], + env: { + node: true, + }, + rules: { + "no-console": ["error", { allow: ["warn"] }], + }, + ignorePatterns: ["dist/**/*.*"], +}; diff --git a/packages/orm-models/.gitignore b/packages/orm-models/.gitignore new file mode 100644 index 000000000..a4b426ea9 --- /dev/null +++ b/packages/orm-models/.gitignore @@ -0,0 +1,3 @@ +node_modules/ + +dist/ \ No newline at end of file diff --git a/packages/orm-models/README.md b/packages/orm-models/README.md new file mode 100644 index 000000000..5a7b657e5 --- /dev/null +++ b/packages/orm-models/README.md @@ -0,0 +1,3 @@ +# `@courselit/common-orm-models` + +ORM models for CourseLit diff --git a/packages/orm-models/additional.d.ts b/packages/orm-models/additional.d.ts new file mode 100644 index 000000000..da522b2fe --- /dev/null +++ b/packages/orm-models/additional.d.ts @@ -0,0 +1 @@ +declare module "mongoose"; diff --git a/packages/orm-models/package.json b/packages/orm-models/package.json new file mode 100644 index 000000000..a6872f582 --- /dev/null +++ b/packages/orm-models/package.json @@ -0,0 +1,50 @@ +{ + "name": "@courselit/orm-models", + "version": "0.0.1", + "description": "Common ORM models for CourseLit", + "author": "Team CourseLit ", + "homepage": "https://github.com/codelitdev/courselit#readme", + "private": true, + "license": "MIT", + "types": "dist/index.d.ts", + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.cjs" + } + }, + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/codelitdev/courselit.git" + }, + "scripts": { + "test": "echo \"Error: run tests from root\" && exit 1", + "clean": "rimraf dist/", + "prepublishOnly": "pnpm run build", + "build": "tsup", + "tsc:build": "tsc", + "dev": "tsup --watch", + "check-types": "tsc --noEmit" + }, + "bugs": { + "url": "https://github.com/codelitdev/courselit/issues" + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^8.46.0", + "@typescript-eslint/parser": "^8.46.0", + "tsconfig": "workspace:^", + "eslint": "^8.12.0", + "rimraf": "^4.1.1", + "tsup": "6.6.0", + "typescript": "^4.9.5" + }, + "dependencies": { + "@courselit/common-models": "workspace:^", + "@courselit/utils": "workspace:^", + "@courselit/email-editor": "workspace:^", + "mongoose": "^8.13.1" + } +} \ No newline at end of file diff --git a/packages/orm-models/src/index.ts b/packages/orm-models/src/index.ts new file mode 100644 index 000000000..0bd1fa915 --- /dev/null +++ b/packages/orm-models/src/index.ts @@ -0,0 +1,14 @@ +export * from "./models/user"; +export * from "./models/membership"; +export * from "./models/media"; +export * from "./models/sequence"; +export * from "./models/user-segment"; +export * from "./models/user-filter"; +export * from "./models/course"; +export * from "./models/rule"; +export * from "./models/email"; +export * from "./models/email-delivery"; +export * from "./models/email-event"; +export * from "./models/subscriber"; +export * from "./models/domain"; +export * from "./models/site-info"; diff --git a/packages/orm-models/src/models/course.ts b/packages/orm-models/src/models/course.ts new file mode 100644 index 000000000..9a25eb883 --- /dev/null +++ b/packages/orm-models/src/models/course.ts @@ -0,0 +1,116 @@ +import mongoose from "mongoose"; +import { generateUniqueId } from "@courselit/utils"; +import { + Constants, + Course, + type ProductAccessType, + type Group, +} from "@courselit/common-models"; +import { MediaSchema } from "./media"; +import { EmailSchema } from "./email"; + +export interface InternalCourse extends Omit { + domain: mongoose.Types.ObjectId; + id: mongoose.Types.ObjectId; + privacy: ProductAccessType; + published: boolean; + isFeatured: boolean; + tags: string[]; + lessons: any[]; + sales: number; + customers: string[]; + certificate?: boolean; +} + +export const CourseSchema = new mongoose.Schema( + { + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + courseId: { type: String, required: true, default: generateUniqueId }, + title: { type: String, required: true }, + slug: { type: String, required: true }, + cost: { type: Number, required: true }, + costType: { + type: String, + required: true, + enum: ["free", "email", "paid"], + }, + privacy: { + type: String, + required: true, + enum: Object.values(Constants.ProductAccessType), + }, + type: { + type: String, + required: true, + enum: Object.values(Constants.CourseType), + }, + creatorId: { type: String, required: true }, + published: { type: Boolean, required: true, default: false }, + tags: [{ type: String }], + lessons: [String], + description: String, + featuredImage: MediaSchema, + groups: [ + { + name: { type: String, required: true }, + _id: { + type: String, + required: true, + default: generateUniqueId, + }, + rank: { type: Number, required: true }, + collapsed: { type: Boolean, required: true, default: true }, + lessonsOrder: { type: [String] }, + drip: new mongoose.Schema({ + type: { + type: String, + required: true, + enum: Constants.dripType, + }, + status: { type: Boolean, required: true, default: false }, + delayInMillis: { type: Number }, + dateInUTC: { type: Number }, + email: EmailSchema, + }), + }, + ], + sales: { type: Number, required: true, default: 0.0 }, + customers: [String], + pageId: { type: String }, + // paymentPlans: [String], + defaultPaymentPlan: { type: String }, + leadMagnet: { type: Boolean, required: true, default: false }, + certificate: Boolean, + }, + { + timestamps: true, + }, +); + +CourseSchema.index({ + title: "text", +}); + +CourseSchema.index({ domain: 1, title: 1 }, { unique: true }); + +CourseSchema.statics.paginatedFind = async function ( + filter, + options: { + page?: number; + limit?: number; + sort?: number; + }, +) { + const page = options.page || 1; + const limit = options.limit || 10; + const sort = options.sort || -1; + const skip = (page - 1) * limit; + + const docs = await this.find(filter) + .sort({ createdAt: sort }) + .lean() + .skip(skip) + .limit(limit) + .exec(); + return docs; +}; diff --git a/packages/orm-models/src/models/domain.ts b/packages/orm-models/src/models/domain.ts new file mode 100644 index 000000000..b92d068ef --- /dev/null +++ b/packages/orm-models/src/models/domain.ts @@ -0,0 +1,44 @@ +import mongoose from "mongoose"; +import { SettingsSchema } from "./site-info"; +import { Domain as PublicDomain } from "@courselit/common-models"; + +export interface Domain extends PublicDomain { + _id: mongoose.Types.ObjectId; + lastEditedThemeId?: string; +} + +export const DomainSchema = new mongoose.Schema( + { + name: { type: String, required: true, unique: true }, + customDomain: { type: String, unique: true, sparse: true }, + email: { type: String, required: true }, + deleted: { type: Boolean, required: true, default: false }, + settings: SettingsSchema, + themeId: { type: String }, + lastEditedThemeId: { type: String }, + sharedWidgets: { + type: mongoose.Schema.Types.Mixed, + default: {}, + }, + draftSharedWidgets: { + type: mongoose.Schema.Types.Mixed, + default: {}, + }, + firstRun: { type: Boolean, required: true, default: false }, + tags: { type: [String], default: [] }, + checkSubscriptionStatusAfter: { type: Date }, + quota: new mongoose.Schema({ + mail: new mongoose.Schema({ + daily: { type: Number, default: 0 }, + monthly: { type: Number, default: 0 }, + dailyCount: { type: Number, default: 0 }, + monthlyCount: { type: Number, default: 0 }, + lastDailyCountUpdate: { type: Date, default: Date.now }, + lastMonthlyCountUpdate: { type: Date, default: Date.now }, + }), + }), + }, + { + timestamps: true, + }, +); diff --git a/packages/orm-models/src/models/email-delivery.ts b/packages/orm-models/src/models/email-delivery.ts new file mode 100644 index 000000000..5e3ccfce6 --- /dev/null +++ b/packages/orm-models/src/models/email-delivery.ts @@ -0,0 +1,13 @@ +import mongoose from "mongoose"; + +export const EmailDeliverySchema = new mongoose.Schema( + { + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + sequenceId: { type: String, required: true }, + userId: { type: String, required: true }, + emailId: { type: String, required: true }, + }, + { + timestamps: { createdAt: true, updatedAt: false }, + }, +); diff --git a/packages/orm-models/src/models/email-event.ts b/packages/orm-models/src/models/email-event.ts new file mode 100644 index 000000000..e02c46de4 --- /dev/null +++ b/packages/orm-models/src/models/email-event.ts @@ -0,0 +1,23 @@ +import mongoose from "mongoose"; +import { Constants } from "@courselit/common-models"; + +export const EmailEventSchema = new mongoose.Schema( + { + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + sequenceId: { type: String, required: true }, + userId: { type: String, required: true }, + emailId: { type: String, required: true }, + action: { + type: String, + required: true, + enum: Object.values(Constants.EmailEventAction), + }, + link: { type: String }, + linkIndex: { type: Number }, + bounceType: { type: String, enum: ["hard", "soft"] }, + bounceReason: { type: String }, + }, + { + timestamps: true, + }, +); diff --git a/packages/orm-models/src/models/email.ts b/packages/orm-models/src/models/email.ts new file mode 100644 index 000000000..f5b811c72 --- /dev/null +++ b/packages/orm-models/src/models/email.ts @@ -0,0 +1,55 @@ +import mongoose from "mongoose"; +import { Email, Constants } from "@courselit/common-models"; +import { generateUniqueId } from "@courselit/utils"; +import { EmailBlock, EmailMeta, EmailStyle } from "@courselit/email-editor"; + +const EmailContentBlockSchema = new mongoose.Schema( + { + blockType: { type: String, required: true }, + settings: { type: Object, required: true, default: () => ({}) }, + }, + { _id: false }, +); + +const EmailStyleSchema = new mongoose.Schema( + { + colors: { type: Object, required: true }, + typography: { type: Object, required: true }, + structure: { type: Object, required: true }, + }, + { _id: false }, +); + +const EmailMetaSchema = new mongoose.Schema( + { + previewText: { type: String }, + utm: { type: Object }, + }, + { _id: false }, +); + +const EmailActionSchema = new mongoose.Schema( + { + type: { type: String, enum: Constants.emailActionTypes }, + data: { type: mongoose.Schema.Types.Mixed }, + }, + { _id: false }, +); + +export const EmailContentSchema = new mongoose.Schema( + { + content: { type: [EmailContentBlockSchema], required: true }, + style: { type: EmailStyleSchema, required: true }, + meta: { type: EmailMetaSchema, required: true }, + }, + { _id: false }, +); + +export const EmailSchema = new mongoose.Schema({ + emailId: { type: String, required: true, default: generateUniqueId }, + content: { type: EmailContentSchema, required: true }, + subject: { type: String, required: true }, + delayInMillis: { type: Number, required: true, default: 86400000 }, + published: { type: Boolean, required: true, default: false }, + action: EmailActionSchema, +}); diff --git a/packages/orm-models/src/models/media.ts b/packages/orm-models/src/models/media.ts new file mode 100644 index 000000000..bb50e1dd2 --- /dev/null +++ b/packages/orm-models/src/models/media.ts @@ -0,0 +1,19 @@ +import { Constants, Media } from "@courselit/common-models"; +import mongoose from "mongoose"; + +type MediaWithOwner = Media & { userId: string }; + +export const MediaSchema = new mongoose.Schema({ + mediaId: { type: String, required: true }, + originalFileName: { type: String, required: true }, + mimeType: { type: String, required: true }, + size: { type: Number, required: true }, + access: { + type: String, + required: true, + enum: Object.values(Constants.MediaAccessType), + }, + thumbnail: String, + caption: String, + file: String, +}); diff --git a/packages/orm-models/src/models/membership.ts b/packages/orm-models/src/models/membership.ts new file mode 100644 index 000000000..d18dbeadf --- /dev/null +++ b/packages/orm-models/src/models/membership.ts @@ -0,0 +1,57 @@ +import type { Membership } from "@courselit/common-models"; +import mongoose, { Document } from "mongoose"; +import { Constants } from "@courselit/common-models"; +import { generateUniqueId } from "@courselit/utils"; + +const { MembershipEntityType, MembershipStatus, MembershipRole } = Constants; + +export interface InternalMembership extends Membership, Document { + domain: mongoose.Types.ObjectId; +} + +export const MembershipSchema = new mongoose.Schema( + { + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + membershipId: { + type: String, + required: true, + unique: true, + default: generateUniqueId, + }, + userId: { type: String, required: true }, + paymentPlanId: { type: String, required: true }, + entityId: { type: String, required: true }, + entityType: { + type: String, + enum: Object.values(MembershipEntityType), + required: true, + }, + sessionId: { type: String, required: true, default: generateUniqueId }, + isIncludedInPlan: { type: Boolean, default: false }, + status: { + type: String, + enum: Object.values(MembershipStatus), + default: MembershipStatus.PENDING, + }, + role: { + type: String, + enum: Object.values(MembershipRole), + }, + joiningReason: { type: String }, + rejectionReason: { type: String }, + subscriptionId: { type: String }, + subscriptionMethod: { type: String }, + }, + { + timestamps: true, + }, +); + +MembershipSchema.statics.paginatedFind = async function (filter, options) { + const page = options.page || 1; + const limit = options.limit || 10; + const skip = (page - 1) * limit; + + const docs = await this.find(filter).skip(skip).limit(limit).exec(); + return docs; +}; diff --git a/packages/orm-models/src/models/rule.ts b/packages/orm-models/src/models/rule.ts new file mode 100644 index 000000000..9baaff7b1 --- /dev/null +++ b/packages/orm-models/src/models/rule.ts @@ -0,0 +1,24 @@ +import mongoose from "mongoose"; +import { Constants, Rule } from "@courselit/common-models"; +import { generateUniqueId } from "@courselit/utils"; + +export const RuleSchema = new mongoose.Schema< + Rule & { domain: mongoose.Schema.Types.ObjectId } +>({ + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + ruleId: { + type: String, + required: true, + default: generateUniqueId, + unique: true, + }, + event: { + type: String, + required: true, + enum: Object.values(Constants.EventType), + index: true, + }, + sequenceId: { type: String, required: true }, + eventDateInMillis: { type: Number }, + eventData: { type: String }, +}); diff --git a/packages/orm-models/src/models/sequence/index.ts b/packages/orm-models/src/models/sequence/index.ts new file mode 100644 index 000000000..7f325de5a --- /dev/null +++ b/packages/orm-models/src/models/sequence/index.ts @@ -0,0 +1,73 @@ +import mongoose from "mongoose"; +import { Email, Sequence } from "@courselit/common-models"; +import { generateUniqueId } from "@courselit/utils"; +import { EmailSchema } from "../email"; +import { UserFilterWithAggregatorSchema } from "../user-filter"; +import { SequenceReportSchema } from "./sequence-report"; +import { Constants } from "@courselit/common-models"; + +export interface AdminSequence + extends Pick< + Sequence, + | "sequenceId" + | "report" + | "title" + | "type" + | "from" + | "trigger" + | "filter" + | "excludeFilter" + | "status" + | "emailsOrder" + > { + domain: mongoose.Types.ObjectId; + creatorId: string; + emails: Partial[]; + entrants: string[]; +} + +const EmailFromSchema = new mongoose.Schema({ + name: { type: String, required: true }, + email: { type: String }, +}); + +const TriggerSchema = new mongoose.Schema({ + type: { + type: String, + required: true, + enum: Object.values(Constants.EventType), + }, + data: { type: String }, +}); + +export const SequenceSchema = new mongoose.Schema( + { + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + sequenceId: { + type: String, + required: true, + default: generateUniqueId, + unique: true, + }, + type: { type: String, required: true, enum: Constants.mailTypes }, + title: { type: String, default: "" }, + emails: [EmailSchema], + creatorId: { type: String, required: true }, + report: SequenceReportSchema, + from: EmailFromSchema, + filter: UserFilterWithAggregatorSchema, + excludeFilter: UserFilterWithAggregatorSchema, + trigger: TriggerSchema, + status: { + type: String, + required: true, + default: Constants.sequenceStatus[0], + enum: Constants.sequenceStatus, + }, + emailsOrder: [String], + entrants: [String], + }, + { + timestamps: true, + }, +); diff --git a/packages/orm-models/src/models/sequence/sequence-report.ts b/packages/orm-models/src/models/sequence/sequence-report.ts new file mode 100644 index 000000000..c14a78936 --- /dev/null +++ b/packages/orm-models/src/models/sequence/sequence-report.ts @@ -0,0 +1,14 @@ +import { SequenceReport } from "@courselit/common-models"; +import mongoose from "mongoose"; + +export const SequenceReportSchema = new mongoose.Schema({ + broadcast: { + lockedAt: Date, + sentAt: Date, + }, + sequence: { + subscribers: [String], + unsubscribers: [String], + failed: [String], + }, +}); diff --git a/packages/orm-models/src/models/site-info.ts b/packages/orm-models/src/models/site-info.ts new file mode 100644 index 000000000..f5d0a88f2 --- /dev/null +++ b/packages/orm-models/src/models/site-info.ts @@ -0,0 +1,28 @@ +import { SiteInfo, Constants } from "@courselit/common-models"; +import mongoose from "mongoose"; +import { MediaSchema } from "./media"; + +export const SettingsSchema = new mongoose.Schema({ + title: { type: String }, + subtitle: { type: String }, + logo: MediaSchema, + currencyISOCode: { type: String, maxlength: 3 }, + paymentMethod: { type: String, enum: Constants.paymentMethods }, + stripeKey: { type: String }, + codeInjectionHead: { type: String }, + codeInjectionBody: { type: String }, + stripeSecret: { type: String }, + paytmSecret: { type: String }, + paypalSecret: { type: String }, + mailingAddress: { type: String }, + hideCourseLitBranding: { type: Boolean, default: false }, + razorpayKey: { type: String }, + razorpaySecret: { type: String }, + razorpayWebhookSecret: { type: String }, + lemonsqueezyKey: { type: String }, + lemonsqueezyStoreId: { type: String }, + lemonsqueezyWebhookSecret: { type: String }, + lemonsqueezyOneTimeVariantId: { type: String }, + lemonsqueezySubscriptionMonthlyVariantId: { type: String }, + lemonsqueezySubscriptionYearlyVariantId: { type: String }, +}); diff --git a/packages/orm-models/src/models/subscriber.ts b/packages/orm-models/src/models/subscriber.ts new file mode 100644 index 000000000..23000f61d --- /dev/null +++ b/packages/orm-models/src/models/subscriber.ts @@ -0,0 +1,13 @@ +import mongoose from "mongoose"; + +export interface Subscriber { + subscriberId: string; + name?: string; + email: string; +} + +export const SubscriberSchema = new mongoose.Schema({ + subscriberId: { type: String, required: true, unique: true }, + name: { type: String }, + email: { type: String, required: true, unique: true }, +}); diff --git a/packages/orm-models/src/models/user-filter.ts b/packages/orm-models/src/models/user-filter.ts new file mode 100644 index 000000000..bed44bc43 --- /dev/null +++ b/packages/orm-models/src/models/user-filter.ts @@ -0,0 +1,27 @@ +import mongoose from "mongoose"; +import { + Constants, + UserFilter, + UserFilterWithAggregator, +} from "@courselit/common-models"; + +export const UserFilterSchema = new mongoose.Schema({ + name: { + type: String, + required: true, + enum: Object.values(Constants.UserFilter), + }, + condition: { type: String, required: true }, + value: { type: String, required: true }, + valueLabel: { type: String }, +}); + +export const UserFilterWithAggregatorSchema = + new mongoose.Schema({ + aggregator: { + type: String, + required: true, + enum: Constants.userFilterAggregationOperators, + }, + filters: [UserFilterSchema], + }); diff --git a/packages/orm-models/src/models/user-segment.ts b/packages/orm-models/src/models/user-segment.ts new file mode 100644 index 000000000..afa2fd625 --- /dev/null +++ b/packages/orm-models/src/models/user-segment.ts @@ -0,0 +1,20 @@ +import mongoose from "mongoose"; +import { UserFilterWithAggregatorSchema } from "./user-filter"; +import { generateUniqueId } from "@courselit/utils"; +import { UserFilterWithAggregator } from "@courselit/common-models"; + +export interface UserSegment { + domain: mongoose.Types.ObjectId; + userId: string; + segmentId: string; + name: string; + filter: UserFilterWithAggregator; +} + +export const UserSegmentSchema = new mongoose.Schema({ + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + userId: { type: String, required: true }, + segmentId: { type: String, required: true, default: generateUniqueId }, + name: { type: String, required: true }, + filter: UserFilterWithAggregatorSchema, +}); diff --git a/packages/orm-models/src/models/user/index.ts b/packages/orm-models/src/models/user/index.ts new file mode 100644 index 000000000..3a12c867f --- /dev/null +++ b/packages/orm-models/src/models/user/index.ts @@ -0,0 +1,54 @@ +import { generateUniqueId } from "@courselit/utils"; +import mongoose from "mongoose"; +import { ProgressSchema } from "./progress"; +import { Constants, User } from "@courselit/common-models"; +import { MediaSchema } from "../media"; + +export interface InternalUser extends Omit { + _id: mongoose.Types.ObjectId; + domain: mongoose.Types.ObjectId; + unsubscribeToken: string; +} + +export const UserSchema = new mongoose.Schema( + { + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + userId: { type: String, required: true, default: generateUniqueId }, + email: { type: String, required: true }, + active: { type: Boolean, required: true, default: true }, + name: { type: String, required: false }, + purchases: [ProgressSchema], + bio: { type: String }, + permissions: [String], + subscribedToUpdates: { type: Boolean, default: true }, + lead: { + type: String, + required: true, + enum: Constants.leads, + default: Constants.leads[0], + }, + tags: [String], + unsubscribeToken: { + type: String, + required: true, + default: generateUniqueId, + }, + avatar: MediaSchema, + }, + { + timestamps: true, + }, +); + +UserSchema.index({ + email: "text", + name: "text", +}); + +UserSchema.index( + { + domain: 1, + email: 1, + }, + { unique: true }, +); diff --git a/packages/orm-models/src/models/user/progress.ts b/packages/orm-models/src/models/user/progress.ts new file mode 100644 index 000000000..8500734e2 --- /dev/null +++ b/packages/orm-models/src/models/user/progress.ts @@ -0,0 +1,16 @@ +import mongoose from "mongoose"; +import { Progress } from "@courselit/common-models/src/progress"; + +export const ProgressSchema = new mongoose.Schema( + { + courseId: { type: String, required: true }, + completedLessons: { type: [String] }, + downloaded: { type: Boolean }, + accessibleGroups: { type: [String] }, + lastDripAt: { type: Date }, + certificateId: { type: String }, + }, + { + timestamps: true, + }, +); diff --git a/packages/orm-models/tsconfig.json b/packages/orm-models/tsconfig.json new file mode 100644 index 000000000..d8a458ea3 --- /dev/null +++ b/packages/orm-models/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "tsconfig/node.json", + "exclude": ["dist", "build", "node_modules"], + "compilerOptions": { + "outDir": "./dist", + "lib": ["es2017", "dom"], + "strict": true, + "module": "esnext", + "moduleResolution": "node", + "target": "es2017", + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "noImplicitAny": false + }, + "include": ["src/**/*", "additional.d.ts"] +} diff --git a/packages/orm-models/tsup.config.ts b/packages/orm-models/tsup.config.ts new file mode 100644 index 000000000..7d1527cc6 --- /dev/null +++ b/packages/orm-models/tsup.config.ts @@ -0,0 +1,18 @@ +import { defineConfig, Options } from "tsup"; + +export default defineConfig((options: Options) => ({ + treeshake: true, + splitting: true, + entry: ["src/**/*.ts"], + format: ["esm"], + dts: true, + minify: true, + clean: true, + external: ["mongoose"], + esbuildOptions(options) { + options.target = "es2017"; + options.platform = "node"; + return options; + }, + ...options, +})); diff --git a/packages/scripts/.gitignore b/packages/scripts/.gitignore new file mode 100644 index 000000000..a4b426ea9 --- /dev/null +++ b/packages/scripts/.gitignore @@ -0,0 +1,3 @@ +node_modules/ + +dist/ \ No newline at end of file diff --git a/packages/scripts/package.json b/packages/scripts/package.json new file mode 100644 index 000000000..48365ecd1 --- /dev/null +++ b/packages/scripts/package.json @@ -0,0 +1,24 @@ +{ + "name": "@courselit/scripts", + "version": "0.0.1", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "domain:cleanup": "ts-node src/cleanup-domain.ts", + "build": "tsc" + }, + "keywords": [], + "author": "", + "license": "ISC", + "packageManager": "pnpm@10.22.0", + "dependencies": { + "mongoose": "^8.14.0" + }, + "devDependencies": { + "@types/node": "^24.10.1", + "ts-node": "^10.9.2", + "tsconfig": "workspace:^", + "@courselit/orm-models": "workspace:^" + } +} \ No newline at end of file diff --git a/packages/scripts/src/cleanup-domain.ts b/packages/scripts/src/cleanup-domain.ts new file mode 100644 index 000000000..9d6a4fefa --- /dev/null +++ b/packages/scripts/src/cleanup-domain.ts @@ -0,0 +1,28 @@ +import mongoose from "mongoose"; +import { DomainSchema } from "@courselit/orm-models"; + +if (!process.env.DB_CONNECTION_STRING) { + throw new Error("DB_CONNECTION_STRING is not set"); +} + +if (!process.argv[2]) { + throw new Error("Domain name is not provided"); +} + +mongoose.connect(process.env.DB_CONNECTION_STRING); + +const DomainModel = mongoose.model("Domain", DomainSchema); + +async function cleanupDomain(name: string) { + const domain = await DomainModel.findOne({ name }); + if (!domain) { + console.log("Domain not found"); + return; + } + console.log(domain); +} + +(async () => { + await cleanupDomain(process.argv[2]); + mongoose.connection.close(); +})(); diff --git a/packages/scripts/tsconfig.json b/packages/scripts/tsconfig.json new file mode 100644 index 000000000..bbe7228e7 --- /dev/null +++ b/packages/scripts/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "tsconfig/node.json", + "exclude": [ + "dist", + "build", + "node_modules" + ], + "compilerOptions": { + "outDir": "./dist", + "lib": [ + "dom" + ], + "strict": false, + "paths": { + "@/*": [ + "./src/*" + ] + } + }, + "include": [ + "src/**/*", + "additional.d.ts" + ] +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 65fa2d48f..99a22ee22 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,7 +16,7 @@ importers: devDependencies: '@changesets/cli': specifier: ^2.29.7 - version: 2.29.7(@types/node@20.19.0) + version: 2.29.7(@types/node@24.10.1) '@testing-library/dom': specifier: ^10.4.0 version: 10.4.0 @@ -55,7 +55,7 @@ importers: version: 9.1.7 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@20.19.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3)) + version: 29.7.0(@types/node@24.10.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) jest-environment-jsdom: specifier: ^29.7.0 version: 29.7.0 @@ -70,10 +70,10 @@ importers: version: 3.5.3 tailwindcss: specifier: ^3.0.0 - version: 3.4.17(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3)) + version: 3.4.17(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@20.19.0)(typescript@5.9.3) + version: 10.9.2(@types/node@24.10.1)(typescript@5.9.3) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -182,13 +182,13 @@ importers: version: 10.1.4(@aws-sdk/credential-providers@3.797.0)(socks@2.8.4) ts-jest: specifier: ^29.4.4 - version: 29.4.4(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(esbuild@0.19.12)(jest-util@29.7.0)(jest@29.7.0(@types/node@20.19.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3)))(typescript@5.9.3) + version: 29.4.4(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(esbuild@0.19.12)(jest-util@29.7.0)(jest@29.7.0(@types/node@24.10.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)))(typescript@5.9.3) tsconfig: specifier: workspace:^ version: link:../../packages/tsconfig tsup: specifier: ^7.2.0 - version: 7.3.0(postcss@8.5.3)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3))(typescript@5.9.3) + version: 7.3.0(postcss@8.5.3)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3))(typescript@5.9.3) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -511,7 +511,7 @@ importers: version: link:../tsconfig tsup: specifier: 6.6.0 - version: 6.6.0(postcss@8.5.3)(ts-node@10.9.2(@types/node@20.19.0)(typescript@4.9.5))(typescript@4.9.5) + version: 6.6.0(postcss@8.5.3)(ts-node@10.9.2(@types/node@24.10.1)(typescript@4.9.5))(typescript@4.9.5) typescript: specifier: ^4.9.5 version: 4.9.5 @@ -653,7 +653,7 @@ importers: version: 2.6.0 tailwindcss-animate: specifier: ^1.0.7 - version: 1.0.7(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3))) + version: 1.0.7(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3))) tus-js-client: specifier: ^4.3.1 version: 4.3.1 @@ -678,7 +678,7 @@ importers: version: link:../tsconfig tsup: specifier: 6.6.0 - version: 6.6.0(postcss@8.5.3)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3))(typescript@5.9.3) + version: 6.6.0(postcss@8.5.3)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3))(typescript@5.9.3) typescript: specifier: ^5.1.6 version: 5.9.3 @@ -797,7 +797,7 @@ importers: version: link:../tsconfig tsup: specifier: 6.6.0 - version: 6.6.0(postcss@8.5.3)(ts-node@10.9.2(@types/node@20.19.0)(typescript@4.9.5))(typescript@4.9.5) + version: 6.6.0(postcss@8.5.3)(ts-node@10.9.2(@types/node@24.10.1)(typescript@4.9.5))(typescript@4.9.5) typescript: specifier: ^4.9.5 version: 4.9.5 @@ -805,6 +805,43 @@ importers: specifier: ^7.4.0 version: 7.18.0(eslint@8.57.1)(typescript@4.9.5) + packages/orm-models: + dependencies: + '@courselit/common-models': + specifier: workspace:^ + version: link:../common-models + '@courselit/email-editor': + specifier: workspace:^ + version: link:../email-editor + '@courselit/utils': + specifier: workspace:^ + version: link:../utils + mongoose: + specifier: ^8.13.1 + version: 8.14.0(@aws-sdk/credential-providers@3.797.0)(socks@2.8.4) + devDependencies: + '@typescript-eslint/eslint-plugin': + specifier: ^8.46.0 + version: 8.46.4(@typescript-eslint/parser@8.46.4(eslint@8.57.1)(typescript@4.9.5))(eslint@8.57.1)(typescript@4.9.5) + '@typescript-eslint/parser': + specifier: ^8.46.0 + version: 8.46.4(eslint@8.57.1)(typescript@4.9.5) + eslint: + specifier: ^8.12.0 + version: 8.57.1 + rimraf: + specifier: ^4.1.1 + version: 4.4.1 + tsconfig: + specifier: workspace:^ + version: link:../tsconfig + tsup: + specifier: 6.6.0 + version: 6.6.0(postcss@8.5.3)(ts-node@10.9.2(@types/node@24.10.1)(typescript@4.9.5))(typescript@4.9.5) + typescript: + specifier: ^4.9.5 + version: 4.9.5 + packages/page-blocks: dependencies: '@courselit/common-models': @@ -885,7 +922,7 @@ importers: version: link:../tsconfig tsup: specifier: 6.6.0 - version: 6.6.0(postcss@8.5.3)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3))(typescript@5.9.3) + version: 6.6.0(postcss@8.5.3)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3))(typescript@5.9.3) typescript: specifier: ^5.1.6 version: 5.9.3 @@ -967,13 +1004,13 @@ importers: version: link:../tailwind-config tailwindcss: specifier: '3' - version: 3.4.17(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3)) + version: 3.4.17(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) tsconfig: specifier: workspace:^ version: link:../tsconfig tsup: specifier: 6.6.0 - version: 6.6.0(postcss@8.5.3)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3))(typescript@5.9.3) + version: 6.6.0(postcss@8.5.3)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3))(typescript@5.9.3) typescript: specifier: ^5.8.3 version: 5.9.3 @@ -981,15 +1018,34 @@ importers: specifier: ^7.4.0 version: 7.18.0(eslint@8.57.1)(typescript@5.9.3) + packages/scripts: + dependencies: + mongoose: + specifier: ^8.14.0 + version: 8.14.0(@aws-sdk/credential-providers@3.797.0)(socks@2.8.4) + devDependencies: + '@courselit/orm-models': + specifier: workspace:^ + version: link:../orm-models + '@types/node': + specifier: ^24.10.1 + version: 24.10.1 + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@24.10.1)(typescript@5.9.3) + tsconfig: + specifier: workspace:^ + version: link:../tsconfig + packages/tailwind-config: dependencies: tailwindcss-animate: specifier: ^1.0.7 - version: 1.0.7(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3))) + version: 1.0.7(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3))) devDependencies: tailwindcss: specifier: ^3.4.1 - version: 3.4.17(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3)) + version: 3.4.17(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) packages/text-editor: dependencies: @@ -5029,6 +5085,9 @@ packages: '@types/node@20.19.0': resolution: {integrity: sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q==} + '@types/node@24.10.1': + resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} + '@types/nodemailer@6.4.17': resolution: {integrity: sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==} @@ -10356,6 +10415,9 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + unherit@3.0.1: resolution: {integrity: sha512-akOOQ/Yln8a2sgcLj4U0Jmx0R5jpIg2IUyRrWOzmEbjBtGzBdHtSeFKgoEcoH4KYIG/Pb8GQ/BwtYm0GCq1Sqg==} @@ -10966,8 +11028,8 @@ snapshots: dependencies: '@astrojs/micromark-extension-mdx-jsx': 1.0.3 '@astrojs/prism': 1.0.2 - acorn: 8.14.1 - acorn-jsx: 5.3.2(acorn@8.14.1) + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) github-slugger: 1.5.0 hast-util-to-html: 8.0.4 import-meta-resolve: 2.2.2 @@ -11729,7 +11791,7 @@ snapshots: dependencies: '@changesets/types': 6.1.0 - '@changesets/cli@2.29.7(@types/node@20.19.0)': + '@changesets/cli@2.29.7(@types/node@24.10.1)': dependencies: '@changesets/apply-release-plan': 7.0.13 '@changesets/assemble-release-plan': 6.0.9 @@ -11745,7 +11807,7 @@ snapshots: '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@changesets/write': 0.4.0 - '@inquirer/external-editor': 1.0.3(@types/node@20.19.0) + '@inquirer/external-editor': 1.0.3(@types/node@24.10.1) '@manypkg/get-packages': 1.1.3 ansi-colors: 4.1.3 ci-info: 3.9.0 @@ -12542,12 +12604,12 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true - '@inquirer/external-editor@1.0.3(@types/node@20.19.0)': + '@inquirer/external-editor@1.0.3(@types/node@24.10.1)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.0 optionalDependencies: - '@types/node': 20.19.0 + '@types/node': 24.10.1 '@ioredis/commands@1.2.0': {} @@ -12614,7 +12676,7 @@ snapshots: - supports-color - ts-node - '@jest/core@29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3))': + '@jest/core@29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 @@ -12628,7 +12690,7 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.19.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3)) + jest-config: 29.7.0(@types/node@20.19.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -16427,9 +16489,13 @@ snapshots: dependencies: undici-types: 6.21.0 + '@types/node@24.10.1': + dependencies: + undici-types: 7.16.0 + '@types/nodemailer@6.4.17': dependencies: - '@types/node': 18.19.87 + '@types/node': 20.19.0 '@types/object.omit@3.0.3': {} @@ -16983,20 +17049,16 @@ snapshots: acorn-globals@7.0.1: dependencies: - acorn: 8.14.1 + acorn: 8.15.0 acorn-walk: 8.3.4 - acorn-jsx@5.3.2(acorn@8.14.1): - dependencies: - acorn: 8.14.1 - acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 acorn-walk@8.3.4: dependencies: - acorn: 8.14.1 + acorn: 8.15.0 acorn@7.4.1: {} @@ -17841,13 +17903,13 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(@types/node@20.19.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3)): + create-jest@29.7.0(@types/node@24.10.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.19.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3)) + jest-config: 29.7.0(@types/node@24.10.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -18440,7 +18502,7 @@ snapshots: '@next/eslint-plugin-next': 16.0.3 eslint: 9.39.1(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@1.21.7)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7)) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.7)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.1(jiti@1.21.7)) eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@1.21.7)) @@ -18471,7 +18533,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@1.21.7)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.1 @@ -18486,14 +18548,14 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.7)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.46.4(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) eslint: 9.39.1(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@1.21.7)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7)) transitivePeerDependencies: - supports-color @@ -18514,7 +18576,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.1(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.7)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -18725,8 +18787,8 @@ snapshots: espree@9.6.1: dependencies: - acorn: 8.14.1 - acorn-jsx: 5.3.2(acorn@8.14.1) + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) eslint-visitor-keys: 3.4.3 esprima@4.0.1: {} @@ -19784,16 +19846,16 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@20.19.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3)): + jest-cli@29.7.0(@types/node@24.10.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): dependencies: - '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3)) + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.19.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3)) + create-jest: 29.7.0(@types/node@24.10.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@20.19.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3)) + jest-config: 29.7.0(@types/node@24.10.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -19865,7 +19927,7 @@ snapshots: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.19.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3)): + jest-config@29.7.0(@types/node@20.19.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): dependencies: '@babel/core': 7.26.10 '@jest/test-sequencer': 29.7.0 @@ -19891,7 +19953,38 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 20.19.0 - ts-node: 10.9.2(@types/node@20.19.0)(typescript@5.9.3) + ts-node: 10.9.2(@types/node@24.10.1)(typescript@5.9.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-config@29.7.0(@types/node@24.10.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): + dependencies: + '@babel/core': 7.26.10 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.26.10) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0(babel-plugin-macros@3.1.0) + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 24.10.1 + ts-node: 10.9.2(@types/node@24.10.1)(typescript@5.9.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -19921,7 +20014,7 @@ snapshots: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 '@types/jsdom': 20.0.1 - '@types/node': 18.19.87 + '@types/node': 20.19.0 jest-mock: 29.7.0 jest-util: 29.7.0 jsdom: 20.0.3 @@ -20138,12 +20231,12 @@ snapshots: - supports-color - ts-node - jest@29.7.0(@types/node@20.19.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3)): + jest@29.7.0(@types/node@24.10.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): dependencies: - '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3)) + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@20.19.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3)) + jest-cli: 29.7.0(@types/node@24.10.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -20176,7 +20269,7 @@ snapshots: jsdom@20.0.3: dependencies: abab: 2.0.6 - acorn: 8.14.1 + acorn: 8.15.0 acorn-globals: 7.0.1 cssom: 0.5.0 cssstyle: 2.3.0 @@ -21571,6 +21664,22 @@ snapshots: postcss: 8.5.3 ts-node: 10.9.2(@types/node@20.19.0)(typescript@5.9.3) + postcss-load-config@3.1.4(postcss@8.5.3)(ts-node@10.9.2(@types/node@24.10.1)(typescript@4.9.5)): + dependencies: + lilconfig: 2.1.0 + yaml: 1.10.2 + optionalDependencies: + postcss: 8.5.3 + ts-node: 10.9.2(@types/node@24.10.1)(typescript@4.9.5) + + postcss-load-config@3.1.4(postcss@8.5.3)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): + dependencies: + lilconfig: 2.1.0 + yaml: 1.10.2 + optionalDependencies: + postcss: 8.5.3 + ts-node: 10.9.2(@types/node@24.10.1)(typescript@5.9.3) + postcss-load-config@4.0.2(postcss@8.5.3)(ts-node@10.9.2(@types/node@17.0.21)(typescript@5.9.3)): dependencies: lilconfig: 3.1.3 @@ -21587,13 +21696,13 @@ snapshots: postcss: 8.5.3 ts-node: 10.9.2(@types/node@20.19.0)(typescript@4.9.5) - postcss-load-config@4.0.2(postcss@8.5.3)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3)): + postcss-load-config@4.0.2(postcss@8.5.3)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): dependencies: lilconfig: 3.1.3 yaml: 2.7.1 optionalDependencies: postcss: 8.5.3 - ts-node: 10.9.2(@types/node@20.19.0)(typescript@5.9.3) + ts-node: 10.9.2(@types/node@24.10.1)(typescript@5.9.3) postcss-nested@6.2.0(postcss@8.5.3): dependencies: @@ -22961,7 +23070,7 @@ snapshots: stripe@17.7.0: dependencies: - '@types/node': 18.19.87 + '@types/node': 20.19.0 qs: 6.14.0 strnum@1.1.2: @@ -23053,9 +23162,9 @@ snapshots: dependencies: tailwindcss: 3.4.17(ts-node@10.9.2(@types/node@20.19.0)(typescript@4.9.5)) - tailwindcss-animate@1.0.7(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3))): + tailwindcss-animate@1.0.7(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3))): dependencies: - tailwindcss: 3.4.17(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3)) + tailwindcss: 3.4.17(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) tailwindcss@3.4.17(ts-node@10.9.2(@types/node@17.0.21)(typescript@5.9.3)): dependencies: @@ -23111,7 +23220,7 @@ snapshots: transitivePeerDependencies: - ts-node - tailwindcss@3.4.17(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3)): + tailwindcss@3.4.17(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -23130,7 +23239,7 @@ snapshots: postcss: 8.5.3 postcss-import: 15.1.0(postcss@8.5.3) postcss-js: 4.0.1(postcss@8.5.3) - postcss-load-config: 4.0.2(postcss@8.5.3)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3)) + postcss-load-config: 4.0.2(postcss@8.5.3)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) postcss-nested: 6.2.0(postcss@8.5.3) postcss-selector-parser: 6.1.2 resolve: 1.22.10 @@ -23259,12 +23368,12 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-jest@29.4.4(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(esbuild@0.19.12)(jest-util@29.7.0)(jest@29.7.0(@types/node@20.19.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3)))(typescript@5.9.3): + ts-jest@29.4.4(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(esbuild@0.19.12)(jest-util@29.7.0)(jest@29.7.0(@types/node@24.10.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)))(typescript@5.9.3): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 handlebars: 4.7.8 - jest: 29.7.0(@types/node@20.19.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3)) + jest: 29.7.0(@types/node@24.10.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 @@ -23308,7 +23417,7 @@ snapshots: '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 '@types/node': 17.0.21 - acorn: 8.14.1 + acorn: 8.15.0 acorn-walk: 8.3.4 arg: 4.1.3 create-require: 1.1.1 @@ -23327,7 +23436,7 @@ snapshots: '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 '@types/node': 18.19.87 - acorn: 8.14.1 + acorn: 8.15.0 acorn-walk: 8.3.4 arg: 4.1.3 create-require: 1.1.1 @@ -23346,7 +23455,7 @@ snapshots: '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 '@types/node': 20.19.0 - acorn: 8.14.1 + acorn: 8.15.0 acorn-walk: 8.3.4 arg: 4.1.3 create-require: 1.1.1 @@ -23365,7 +23474,45 @@ snapshots: '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 '@types/node': 20.19.0 - acorn: 8.14.1 + acorn: 8.15.0 + acorn-walk: 8.3.4 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.9.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optional: true + + ts-node@10.9.2(@types/node@24.10.1)(typescript@4.9.5): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 24.10.1 + acorn: 8.15.0 + acorn-walk: 8.3.4 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 4.9.5 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optional: true + + ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 24.10.1 + acorn: 8.15.0 acorn-walk: 8.3.4 arg: 4.1.3 create-require: 1.1.1 @@ -23402,7 +23549,7 @@ snapshots: bundle-require: 4.2.1(esbuild@0.17.19) cac: 6.7.14 chokidar: 3.6.0 - debug: 4.4.0 + debug: 4.4.1 esbuild: 0.17.19 execa: 5.1.1 globby: 11.1.0 @@ -23425,7 +23572,7 @@ snapshots: bundle-require: 4.2.1(esbuild@0.17.19) cac: 6.7.14 chokidar: 3.6.0 - debug: 4.4.0 + debug: 4.4.1 esbuild: 0.17.19 execa: 5.1.1 globby: 11.1.0 @@ -23443,7 +23590,53 @@ snapshots: - supports-color - ts-node - tsup@7.3.0(postcss@8.5.3)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3))(typescript@5.9.3): + tsup@6.6.0(postcss@8.5.3)(ts-node@10.9.2(@types/node@24.10.1)(typescript@4.9.5))(typescript@4.9.5): + dependencies: + bundle-require: 4.2.1(esbuild@0.17.19) + cac: 6.7.14 + chokidar: 3.6.0 + debug: 4.4.1 + esbuild: 0.17.19 + execa: 5.1.1 + globby: 11.1.0 + joycon: 3.1.1 + postcss-load-config: 3.1.4(postcss@8.5.3)(ts-node@10.9.2(@types/node@24.10.1)(typescript@4.9.5)) + resolve-from: 5.0.0 + rollup: 3.29.5 + source-map: 0.8.0-beta.0 + sucrase: 3.35.0 + tree-kill: 1.2.2 + optionalDependencies: + postcss: 8.5.3 + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + - ts-node + + tsup@6.6.0(postcss@8.5.3)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3))(typescript@5.9.3): + dependencies: + bundle-require: 4.2.1(esbuild@0.17.19) + cac: 6.7.14 + chokidar: 3.6.0 + debug: 4.4.1 + esbuild: 0.17.19 + execa: 5.1.1 + globby: 11.1.0 + joycon: 3.1.1 + postcss-load-config: 3.1.4(postcss@8.5.3)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) + resolve-from: 5.0.0 + rollup: 3.29.5 + source-map: 0.8.0-beta.0 + sucrase: 3.35.0 + tree-kill: 1.2.2 + optionalDependencies: + postcss: 8.5.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + - ts-node + + tsup@7.3.0(postcss@8.5.3)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3))(typescript@5.9.3): dependencies: bundle-require: 4.2.1(esbuild@0.19.12) cac: 6.7.14 @@ -23453,7 +23646,7 @@ snapshots: execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 - postcss-load-config: 4.0.2(postcss@8.5.3)(ts-node@10.9.2(@types/node@20.19.0)(typescript@5.9.3)) + postcss-load-config: 4.0.2(postcss@8.5.3)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) resolve-from: 5.0.0 rollup: 4.40.0 source-map: 0.8.0-beta.0 @@ -23597,6 +23790,8 @@ snapshots: undici-types@6.21.0: {} + undici-types@7.16.0: {} + unherit@3.0.1: {} unified@10.1.2: From 96347016abf84802d3cbad9ae76943e7abb702f2 Mon Sep 17 00:00:00 2001 From: Rajat Date: Fri, 28 Nov 2025 09:48:50 +0000 Subject: [PATCH 2/4] Lint fixes --- packages/orm-models/package.json | 2 +- packages/scripts/package.json | 46 ++++++++++++++++---------------- packages/scripts/tsconfig.json | 21 ++++----------- 3 files changed, 29 insertions(+), 40 deletions(-) diff --git a/packages/orm-models/package.json b/packages/orm-models/package.json index a6872f582..8dd370a4e 100644 --- a/packages/orm-models/package.json +++ b/packages/orm-models/package.json @@ -47,4 +47,4 @@ "@courselit/email-editor": "workspace:^", "mongoose": "^8.13.1" } -} \ No newline at end of file +} diff --git a/packages/scripts/package.json b/packages/scripts/package.json index 48365ecd1..0b368d61d 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -1,24 +1,24 @@ { - "name": "@courselit/scripts", - "version": "0.0.1", - "description": "", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "domain:cleanup": "ts-node src/cleanup-domain.ts", - "build": "tsc" - }, - "keywords": [], - "author": "", - "license": "ISC", - "packageManager": "pnpm@10.22.0", - "dependencies": { - "mongoose": "^8.14.0" - }, - "devDependencies": { - "@types/node": "^24.10.1", - "ts-node": "^10.9.2", - "tsconfig": "workspace:^", - "@courselit/orm-models": "workspace:^" - } -} \ No newline at end of file + "name": "@courselit/scripts", + "version": "0.0.1", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "domain:cleanup": "ts-node src/cleanup-domain.ts", + "build": "tsc" + }, + "keywords": [], + "author": "", + "license": "ISC", + "packageManager": "pnpm@10.22.0", + "dependencies": { + "mongoose": "^8.14.0" + }, + "devDependencies": { + "@types/node": "^24.10.1", + "ts-node": "^10.9.2", + "tsconfig": "workspace:^", + "@courselit/orm-models": "workspace:^" + } +} diff --git a/packages/scripts/tsconfig.json b/packages/scripts/tsconfig.json index bbe7228e7..ab1ef5cd9 100644 --- a/packages/scripts/tsconfig.json +++ b/packages/scripts/tsconfig.json @@ -1,24 +1,13 @@ { "extends": "tsconfig/node.json", - "exclude": [ - "dist", - "build", - "node_modules" - ], + "exclude": ["dist", "build", "node_modules"], "compilerOptions": { "outDir": "./dist", - "lib": [ - "dom" - ], + "lib": ["dom"], "strict": false, "paths": { - "@/*": [ - "./src/*" - ] + "@/*": ["./src/*"] } }, - "include": [ - "src/**/*", - "additional.d.ts" - ] -} \ No newline at end of file + "include": ["src/**/*", "additional.d.ts"] +} From 18f1972efc59bbd0d020b3288b341a3620d330e3 Mon Sep 17 00:00:00 2001 From: Rajat Date: Sun, 30 Nov 2025 17:18:45 +0000 Subject: [PATCH 3/4] clean up script --- apps/web/models/Widget.ts | 5 - apps/web/next-env.d.ts | 2 +- packages/orm-models/package.json | 3 +- packages/orm-models/src/index.ts | 20 ++ packages/orm-models/src/models/activity.ts | 31 ++ packages/orm-models/src/models/apikey.ts | 29 ++ .../src/models/certificate-template.ts | 43 +++ packages/orm-models/src/models/certificate.ts | 28 ++ .../src/models/community-comment.ts | 76 +++++ .../orm-models/src/models/community-media.ts | 10 + .../src/models/community-post-subscriber.ts | 31 ++ .../orm-models/src/models/community-post.ts | 60 ++++ .../orm-models/src/models/community-report.ts | 57 ++++ packages/orm-models/src/models/community.ts | 53 +++ .../orm-models/src/models/download-link.ts | 28 ++ packages/orm-models/src/models/invoice.ts | 34 ++ .../src/models/lesson-evaluation.ts | 21 ++ packages/orm-models/src/models/lesson.ts | 45 +++ .../orm-models/src/models/notification.ts | 85 +++++ .../orm-models/src/models/ongoing-sequence.ts | 20 ++ packages/orm-models/src/models/page.ts | 66 ++++ .../orm-models/src/models/payment-plan.ts | 76 +++++ packages/orm-models/src/models/theme.ts | 120 +++++++ packages/orm-models/src/models/user-theme.ts | 38 +++ packages/orm-models/src/models/widget.ts | 16 + packages/scripts/.gitignore | 4 +- packages/scripts/package.json | 10 +- packages/scripts/src/cleanup-domain.ts | 320 +++++++++++++++++- packages/utils/src/extract-media-ids.ts | 32 ++ packages/utils/src/index.ts | 1 + pnpm-lock.yaml | 12 + 31 files changed, 1363 insertions(+), 13 deletions(-) create mode 100644 packages/orm-models/src/models/activity.ts create mode 100644 packages/orm-models/src/models/apikey.ts create mode 100644 packages/orm-models/src/models/certificate-template.ts create mode 100644 packages/orm-models/src/models/certificate.ts create mode 100644 packages/orm-models/src/models/community-comment.ts create mode 100644 packages/orm-models/src/models/community-media.ts create mode 100644 packages/orm-models/src/models/community-post-subscriber.ts create mode 100644 packages/orm-models/src/models/community-post.ts create mode 100644 packages/orm-models/src/models/community-report.ts create mode 100644 packages/orm-models/src/models/community.ts create mode 100644 packages/orm-models/src/models/download-link.ts create mode 100644 packages/orm-models/src/models/invoice.ts create mode 100644 packages/orm-models/src/models/lesson-evaluation.ts create mode 100644 packages/orm-models/src/models/lesson.ts create mode 100644 packages/orm-models/src/models/notification.ts create mode 100644 packages/orm-models/src/models/ongoing-sequence.ts create mode 100644 packages/orm-models/src/models/page.ts create mode 100644 packages/orm-models/src/models/payment-plan.ts create mode 100644 packages/orm-models/src/models/theme.ts create mode 100644 packages/orm-models/src/models/user-theme.ts create mode 100644 packages/orm-models/src/models/widget.ts create mode 100644 packages/utils/src/extract-media-ids.ts diff --git a/apps/web/models/Widget.ts b/apps/web/models/Widget.ts index 5066b22d1..b6ac6c132 100644 --- a/apps/web/models/Widget.ts +++ b/apps/web/models/Widget.ts @@ -9,8 +9,3 @@ export const WidgetSchema = new mongoose.Schema({ shared: { type: Boolean, required: true, default: false }, settings: mongoose.Schema.Types.Mixed, }); - -const WidgetModel = - mongoose.models.Widget || mongoose.model("Widget", WidgetSchema); - -export default WidgetModel; diff --git a/apps/web/next-env.d.ts b/apps/web/next-env.d.ts index 9edff1c7c..c4b7818fb 100644 --- a/apps/web/next-env.d.ts +++ b/apps/web/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/packages/orm-models/package.json b/packages/orm-models/package.json index 8dd370a4e..4738105f6 100644 --- a/packages/orm-models/package.json +++ b/packages/orm-models/package.json @@ -45,6 +45,7 @@ "@courselit/common-models": "workspace:^", "@courselit/utils": "workspace:^", "@courselit/email-editor": "workspace:^", + "@courselit/page-models": "workspace:^", "mongoose": "^8.13.1" } -} +} \ No newline at end of file diff --git a/packages/orm-models/src/index.ts b/packages/orm-models/src/index.ts index 0bd1fa915..31a300cbb 100644 --- a/packages/orm-models/src/index.ts +++ b/packages/orm-models/src/index.ts @@ -12,3 +12,23 @@ export * from "./models/email-event"; export * from "./models/subscriber"; export * from "./models/domain"; export * from "./models/site-info"; +export * from "./models/lesson"; +export * from "./models/certificate"; +export * from "./models/certificate-template"; +export * from "./models/payment-plan"; +export * from "./models/activity"; +export * from "./models/lesson-evaluation"; +export * from "./models/page"; +export * from "./models/community"; +export * from "./models/community-report"; +export * from "./models/community-post-subscriber"; +export * from "./models/community-comment"; +export * from "./models/community-media"; +export * from "./models/community-post"; +export * from "./models/invoice"; +export * from "./models/theme"; +export * from "./models/ongoing-sequence"; +export * from "./models/notification"; +export * from "./models/download-link"; +export * from "./models/apikey"; +export * from "./models/user-theme"; diff --git a/packages/orm-models/src/models/activity.ts b/packages/orm-models/src/models/activity.ts new file mode 100644 index 000000000..6b5b821ff --- /dev/null +++ b/packages/orm-models/src/models/activity.ts @@ -0,0 +1,31 @@ +import mongoose from "mongoose"; +import { ActivityType, Constants } from "@courselit/common-models"; + +export interface Activity { + domain: mongoose.Types.ObjectId; + userId: string; + type: ActivityType; + entityId?: string; + metadata?: Record; + createdAt?: Date; + updatedAt?: Date; +} + +export const ActivitySchema = new mongoose.Schema( + { + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + userId: { type: String, required: true }, + type: { + type: String, + required: true, + enum: Object.values(Constants.ActivityType), + }, + entityId: { type: String }, + metadata: { type: mongoose.Schema.Types.Mixed, default: {} }, + }, + { + timestamps: true, + }, +); + +ActivitySchema.index({ domain: 1, type: 1, createdAt: 1 }); diff --git a/packages/orm-models/src/models/apikey.ts b/packages/orm-models/src/models/apikey.ts new file mode 100644 index 000000000..675581ecf --- /dev/null +++ b/packages/orm-models/src/models/apikey.ts @@ -0,0 +1,29 @@ +import { generateUniqueId } from "@courselit/utils"; +import mongoose from "mongoose"; + +export interface ApiKey { + domain: mongoose.Types.ObjectId; + keyId: string; + name: string; + key: string; +} + +export const ApiKeySchema = new mongoose.Schema( + { + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + keyId: { type: String, required: true, default: generateUniqueId }, + name: { type: String, required: true }, + key: { type: String, required: true, default: generateUniqueId }, + }, + { + timestamps: true, + }, +); + +ApiKeySchema.index( + { + domain: 1, + name: 1, + }, + { unique: true }, +); diff --git a/packages/orm-models/src/models/certificate-template.ts b/packages/orm-models/src/models/certificate-template.ts new file mode 100644 index 000000000..8718ee000 --- /dev/null +++ b/packages/orm-models/src/models/certificate-template.ts @@ -0,0 +1,43 @@ +import { Media } from "@courselit/common-models"; +import { generateUniqueId } from "@courselit/utils"; +import mongoose from "mongoose"; +import { MediaSchema } from "./media"; + +export interface InternalCertificateTemplate { + domain: mongoose.Types.ObjectId; + templateId: string; + courseId: string; + title: string; + subtitle: string; + description: string; + signatureImage: Media; + signatureName: string; + signatureDesignation?: string; + logo?: Media; + createdAt: Date; + updatedAt: Date; +} + +export const CertificateTemplateSchema = + new mongoose.Schema( + { + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + templateId: { + type: String, + required: true, + unique: true, + default: generateUniqueId, + }, + courseId: { type: String, required: true }, + title: { type: String, required: true }, + subtitle: { type: String, required: true }, + description: { type: String, required: true }, + signatureImage: MediaSchema, + signatureName: { type: String, required: true }, + signatureDesignation: { type: String }, + logo: MediaSchema, + }, + { + timestamps: true, + }, + ); diff --git a/packages/orm-models/src/models/certificate.ts b/packages/orm-models/src/models/certificate.ts new file mode 100644 index 000000000..5aa503530 --- /dev/null +++ b/packages/orm-models/src/models/certificate.ts @@ -0,0 +1,28 @@ +import { generateUniqueId } from "@courselit/utils"; +import mongoose from "mongoose"; + +export interface InternalCertificate { + domain: mongoose.Types.ObjectId; + certificateId: string; + userId: string; + courseId: string; + createdAt: Date; + updatedAt: Date; +} + +export const CertificateSchema = new mongoose.Schema( + { + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + certificateId: { + type: String, + required: true, + unique: true, + default: generateUniqueId, + }, + userId: { type: String, required: true }, + courseId: { type: String, required: true }, + }, + { + timestamps: true, + }, +); diff --git a/packages/orm-models/src/models/community-comment.ts b/packages/orm-models/src/models/community-comment.ts new file mode 100644 index 000000000..f980fb0e3 --- /dev/null +++ b/packages/orm-models/src/models/community-comment.ts @@ -0,0 +1,76 @@ +import { generateUniqueId } from "@courselit/utils"; +import mongoose from "mongoose"; +import { CommunityMediaSchema } from "./community-media"; +import { + CommunityComment, + CommunityCommentReply, +} from "@courselit/common-models"; + +export interface InternalCommunityComment + extends Pick< + CommunityComment, + "communityId" | "postId" | "commentId" | "content" | "media" + > { + domain: mongoose.Types.ObjectId; + userId: string; + likes: string[]; + replies: InternalReply[]; + deleted: boolean; +} + +export interface InternalReply + extends Omit { + userId: string; + likes: string[]; +} + +export const ReplySchema = new mongoose.Schema( + { + userId: { type: String, required: true }, + content: { type: String, required: true }, + media: [CommunityMediaSchema], + replyId: { type: String, required: true, default: generateUniqueId }, + parentReplyId: { type: String, default: null }, + likes: [String], + deleted: { type: Boolean, default: false }, + }, + { + timestamps: true, + }, +); + +export const CommunityCommentSchema = + new mongoose.Schema( + { + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + userId: { type: String, required: true }, + communityId: { type: String, required: true }, + postId: { type: String, required: true }, + commentId: { + type: String, + required: true, + unique: true, + default: generateUniqueId, + }, + content: { type: String, required: true }, + media: [CommunityMediaSchema], + likes: [String], + replies: [ReplySchema], + deleted: { type: Boolean, required: true, default: false }, + }, + { + timestamps: true, + }, + ); + +CommunityCommentSchema.statics.paginatedFind = async function ( + filter, + options, +) { + const page = options.page || 1; + const limit = options.limit || 10; + const skip = (page - 1) * limit; + + const docs = await this.find(filter).skip(skip).limit(limit).exec(); + return docs; +}; diff --git a/packages/orm-models/src/models/community-media.ts b/packages/orm-models/src/models/community-media.ts new file mode 100644 index 000000000..33cd5ccc5 --- /dev/null +++ b/packages/orm-models/src/models/community-media.ts @@ -0,0 +1,10 @@ +import mongoose from "mongoose"; +import { MediaSchema } from "./media"; +import { CommunityMedia } from "@courselit/common-models"; + +export const CommunityMediaSchema = new mongoose.Schema({ + type: { type: String, required: true }, + title: { type: String, required: true }, + url: { type: String }, + media: MediaSchema, +}); diff --git a/packages/orm-models/src/models/community-post-subscriber.ts b/packages/orm-models/src/models/community-post-subscriber.ts new file mode 100644 index 000000000..c17087d84 --- /dev/null +++ b/packages/orm-models/src/models/community-post-subscriber.ts @@ -0,0 +1,31 @@ +import { generateUniqueId } from "@courselit/utils"; +import mongoose from "mongoose"; + +export interface CommunityPostSubscriber { + domain: mongoose.Types.ObjectId; + subscriptionId: string; + postId: string; + userId: string; + subscription: boolean; +} + +export const CommunityPostSubscriberSchema = + new mongoose.Schema( + { + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + subscriptionId: { + type: String, + required: true, + unique: true, + default: generateUniqueId, + }, + postId: { type: String, required: true }, + userId: { type: String, required: true }, + subscription: { type: Boolean, default: true }, + }, + { + timestamps: true, + }, + ); + +CommunityPostSubscriberSchema.index({ postId: 1, userId: 1 }, { unique: true }); diff --git a/packages/orm-models/src/models/community-post.ts b/packages/orm-models/src/models/community-post.ts new file mode 100644 index 000000000..ee6799969 --- /dev/null +++ b/packages/orm-models/src/models/community-post.ts @@ -0,0 +1,60 @@ +import { generateUniqueId } from "@courselit/utils"; +import mongoose from "mongoose"; +import { CommunityPost } from "@courselit/common-models"; +import { CommunityMediaSchema } from "./community-media"; + +export interface InternalCommunityPost + extends Pick< + CommunityPost, + | "title" + | "communityId" + | "postId" + | "content" + | "category" + | "media" + | "pinned" + | "deleted" + > { + domain: mongoose.Types.ObjectId; + userId: string; + likes: string[]; + createdAt: string; + updatedAt: string; +} + +export const CommunityPostSchema = new mongoose.Schema( + { + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + userId: { type: String, required: true }, + communityId: { type: String, required: true }, + postId: { + type: String, + required: true, + unique: true, + default: generateUniqueId, + }, + title: { type: String, required: true }, + content: { type: String, required: true }, + category: String, + media: [CommunityMediaSchema], + pinned: { type: Boolean, default: false }, + likes: [String], + deleted: { type: Boolean, default: false }, + }, + { + timestamps: true, + }, +); + +CommunityPostSchema.statics.paginatedFind = async function (filter, options) { + const page = options.page || 1; + const limit = options.limit || 10; + const skip = (page - 1) * limit; + + const docs = await this.find(filter) + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit) + .exec(); + return docs; +}; diff --git a/packages/orm-models/src/models/community-report.ts b/packages/orm-models/src/models/community-report.ts new file mode 100644 index 000000000..15e153d6a --- /dev/null +++ b/packages/orm-models/src/models/community-report.ts @@ -0,0 +1,57 @@ +import { CommunityReport, Constants } from "@courselit/common-models"; +import { generateUniqueId } from "@courselit/utils"; +import mongoose from "mongoose"; + +export interface InternalCommunityReport + extends Omit, + mongoose.Document { + domain: mongoose.Types.ObjectId; + userId: string; + contentId: string; +} + +export const CommunityReportSchema = + new mongoose.Schema( + { + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + communityId: { type: String, required: true }, + reportId: { + type: String, + required: true, + unique: true, + default: generateUniqueId, + }, + contentId: { type: String, required: true }, + type: { type: String, required: true }, + reason: { type: String, required: true }, + status: { + type: String, + required: true, + default: Constants.CommunityReportStatus.PENDING, + }, + userId: { type: String, required: true }, + contentParentId: { type: String }, + rejectionReason: { type: String }, + }, + { + timestamps: true, + }, + ); + +CommunityReportSchema.statics.paginatedFind = async function (filter, options) { + const page = options.page || 1; + const limit = options.limit || 10; + const skip = (page - 1) * limit; + + const docs = await this.find(filter) + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit) + .exec(); + return docs; +}; + +CommunityReportSchema.index( + { communityId: 1, contentId: 1, userId: 1 }, + { unique: true }, +); diff --git a/packages/orm-models/src/models/community.ts b/packages/orm-models/src/models/community.ts new file mode 100644 index 000000000..d9ca52da8 --- /dev/null +++ b/packages/orm-models/src/models/community.ts @@ -0,0 +1,53 @@ +import { Community } from "@courselit/common-models"; +import { generateUniqueId } from "@courselit/utils"; +import mongoose from "mongoose"; +import { MediaSchema } from "./media"; + +export interface InternalCommunity extends Omit { + domain: mongoose.Types.ObjectId; + createdAt: Date; + updatedAt: Date; + deleted: boolean; +} + +export const CommunitySchema = new mongoose.Schema( + { + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + communityId: { + type: String, + required: true, + unique: true, + default: generateUniqueId, + }, + name: { type: String, required: true }, + description: { type: mongoose.Schema.Types.Mixed, default: null }, + banner: { type: mongoose.Schema.Types.Mixed, default: null }, + enabled: { type: Boolean, default: false }, + categories: { type: [String], default: ["General"] }, + autoAcceptMembers: { type: Boolean, default: false }, + joiningReasonText: { type: String }, + pageId: { type: String, required: true }, + defaultPaymentPlan: { type: String }, + featuredImage: MediaSchema, + deleted: { type: Boolean, default: false }, + }, + { + timestamps: true, + }, +); + +CommunitySchema.index({ domain: 1, name: 1 }, { unique: true }); + +CommunitySchema.statics.paginatedFind = async function (filter, options) { + const page = options.page || 1; + const limit = options.limit || 10; + const sort = options.sort || -1; + const skip = (page - 1) * limit; + + const docs = await this.find(filter) + .sort({ createdAt: sort }) + .skip(skip) + .limit(limit) + .exec(); + return docs; +}; diff --git a/packages/orm-models/src/models/download-link.ts b/packages/orm-models/src/models/download-link.ts new file mode 100644 index 000000000..25e16a888 --- /dev/null +++ b/packages/orm-models/src/models/download-link.ts @@ -0,0 +1,28 @@ +import mongoose from "mongoose"; + +export interface DownloadLink { + domain: mongoose.Types.ObjectId; + courseId: string; + userId: string; + token: string; + expiresAt: Date; + consumed: boolean; +} + +// const generateUniqueToken = (): string => +// randomBytes(constants.downLoadLinkLength).toString("hex"); + +// const getDateAfter24Hours = (): Date => { +// const now = new Date(); +// now.setDate(now.getDate() + constants.downLoadLinkExpiresInDays); +// return now; +// }; + +export const DownloadLinkSchema = new mongoose.Schema({ + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + courseId: { type: String, required: true }, + userId: { type: String, required: true }, + token: { type: String, required: true }, + expiresAt: { type: Date, required: true }, + consumed: { type: Boolean, required: true, default: false }, +}); diff --git a/packages/orm-models/src/models/invoice.ts b/packages/orm-models/src/models/invoice.ts new file mode 100644 index 000000000..885ad951c --- /dev/null +++ b/packages/orm-models/src/models/invoice.ts @@ -0,0 +1,34 @@ +import { Constants, Invoice } from "@courselit/common-models"; +import { generateUniqueId } from "@courselit/utils"; +import mongoose from "mongoose"; + +export interface InternalInvoice extends Invoice { + domain: mongoose.Types.ObjectId; +} + +export const InvoiceSchema = new mongoose.Schema( + { + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + invoiceId: { + type: String, + required: true, + unique: true, + default: generateUniqueId, + }, + membershipId: { type: String, required: true }, + membershipSessionId: { type: String, required: true }, + amount: { type: Number, required: true }, + status: { + type: String, + enum: Object.values(Constants.InvoiceStatus), + default: Constants.InvoiceStatus.PENDING, + }, + paymentProcessor: { type: String, required: true }, + paymentProcessorEntityId: { type: String }, + paymentProcessorTransactionId: { type: String }, + currencyISOCode: { type: String, required: true }, + }, + { + timestamps: true, + }, +); diff --git a/packages/orm-models/src/models/lesson-evaluation.ts b/packages/orm-models/src/models/lesson-evaluation.ts new file mode 100644 index 000000000..d7fe082a5 --- /dev/null +++ b/packages/orm-models/src/models/lesson-evaluation.ts @@ -0,0 +1,21 @@ +import mongoose from "mongoose"; + +export interface LessonEvaluation { + domain: mongoose.Types.ObjectId; + lessonId: string; + userId: string; + pass: boolean; + requiresPassingGrade: boolean; + score?: number; + passingGrade?: number; +} + +export const LessonEvaluationSchema = new mongoose.Schema({ + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + lessonId: { type: String, required: true }, + userId: { type: String, required: true }, + pass: { type: Boolean, required: true }, + requiresPassingGrade: { type: Boolean, required: true }, + score: { type: Number }, + passingGrade: { type: Number }, +}); diff --git a/packages/orm-models/src/models/lesson.ts b/packages/orm-models/src/models/lesson.ts new file mode 100644 index 000000000..32b3a6b15 --- /dev/null +++ b/packages/orm-models/src/models/lesson.ts @@ -0,0 +1,45 @@ +import { + LessonType, + Media, + Quiz, + TextEditorContent, + Constants, +} from "@courselit/common-models"; +import { generateUniqueId } from "@courselit/utils"; +import mongoose from "mongoose"; +import { MediaSchema } from "./media"; + +export interface InternalLesson { + id: mongoose.Types.ObjectId; + domain: mongoose.Types.ObjectId; + lessonId: string; + title: string; + type: LessonType; + content?: Quiz | TextEditorContent | { value: string }; + media?: Media; + downloadable: boolean; + creatorId: string; + courseId: string; + requiresEnrollment: boolean; + published: boolean; + groupId: string; +} + +export const LessonSchema = new mongoose.Schema({ + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + lessonId: { type: String, required: true, default: generateUniqueId }, + title: { type: String, required: true }, + type: { + type: String, + required: true, + enum: Object.values(Constants.LessonType), + }, + content: { type: mongoose.Schema.Types.Mixed, default: {} }, + media: MediaSchema, + downloadable: { type: Boolean, default: false }, + creatorId: { type: String, required: true }, + courseId: { type: String, required: true }, + requiresEnrollment: { type: Boolean, default: true }, + published: { type: Boolean, required: true, default: false }, + groupId: { type: String, required: true }, +}); diff --git a/packages/orm-models/src/models/notification.ts b/packages/orm-models/src/models/notification.ts new file mode 100644 index 000000000..db50bab71 --- /dev/null +++ b/packages/orm-models/src/models/notification.ts @@ -0,0 +1,85 @@ +import { + Constants, + Notification, + NotificationEntityAction, +} from "@courselit/common-models"; +import { generateUniqueId } from "@courselit/utils"; +import mongoose from "mongoose"; + +export interface InternalNotification + extends Omit, + mongoose.Document { + domain: mongoose.Types.ObjectId; + notificationId: string; + userId: string; + entityAction: NotificationEntityAction; + entityId: string; + read: boolean; + createdAt: Date; + updatedAt: Date; + entityTargetId?: string; +} + +export const NotificationSchema = new mongoose.Schema( + { + domain: { + type: mongoose.Schema.Types.ObjectId, + required: true, + }, + notificationId: { + type: String, + required: true, + default: generateUniqueId, + unique: true, + }, + userId: { + type: String, + required: true, + ref: "User", + }, + forUserId: { + type: String, + required: true, + ref: "User", + }, + entityAction: { + type: String, + required: true, + enum: Object.values(Constants.NotificationEntityAction), + }, + entityId: { + type: String, + required: true, + }, + read: { + type: Boolean, + default: false, + }, + entityTargetId: { + type: String, + }, + }, + { + timestamps: true, + }, +); + +NotificationSchema.statics.paginate = async function (userId, options) { + const page = options.page || 1; + const limit = options.limit || 10; + const skip = (page - 1) * limit; + + const query = { + forUserId: userId, + }; + + const notifications = await this.find(query) + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit) + .lean(); + + const total = await this.countDocuments(query); + + return { notifications, total }; +}; diff --git a/packages/orm-models/src/models/ongoing-sequence.ts b/packages/orm-models/src/models/ongoing-sequence.ts new file mode 100644 index 000000000..f3f2d0e9a --- /dev/null +++ b/packages/orm-models/src/models/ongoing-sequence.ts @@ -0,0 +1,20 @@ +import { OngoingSequence } from "@courselit/common-models"; +import mongoose, { Schema } from "mongoose"; + +export const OngoingSequenceSchema: Schema = new Schema< + OngoingSequence & { domain: mongoose.Schema.Types.ObjectId } +>( + { + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + sequenceId: { type: String, required: true, index: true }, + userId: { type: String, required: true }, + nextEmailScheduledTime: { type: Number, required: true }, + retryCount: { type: Number, required: true, default: 0 }, + sentEmailIds: { type: [String] }, + }, + { + timestamps: true, + }, +); + +OngoingSequenceSchema.index({ sequenceId: 1, userId: 1 }, { unique: true }); diff --git a/packages/orm-models/src/models/page.ts b/packages/orm-models/src/models/page.ts new file mode 100644 index 000000000..479af3b26 --- /dev/null +++ b/packages/orm-models/src/models/page.ts @@ -0,0 +1,66 @@ +import mongoose from "mongoose"; +import { WidgetSchema } from "./widget"; +import { + WidgetInstance, + Page as PublicPage, + Media, + Constants, +} from "@courselit/common-models"; +import { MediaSchema } from "./media"; + +const { PageType } = Constants; + +export interface InternalPage extends PublicPage { + id: mongoose.Types.ObjectId; + domain: mongoose.Types.ObjectId; + draftLayout: WidgetInstance[]; + creatorId: string; + draftTitle?: string; + draftDescription?: string; + draftSocialImage?: Media; + draftRobotsAllowed?: boolean; +} + +export const PageSchema = new mongoose.Schema( + { + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + pageId: { type: String, required: true }, + type: { + type: String, + required: true, + enum: [ + PageType.PRODUCT, + PageType.SITE, + PageType.BLOG, + PageType.COMMUNITY, + ], + default: PageType.PRODUCT, + }, + creatorId: { type: String, required: true }, + name: { type: String, required: true }, + layout: { type: [WidgetSchema], default: [] }, + draftLayout: { type: [WidgetSchema], default: [] }, + entityId: { type: String }, + deleteable: { type: Boolean, required: true, default: false }, + title: { type: String }, + description: String, + socialImage: MediaSchema, + robotsAllowed: { type: Boolean, default: true }, + draftTitle: String, + draftDescription: String, + draftSocialImage: MediaSchema, + draftRobotsAllowed: Boolean, + deleted: { type: Boolean, default: false }, + }, + { + timestamps: true, + }, +); + +PageSchema.index( + { + domain: 1, + pageId: 1, + }, + { unique: true }, +); diff --git a/packages/orm-models/src/models/payment-plan.ts b/packages/orm-models/src/models/payment-plan.ts new file mode 100644 index 000000000..e3f02bfb4 --- /dev/null +++ b/packages/orm-models/src/models/payment-plan.ts @@ -0,0 +1,76 @@ +import { Constants, PaymentPlan } from "@courselit/common-models"; +import { generateUniqueId } from "@courselit/utils"; +import mongoose from "mongoose"; + +export interface InternalPaymentPlan extends PaymentPlan { + domain: mongoose.Types.ObjectId; + userId: string; + archived: boolean; + internal: boolean; +} + +export const PaymentPlanSchema = new mongoose.Schema( + { + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + planId: { + type: String, + required: true, + unique: true, + default: generateUniqueId, + }, + name: { type: String, required: true }, + type: { + type: String, + required: true, + enum: Object.values(Constants.PaymentPlanType), + }, + entityId: { type: String, required: true }, + entityType: { + type: String, + required: true, + enum: Object.values(Constants.MembershipEntityType), + }, + userId: { type: String, required: true }, + oneTimeAmount: { type: Number }, + emiAmount: { type: Number }, + emiTotalInstallments: { type: Number }, + subscriptionMonthlyAmount: { type: Number }, + subscriptionYearlyAmount: { type: Number }, + archived: { type: Boolean, default: false }, + internal: { type: Boolean, default: false }, + description: { type: String }, + includedProducts: { type: [String], default: [] }, + }, + { + timestamps: true, + }, +); + +PaymentPlanSchema.pre("save", async function (next) { + if (this.internal) { + const existingInternalPlan = await (this.constructor as any).findOne({ + domain: this.domain, + internal: true, + _id: { $ne: this._id }, + }); + + if (existingInternalPlan) { + const error = new Error( + "Only one internal payment plan allowed per domain", + ); + return next(error); + } + + if (this.type !== Constants.PaymentPlanType.FREE) { + const error = new Error("Internal payment plans must be free"); + return next(error); + } + } + next(); +}); + +// Add indexes for common query patterns +PaymentPlanSchema.index({ domain: 1, entityId: 1, entityType: 1, archived: 1 }); +PaymentPlanSchema.index({ domain: 1, internal: 1 }); +PaymentPlanSchema.index({ domain: 1, planId: 1, archived: 1 }); +PaymentPlanSchema.index({ domain: 1, archived: 1, type: 1 }); diff --git a/packages/orm-models/src/models/theme.ts b/packages/orm-models/src/models/theme.ts new file mode 100644 index 000000000..1482f1f74 --- /dev/null +++ b/packages/orm-models/src/models/theme.ts @@ -0,0 +1,120 @@ +import { ThemeStyle } from "@courselit/page-models"; +import mongoose from "mongoose"; + +const TypographySchema = new mongoose.Schema( + { + fontFamily: { type: String, required: true }, + fontSize: { type: String, required: true }, + fontWeight: { type: String, required: true }, + lineHeight: { type: String }, + letterSpacing: { type: String }, + textTransform: { type: String }, + textDecoration: { type: String }, + textOverflow: { type: String }, + }, + { _id: false }, +); + +const ColorsSchema = new mongoose.Schema( + { + background: { type: String, required: true }, + foreground: { type: String, required: true }, + card: { type: String, required: true }, + cardForeground: { type: String, required: true }, + popover: { type: String, required: true }, + popoverForeground: { type: String, required: true }, + primary: { type: String, required: true }, + primaryForeground: { type: String, required: true }, + secondary: { type: String, required: true }, + secondaryForeground: { type: String, required: true }, + muted: { type: String, required: true }, + mutedForeground: { type: String, required: true }, + accent: { type: String, required: true }, + accentForeground: { type: String, required: true }, + destructive: { type: String, required: true }, + border: { type: String, required: true }, + input: { type: String, required: true }, + ring: { type: String, required: true }, + chart1: { type: String, required: true }, + chart2: { type: String, required: true }, + chart3: { type: String, required: true }, + chart4: { type: String, required: true }, + chart5: { type: String, required: true }, + sidebar: { type: String, required: true }, + sidebarForeground: { type: String, required: true }, + sidebarPrimary: { type: String, required: true }, + sidebarPrimaryForeground: { type: String, required: true }, + sidebarAccent: { type: String, required: true }, + sidebarAccentForeground: { type: String, required: true }, + sidebarBorder: { type: String, required: true }, + sidebarRing: { type: String, required: true }, + shadow2xs: { type: String, required: true }, + shadowXs: { type: String, required: true }, + shadowSm: { type: String, required: true }, + shadowMd: { type: String, required: true }, + shadowLg: { type: String, required: true }, + shadowXl: { type: String, required: true }, + shadow2xl: { type: String, required: true }, + }, + { _id: false }, +); + +export const ThemeSchema = new mongoose.Schema({ + colors: { + type: { + light: { type: ColorsSchema, required: true }, + dark: { type: ColorsSchema, required: true }, + }, + _id: false, + }, + typography: { + type: { + preheader: { type: TypographySchema, required: true }, + header1: { type: TypographySchema, required: true }, + header2: { type: TypographySchema, required: true }, + header3: { type: TypographySchema, required: true }, + header4: { type: TypographySchema, required: true }, + subheader1: { type: TypographySchema, required: true }, + subheader2: { type: TypographySchema, required: true }, + text1: { type: TypographySchema, required: true }, + text2: { type: TypographySchema, required: true }, + link: { type: TypographySchema, required: true }, + button: { type: TypographySchema, required: true }, + input: { type: TypographySchema, required: true }, + caption: { type: TypographySchema, required: true }, + }, + _id: false, + }, + interactives: { + type: { + button: { type: mongoose.Schema.Types.Mixed, required: true }, + link: { type: mongoose.Schema.Types.Mixed, required: true }, + card: { type: mongoose.Schema.Types.Mixed, required: true }, + input: { type: mongoose.Schema.Types.Mixed, required: true }, + }, + _id: false, + }, + structure: { + type: { + page: { + type: { + width: { type: String, required: true }, + }, + required: true, + _id: false, + }, + section: { + type: { + padding: { + type: mongoose.Schema.Types.Mixed, + required: true, + }, + }, + required: true, + _id: false, + }, + }, + required: true, + _id: false, + }, +}); diff --git a/packages/orm-models/src/models/user-theme.ts b/packages/orm-models/src/models/user-theme.ts new file mode 100644 index 000000000..3a890885b --- /dev/null +++ b/packages/orm-models/src/models/user-theme.ts @@ -0,0 +1,38 @@ +import { generateUniqueId } from "@courselit/utils"; +import mongoose from "mongoose"; +import { ThemeSchema } from "./theme"; +import { Theme, ThemeStyle } from "@courselit/page-models"; + +export interface UserTheme { + themeId: Theme["id"]; + name: Theme["name"]; + parentThemeId: string; + userId: string; + theme: ThemeStyle; + draftTheme: ThemeStyle; + createdAt: Date; + updatedAt: Date; +} + +export interface InternalUserTheme extends UserTheme { + domain: mongoose.Types.ObjectId; +} + +export const UserThemeSchema = new mongoose.Schema({ + domain: { type: mongoose.Schema.Types.ObjectId, required: true }, + themeId: { + type: String, + required: true, + unique: true, + default: generateUniqueId, + }, + name: { type: String, required: true }, + parentThemeId: { type: String, required: true }, + userId: { type: String, required: true }, + theme: { type: ThemeSchema, required: true }, + draftTheme: { type: ThemeSchema, required: true }, + createdAt: { type: Date, default: Date.now }, + updatedAt: { type: Date, default: Date.now }, +}); + +UserThemeSchema.index({ domain: 1, name: 1 }, { unique: true }); diff --git a/packages/orm-models/src/models/widget.ts b/packages/orm-models/src/models/widget.ts new file mode 100644 index 000000000..5066b22d1 --- /dev/null +++ b/packages/orm-models/src/models/widget.ts @@ -0,0 +1,16 @@ +import { WidgetInstance } from "@courselit/common-models"; +import { generateUniqueId } from "@courselit/utils"; +import mongoose from "mongoose"; + +export const WidgetSchema = new mongoose.Schema({ + widgetId: { type: String, required: true, default: generateUniqueId }, + name: { type: String, required: true }, + deleteable: { type: Boolean, required: true, default: true }, + shared: { type: Boolean, required: true, default: false }, + settings: mongoose.Schema.Types.Mixed, +}); + +const WidgetModel = + mongoose.models.Widget || mongoose.model("Widget", WidgetSchema); + +export default WidgetModel; diff --git a/packages/scripts/.gitignore b/packages/scripts/.gitignore index a4b426ea9..3db3e1aed 100644 --- a/packages/scripts/.gitignore +++ b/packages/scripts/.gitignore @@ -1,3 +1,5 @@ node_modules/ -dist/ \ No newline at end of file +dist/ + +.env* \ No newline at end of file diff --git a/packages/scripts/package.json b/packages/scripts/package.json index 0b368d61d..63c71ff6e 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -3,6 +3,7 @@ "version": "0.0.1", "description": "", "main": "index.js", + "type": "module", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "domain:cleanup": "ts-node src/cleanup-domain.ts", @@ -13,12 +14,15 @@ "license": "ISC", "packageManager": "pnpm@10.22.0", "dependencies": { + "medialit": "^0.1.0", "mongoose": "^8.14.0" }, "devDependencies": { + "@courselit/orm-models": "workspace:^", + "@courselit/common-models": "workspace:^", + "@courselit/utils": "workspace:^", "@types/node": "^24.10.1", "ts-node": "^10.9.2", - "tsconfig": "workspace:^", - "@courselit/orm-models": "workspace:^" + "tsconfig": "workspace:^" } -} +} \ No newline at end of file diff --git a/packages/scripts/src/cleanup-domain.ts b/packages/scripts/src/cleanup-domain.ts index 9d6a4fefa..c3e8f6c19 100644 --- a/packages/scripts/src/cleanup-domain.ts +++ b/packages/scripts/src/cleanup-domain.ts @@ -1,5 +1,57 @@ import mongoose from "mongoose"; -import { DomainSchema } from "@courselit/orm-models"; +import { + CourseSchema, + DomainSchema, + LessonSchema, + CertificateTemplateSchema, + CertificateSchema, + MembershipSchema, + PaymentPlanSchema, + ActivitySchema, + LessonEvaluationSchema, + PageSchema, + CommunitySchema, + CommunityReportSchema, + CommunityPostSubscriberSchema, + CommunityCommentSchema, + CommunityPostSchema, + InvoiceSchema, + UserSchema, + SequenceSchema, + UserSegmentSchema, + OngoingSequenceSchema, + NotificationSchema, + RuleSchema, + EmailEventSchema, + EmailDeliverySchema, + DownloadLinkSchema, + ApiKeySchema, + UserThemeSchema, +} from "@courselit/orm-models"; +import type { + InternalCertificateTemplate, + InternalCourse, + InternalLesson, + InternalPage, + InternalCommunity, + InternalUser, +} from "@courselit/orm-models"; +import { loadEnvFile } from "node:process"; +import { MediaLit } from "medialit"; +import { extractMediaIDs } from "@courselit/utils"; +import CommonModels from "@courselit/common-models"; +const { CommunityMediaTypes } = CommonModels; + +function getMediaLitClient() { + const medialit = new MediaLit({ + apiKey: process.env.MEDIALIT_APIKEY, + endpoint: process.env.MEDIALIT_SERVER, + }); + + return medialit; +} + +loadEnvFile(); if (!process.env.DB_CONNECTION_STRING) { throw new Error("DB_CONNECTION_STRING is not set"); @@ -11,7 +63,51 @@ if (!process.argv[2]) { mongoose.connect(process.env.DB_CONNECTION_STRING); +async function deleteMedia(mediaId: string) { + try { + const medialitClient = getMediaLitClient(); + await medialitClient.delete(mediaId); + } catch (error) { + console.log("Can't delete media", mediaId, error); + } +} + const DomainModel = mongoose.model("Domain", DomainSchema); +const CourseModel = mongoose.model("Course", CourseSchema); +const LessonModel = mongoose.model("Lesson", LessonSchema); +const CertificateTemplateModel = mongoose.model( + "CertificateTemplate", + CertificateTemplateSchema, +); +const CertificateModel = mongoose.model("Certificate", CertificateSchema); +const MembershipModel = mongoose.model("Membership", MembershipSchema); +const PaymentPlanModel = mongoose.model("PaymentPlan", PaymentPlanSchema); +const ActivityModel = mongoose.model("Activity", ActivitySchema); +const LessonEvaluationModel = mongoose.model( + "LessonEvaluation", + LessonEvaluationSchema, +); +const PageModel = mongoose.model("Page", PageSchema); +const CommunityModel = mongoose.model("Community", CommunitySchema); +const CommunityPostSubscriberModel = mongoose.model( + "CommunityPostSubscriber", + CommunityPostSubscriberSchema, +); +const InvoiceModel = mongoose.model("Invoice", InvoiceSchema); +const UserModel = mongoose.model("User", UserSchema); +const SequenceModel = mongoose.model("Sequence", SequenceSchema); +const UserSegmentModel = mongoose.model("UserSegment", UserSegmentSchema); +const UserThemeModel = mongoose.model("UserTheme", UserThemeSchema); +const OngoingSequenceModel = mongoose.model( + "OngoingSequence", + OngoingSequenceSchema, +); +const NotificationModel = mongoose.model("Notification", NotificationSchema); +const RuleModel = mongoose.model("Rule", RuleSchema); +const EmailEventModel = mongoose.model("EmailEvent", EmailEventSchema); +const EmailDeliveryModel = mongoose.model("EmailDelivery", EmailDeliverySchema); +const DownloadLinkModel = mongoose.model("DownloadLink", DownloadLinkSchema); +const ApiKeyModel = mongoose.model("ApiKey", ApiKeySchema); async function cleanupDomain(name: string) { const domain = await DomainModel.findOne({ name }); @@ -19,7 +115,227 @@ async function cleanupDomain(name: string) { console.log("Domain not found"); return; } - console.log(domain); + + await ActivityModel.deleteMany({ domain: domain._id }); + await InvoiceModel.deleteMany({ domain: domain._id }); + await MembershipModel.deleteMany({ domain: domain._id }); + await PaymentPlanModel.deleteMany({ domain: domain._id }); + await CommunityReportModel.deleteMany({ domain: domain._id }); + await CommunityCommentModel.deleteMany({ domain: domain._id }); + await CertificateModel.deleteMany({ domain: domain._id }); + await LessonEvaluationModel.deleteMany({ domain: domain._id }); + await UserSegmentModel.deleteMany({ domain: domain._id }); + await UserThemeModel.deleteMany({ domain: domain._id }); + await NotificationModel.deleteMany({ domain: domain._id }); + await RuleModel.deleteMany({ domain: domain._id }); + await OngoingSequenceModel.deleteMany({ domain: domain._id }); + await SequenceModel.deleteMany({ domain: domain._id }); + await EmailEventModel.deleteMany({ domain: domain._id }); + await EmailDeliveryModel.deleteMany({ domain: domain._id }); + await DownloadLinkModel.deleteMany({ domain: domain._id }); + await ApiKeyModel.deleteMany({ domain: domain._id }); + + const products = (await CourseModel.find({ + domain: domain._id, + }).lean()) as InternalCourse[]; + for (const product of products) { + await deleteProduct({ product, domain: domain._id }); + } + + await CommunityPostSubscriberModel.deleteMany({ domain: domain._id }); + const communities = (await CommunityModel.find({ + domain: domain._id, + }).lean()) as InternalCommunity[]; + for (const community of communities) { + await deleteCommunity({ community, domain: domain._id }); + } + + const usersWithAvatar = (await UserModel.find({ + domain: domain._id, + avatar: { $exists: true }, + }).lean()) as InternalUser[]; + for (const user of usersWithAvatar) { + await deleteMedia(user.avatar.mediaId); + } + await UserModel.deleteMany({ domain: domain._id }); + + const mediaToBeDeleted = extractMediaIDs(JSON.stringify(domain)); + for (const mediaId of Array.from(mediaToBeDeleted)) { + await deleteMedia(mediaId); + } + await DomainModel.deleteOne({ _id: domain._id }); +} + +async function deleteProduct({ + product, + domain, +}: { + product: InternalCourse; + domain: mongoose.Types.ObjectId; +}) { + const certificateTemplate = + await CertificateTemplateModel.findOne( + { + domain, + courseId: product.courseId, + }, + ); + if (certificateTemplate?.signatureImage?.mediaId) { + await deleteMedia(certificateTemplate.signatureImage.mediaId); + } + if (certificateTemplate?.logo?.mediaId) { + await deleteMedia(certificateTemplate.logo.mediaId); + } + await CertificateTemplateModel.deleteOne({ + domain, + courseId: product.courseId, + }); + await deleteLessons(product.courseId, domain); + if (product.featuredImage) { + await deleteMedia(product.featuredImage.mediaId); + } + if (product.description) { + const extractedMediaIds = extractMediaIDs(product.description || ""); + for (const mediaId of Array.from(extractedMediaIds)) { + await deleteMedia(mediaId); + } + } + await deletePage({ + pageId: product.pageId, + domain, + }); + await CourseModel.deleteOne({ + domain, + courseId: product.courseId, + }); +} + +async function deletePage({ + pageId, + domain, +}: { + pageId: string; + domain: mongoose.Types.ObjectId; +}) { + const page = (await PageModel.findOne({ + pageId, + domain, + }).lean()) as InternalPage; + + if (!page) { + return; + } + + const mediaToBeDeleted = extractMediaIDs(JSON.stringify(page)); + for (const mediaId of Array.from(mediaToBeDeleted)) { + console.log("Page media", mediaId); + await deleteMedia(mediaId); + } + + await PageModel.deleteOne({ + domain, + pageId, + }); +} + +async function deleteLessons(id: string, domain: mongoose.Types.ObjectId) { + const lessons = (await LessonModel.find({ + courseId: id, + domain, + }).lean()) as InternalLesson[]; + for (const lesson of lessons) { + if (lesson.media?.mediaId) { + await deleteMedia(lesson.media.mediaId); + } + if (lesson.content) { + const extractedMediaIds = extractMediaIDs( + JSON.stringify(lesson.content), + ); + for (const mediaId of Array.from(extractedMediaIds)) { + await deleteMedia(mediaId); + } + } + } + await LessonModel.deleteMany({ courseId: id, domain }); +} + +const CommunityReportModel = mongoose.model( + "CommunityReport", + CommunityReportSchema, +); +const CommunityCommentModel = mongoose.model( + "CommunityComment", + CommunityCommentSchema, +); +const CommunityPostModel = mongoose.model("CommunityPost", CommunityPostSchema); + +async function deleteCommunity({ + community, + domain, +}: { + community: InternalCommunity; + domain: mongoose.Types.ObjectId; +}) { + const mediaTypesToDelete = [ + CommunityMediaTypes.IMAGE, + CommunityMediaTypes.VIDEO, + CommunityMediaTypes.GIF, + CommunityMediaTypes.PDF, + ]; + const postsWithMedia = await CommunityPostModel.aggregate<{ + media: CommonModels.CommunityMedia[]; + }>([ + { + $match: { + domain, + communityId: community.communityId, + "media.type": { $in: mediaTypesToDelete }, + }, + }, + { + $project: { + media: { + $filter: { + input: "$media", + as: "media", + cond: { + $and: [ + { + $in: ["$$media.type", mediaTypesToDelete], + }, + { $ifNull: ["$$media.media.mediaId", false] }, + ], + }, + }, + }, + }, + }, + ]); + for (const post of postsWithMedia) { + for (const media of post.media) { + console.log("Post media", media); + const mediaId = media.media?.mediaId; + if (mediaId) { + await deleteMedia(mediaId); + } + } + } + await CommunityPostModel.deleteMany({ + domain, + communityId: community.communityId, + }); + await deletePage({ + pageId: community.pageId, + domain, + }); + const mediaToBeDeleted = extractMediaIDs(JSON.stringify(community)); + for (const mediaId of Array.from(mediaToBeDeleted)) { + await deleteMedia(mediaId); + } + await CommunityModel.deleteOne({ + domain, + communityId: community.communityId, + }); } (async () => { diff --git a/packages/utils/src/extract-media-ids.ts b/packages/utils/src/extract-media-ids.ts new file mode 100644 index 000000000..af0640cce --- /dev/null +++ b/packages/utils/src/extract-media-ids.ts @@ -0,0 +1,32 @@ +export function extractMediaIDs(doc: string): Set { + const mediaIds = new Set(); + + const regex = /https?:\/\/[^\s"']+/gi; + let match: RegExpExecArray | null; + while ((match = regex.exec(doc)) !== null) { + const url = match[0]; + + try { + const { pathname } = new URL(url); + const segments = pathname.split("/").filter(Boolean); + + if (segments.length < 2) { + continue; + } + + const lastSegment = segments[segments.length - 1]; + if (!/^main\.[^/]+$/i.test(lastSegment)) { + continue; + } + + const mediaId = segments[segments.length - 2]; + if (mediaId) { + mediaIds.add(mediaId); + } + } catch { + continue; + } + } + + return mediaIds; +} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index ff63bed2c..8ac6c0da5 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -11,3 +11,4 @@ export { default as jwtUtils } from "./jwt-utils"; export { getPlanPrice } from "./get-plan-price"; export { truncate } from "./truncate"; export { isVideo } from "./is-video"; +export { extractMediaIDs } from "./extract-media-ids"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4a5e8e0ca..571c03446 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -816,6 +816,9 @@ importers: '@courselit/email-editor': specifier: workspace:^ version: link:../email-editor + '@courselit/page-models': + specifier: workspace:^ + version: link:../page-models '@courselit/utils': specifier: workspace:^ version: link:../utils @@ -1023,13 +1026,22 @@ importers: packages/scripts: dependencies: + medialit: + specifier: ^0.1.0 + version: 0.1.0 mongoose: specifier: ^8.14.0 version: 8.14.0(@aws-sdk/credential-providers@3.797.0)(socks@2.8.4) devDependencies: + '@courselit/common-models': + specifier: workspace:^ + version: link:../common-models '@courselit/orm-models': specifier: workspace:^ version: link:../orm-models + '@courselit/utils': + specifier: workspace:^ + version: link:../utils '@types/node': specifier: ^24.10.1 version: 24.10.1 From 4fccb6f09ecdec2dbb4901d7aa2c3e4bc2b5ff34 Mon Sep 17 00:00:00 2001 From: Rajat Date: Sun, 30 Nov 2025 17:19:24 +0000 Subject: [PATCH 4/4] Lint fixes --- packages/orm-models/package.json | 2 +- packages/scripts/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/orm-models/package.json b/packages/orm-models/package.json index 4738105f6..d0b8e4920 100644 --- a/packages/orm-models/package.json +++ b/packages/orm-models/package.json @@ -48,4 +48,4 @@ "@courselit/page-models": "workspace:^", "mongoose": "^8.13.1" } -} \ No newline at end of file +} diff --git a/packages/scripts/package.json b/packages/scripts/package.json index 63c71ff6e..4ef1cad81 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -25,4 +25,4 @@ "ts-node": "^10.9.2", "tsconfig": "workspace:^" } -} \ No newline at end of file +}