// server/models/member.js import mongoose from "mongoose"; import { resolve } from "path"; import { fileURLToPath } from "url"; const __dirname = fileURLToPath(new URL(".", import.meta.url)); // Import configs using dynamic imports to avoid build issues const getValidCircleValues = () => ["community", "founder", "practitioner"]; const getValidContributionValues = () => ["0", "5", "15", "30", "50"]; const memberSchema = new mongoose.Schema({ email: { type: String, required: true, unique: true }, emailHistory: [ { email: { type: String, required: true }, changedAt: { type: Date, default: Date.now }, }, ], name: { type: String, required: true }, circle: { type: String, enum: getValidCircleValues(), required: true, }, contributionTier: { type: String, enum: getValidContributionValues(), required: true, }, role: { type: String, enum: ["member", "admin"], default: "member", }, status: { type: String, enum: ["pending_payment", "active", "suspended", "cancelled", "guest"], default: "pending_payment", }, helcimCustomerId: String, helcimSubscriptionId: String, billingCadence: { type: String, enum: ['monthly', 'annual'], default: 'monthly', }, paymentMethod: { type: String, enum: ["card", "bank", "none"], default: "none", }, subscriptionStartDate: Date, subscriptionEndDate: Date, nextBillingDate: Date, slackInvited: { type: Boolean, default: false }, slackInviteStatus: { type: String, enum: ["pending", "sent", "failed", "accepted", "joined"], default: "pending", }, slackUserId: String, // Profile fields pronouns: String, timeZone: String, avatar: String, studio: String, bio: String, location: String, socialLinks: { mastodon: String, linkedin: String, website: String, other: String, }, showInDirectory: { type: Boolean, default: true }, craftTags: [String], board: { slackHandle: String, }, notifications: { events: { type: Boolean, default: true }, }, inviteEmailSent: { type: Boolean, default: false }, inviteEmailSentAt: Date, agreement: { acceptedAt: Date, }, // Magic link single-use enforcement magicLinkJti: String, magicLinkJtiUsed: { type: Boolean, default: false }, // Session revocation via token versioning tokenVersion: { type: Number, default: 0 }, memberNumber: { type: Number, unique: true, sparse: true }, onboarding: { completedAt: { type: Date, default: null }, eventPageVisited: { type: Boolean, default: false }, boardPageVisited: { type: Boolean, default: false }, wikiClicked: { type: Boolean, default: false }, skipped: { profileTags: { type: Boolean, default: false }, visitEvent: { type: Boolean, default: false }, board: { type: Boolean, default: false }, wiki: { type: Boolean, default: false }, }, }, createdAt: { type: Date, default: Date.now }, lastLogin: Date, }); // Check if model already exists to prevent re-compilation in development export default mongoose.models.Member || mongoose.model("Member", memberSchema);