Spaces:
Running
Running
Download src/models/Voucher.js from AndreaNotes1/auth-app-api: direct link, hf CLI and curl.
- Browser
- Download file 4.1 kB
-
https://huggingface.co/spaces/AndreaNotes1/auth-app-api/resolve/main/src/models/Voucher.js
- Command line
-
hf download hf://spaces/AndreaNotes1/auth-app-api/src/models/Voucher.js
-
curl -L -o Voucher.js https://huggingface.co/spaces/AndreaNotes1/auth-app-api/resolve/main/src/models/Voucher.js
4.1 kB
| const mongoose = require("mongoose"); | |
| // Records that a specific user redeemed a specific voucher, and which | |
| // (free) order that redemption produced β kept as a subdocument so a | |
| // voucher's full redemption history is visible in one place without a | |
| // second query. | |
| const redemptionSchema = new mongoose.Schema( | |
| { | |
| user: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true }, | |
| order: { type: mongoose.Schema.Types.ObjectId, ref: "Order", required: true }, | |
| redeemedAt: { type: Date, default: Date.now }, | |
| }, | |
| { _id: false } | |
| ); | |
| const voucherSchema = new mongoose.Schema( | |
| { | |
| // Always stored upper-cased so lookups are case-insensitive without | |
| // needing a collation β the code itself is not secret in the sense a | |
| // password is, but it IS a bearer credential, so it's still generated | |
| // with real randomness (see utils/voucherCode.js) rather than | |
| // something guessable. | |
| code: { type: String, required: true, unique: true, uppercase: true, trim: true, index: true }, | |
| course: { type: mongoose.Schema.Types.ObjectId, ref: "Course", required: true, index: true }, | |
| createdBy: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true }, | |
| // How many different users may redeem this single code. Defaults to | |
| // 1 (one voucher == one seat), but an admin can raise this to hand | |
| // the same code to a small group (e.g. a workshop cohort) instead of | |
| // generating one per person. | |
| maxRedemptions: { type: Number, default: 1, min: 1, max: 10000 }, | |
| redemptions: { type: [redemptionSchema], default: [] }, | |
| // Optional allow-list of emails (lower-cased) this voucher may be | |
| // redeemed by. Empty array = anyone who has the code may redeem it | |
| // (up to maxRedemptions). Non-empty = only these accounts may, which | |
| // is how an admin "gives a voucher to specific users" rather than | |
| // just publishing an open promo code. | |
| restrictedToEmails: { type: [String], default: [] }, | |
| expiresAt: { type: Date, default: null }, | |
| isActive: { type: Boolean, default: true }, | |
| // Free-text admin memo, e.g. "Scholarship cohort β Fall 2026". Never | |
| // shown to the redeeming user. | |
| note: { type: String, default: "", maxlength: 500 }, | |
| }, | |
| { timestamps: true } | |
| ); | |
| voucherSchema.methods.isExpired = function isExpired() { | |
| return !!(this.expiresAt && this.expiresAt.getTime() < Date.now()); | |
| }; | |
| voucherSchema.methods.hasRemainingRedemptions = function hasRemainingRedemptions() { | |
| return this.redemptions.length < this.maxRedemptions; | |
| }; | |
| voucherSchema.methods.isRedeemableBy = function isRedeemableBy(email) { | |
| if (this.restrictedToEmails.length === 0) return true; | |
| return this.restrictedToEmails.includes(String(email).toLowerCase().trim()); | |
| }; | |
| voucherSchema.methods.hasBeenRedeemedBy = function hasBeenRedeemedBy(userId) { | |
| return this.redemptions.some((r) => r.user.toString() === userId.toString()); | |
| }; | |
| /** Why (if at all) this voucher currently can't be redeemed β null means it's valid to try. */ | |
| voucherSchema.methods.redemptionBlockReason = function redemptionBlockReason(user) { | |
| if (!this.isActive) return "This voucher has been deactivated."; | |
| if (this.isExpired()) return "This voucher has expired."; | |
| if (!this.hasRemainingRedemptions()) return "This voucher has already been fully redeemed."; | |
| if (!this.isRedeemableBy(user.email)) return "This voucher isn't assigned to your account."; | |
| if (this.hasBeenRedeemedBy(user._id)) return "You've already redeemed this voucher."; | |
| return null; | |
| }; | |
| voucherSchema.methods.toAdminJSON = function toAdminJSON() { | |
| return { | |
| id: this._id, | |
| code: this.code, | |
| course: this.course, | |
| createdBy: this.createdBy, | |
| maxRedemptions: this.maxRedemptions, | |
| redemptionsCount: this.redemptions.length, | |
| redemptions: this.redemptions, | |
| restrictedToEmails: this.restrictedToEmails, | |
| expiresAt: this.expiresAt, | |
| isActive: this.isActive, | |
| isExpired: this.isExpired(), | |
| note: this.note, | |
| createdAt: this.createdAt, | |
| }; | |
| }; | |
| module.exports = mongoose.model("Voucher", voucherSchema); | |