Spaces:
Running
Running
Download src/models/Course.js from AndreaNotes1/auth-app-api: direct link, hf CLI and curl.
- Browser
- Download file 5.11 kB
-
https://huggingface.co/spaces/AndreaNotes1/auth-app-api/resolve/main/src/models/Course.js
- Command line
-
hf download hf://spaces/AndreaNotes1/auth-app-api/src/models/Course.js
-
curl -L -o Course.js https://huggingface.co/spaces/AndreaNotes1/auth-app-api/resolve/main/src/models/Course.js
5.11 kB
| const mongoose = require("mongoose"); | |
| const CATEGORIES = [ | |
| "Artificial Intelligence", | |
| "Cloud Computing", | |
| "Quantum Computing", | |
| "Cyber Security", | |
| "Networking", | |
| "Systems Engineering", | |
| "Professional Ethics", | |
| "Robotics", | |
| ]; | |
| const lessonSchema = new mongoose.Schema( | |
| { | |
| title: { type: String, required: true, trim: true, maxlength: 200 }, | |
| description: { type: String, default: "", maxlength: 1000 }, | |
| type: { type: String, enum: ["video", "document"], required: true }, | |
| order: { type: Number, default: 0 }, | |
| // Free preview lessons can be viewed without purchasing the course | |
| // (e.g. an intro video) — everything else requires confirmed access. | |
| isPreview: { type: Boolean, default: false }, | |
| // Cloudinary storage details. Uploaded with delivery type | |
| // "authenticated" so files can NEVER be fetched by guessing/sharing | |
| // the public ID — every playback URL must be freshly signed | |
| // server-side (see utils/cloudinaryUpload.js) for a user who is | |
| // confirmed to have access. These are plain (selected-by-default) | |
| // fields — deliberately NOT using Mongoose's `select: false` here, | |
| // because that projection option is unreliable two levels deep | |
| // inside an array-of-subdocuments-within-an-array-of-subdocuments | |
| // (curriculum[].lessons[].field) across Mongoose versions. The real | |
| // security boundary is that every response is built through | |
| // toCurriculumJSON()/toLockedCurriculumJSON() below, which simply | |
| // never copies these fields into what gets sent to the client. | |
| cloudinaryPublicId: { type: String, required: true }, | |
| cloudinaryResourceType: { type: String, enum: ["video", "raw", "image"], required: true }, | |
| format: { type: String, default: "" }, | |
| bytes: { type: Number, default: 0 }, | |
| durationSeconds: { type: Number, default: 0 }, | |
| }, | |
| { timestamps: true } | |
| ); | |
| const sectionSchema = new mongoose.Schema( | |
| { | |
| title: { type: String, required: true, trim: true, maxlength: 200 }, | |
| order: { type: Number, default: 0 }, | |
| lessons: { type: [lessonSchema], default: [] }, | |
| }, | |
| { timestamps: true } | |
| ); | |
| const courseSchema = new mongoose.Schema( | |
| { | |
| title: { type: String, required: true, trim: true, maxlength: 200 }, | |
| slug: { type: String, required: true, unique: true, lowercase: true, trim: true, index: true }, | |
| price: { type: Number, required: true, min: 0 }, | |
| currency: { type: String, default: "USD", uppercase: true, minlength: 3, maxlength: 3 }, | |
| duration: { type: String, required: true, trim: true }, | |
| level: { type: String, required: true, trim: true }, | |
| description: { type: String, required: true }, | |
| category: { type: String, enum: CATEGORIES, required: true, index: true }, | |
| // Marketing thumbnail shown on the homepage "Our Academy" preview and | |
| // on the Academy catalogue page. Uploaded by an admin as an actual | |
| // image file (see content.controller.js -> uploadThumbnail), stored | |
| // in Cloudinary as a plain PUBLIC asset (type: "upload") — unlike | |
| // lesson videos/documents, a thumbnail is meant to be publicly | |
| // visible to any visitor browsing the storefront, so it deliberately | |
| // does NOT use the authenticated/signed-URL scheme those use. | |
| image: { type: String, default: "" }, | |
| thumbnailPublicId: { type: String, default: "" }, | |
| highlights: { type: [String], default: [] }, | |
| isPublished: { type: Boolean, default: true }, | |
| // Study content, uploaded exclusively by admins (see content.controller.js). | |
| curriculum: { type: [sectionSchema], default: [] }, | |
| }, | |
| { timestamps: true } | |
| ); | |
| /** Curriculum view for enrolled users / admins: structure + metadata, no storage internals. */ | |
| courseSchema.methods.toCurriculumJSON = function toCurriculumJSON() { | |
| return this.curriculum | |
| .slice() | |
| .sort((a, b) => a.order - b.order) | |
| .map((section) => ({ | |
| id: section._id, | |
| title: section.title, | |
| order: section.order, | |
| lessons: section.lessons | |
| .slice() | |
| .sort((a, b) => a.order - b.order) | |
| .map((lesson) => ({ | |
| id: lesson._id, | |
| title: lesson.title, | |
| description: lesson.description, | |
| type: lesson.type, | |
| order: lesson.order, | |
| isPreview: lesson.isPreview, | |
| durationSeconds: lesson.durationSeconds, | |
| })), | |
| })); | |
| }; | |
| /** Locked view for logged-in users who have NOT purchased the course: titles only, previews flagged. */ | |
| courseSchema.methods.toLockedCurriculumJSON = function toLockedCurriculumJSON() { | |
| return this.toCurriculumJSON().map((section) => ({ | |
| ...section, | |
| lessons: section.lessons.map((lesson) => ({ | |
| id: lesson.id, | |
| title: lesson.title, | |
| type: lesson.type, | |
| order: lesson.order, | |
| isPreview: lesson.isPreview, | |
| durationSeconds: lesson.durationSeconds, | |
| locked: !lesson.isPreview, | |
| })), | |
| })); | |
| }; | |
| module.exports = mongoose.model("Course", courseSchema); | |
| module.exports.CATEGORIES = CATEGORIES; | |