Add missing schemas, member model fields, and import endpoint
Adds memberInviteSchema and bulkMemberImportSchema needed by the invite and CSV import endpoints. Adds inviteEmailSent/inviteEmailSentAt fields to member model. Adds the bulk import API route.
This commit is contained in:
parent
5b4ca1b41d
commit
2705d171bd
3 changed files with 70 additions and 0 deletions
53
server/api/admin/members/import.post.js
Normal file
53
server/api/admin/members/import.post.js
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import Member from '../../../models/member.js'
|
||||
import { connectDB } from '../../../utils/mongoose.js'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
await requireAdmin(event)
|
||||
const { members } = await validateBody(event, bulkMemberImportSchema)
|
||||
await connectDB()
|
||||
|
||||
// Check for duplicate emails within the batch
|
||||
const batchEmails = members.map(m => m.email)
|
||||
const uniqueEmails = new Set(batchEmails)
|
||||
if (uniqueEmails.size !== batchEmails.length) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'Duplicate emails found in import batch'
|
||||
})
|
||||
}
|
||||
|
||||
// Check which emails already exist in the DB
|
||||
const existingMembers = await Member.find(
|
||||
{ email: { $in: batchEmails } },
|
||||
{ email: 1 }
|
||||
)
|
||||
const existingEmails = new Set(existingMembers.map(m => m.email))
|
||||
|
||||
const results = []
|
||||
|
||||
for (const row of members) {
|
||||
if (existingEmails.has(row.email)) {
|
||||
results.push({ email: row.email, success: false, error: 'Email already exists' })
|
||||
continue
|
||||
}
|
||||
|
||||
try {
|
||||
const member = new Member({
|
||||
name: row.name,
|
||||
email: row.email,
|
||||
circle: row.circle,
|
||||
contributionTier: row.contributionTier,
|
||||
slackInvited: false
|
||||
})
|
||||
const saved = await member.save()
|
||||
results.push({ email: row.email, success: true, memberId: saved._id })
|
||||
} catch (err) {
|
||||
results.push({ email: row.email, success: false, error: err.message })
|
||||
}
|
||||
}
|
||||
|
||||
const created = results.filter(r => r.success).length
|
||||
const failed = results.filter(r => !r.success).length
|
||||
|
||||
return { created, failed, results }
|
||||
})
|
||||
|
|
@ -133,6 +133,9 @@ const memberSchema = new mongoose.Schema({
|
|||
},
|
||||
},
|
||||
|
||||
inviteEmailSent: { type: Boolean, default: false },
|
||||
inviteEmailSentAt: Date,
|
||||
|
||||
createdAt: { type: Date, default: Date.now },
|
||||
lastLogin: Date,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -330,3 +330,17 @@ export const adminMemberCreateSchema = z.object({
|
|||
export const adminRoleUpdateSchema = z.object({
|
||||
role: z.enum(['admin', 'member'])
|
||||
})
|
||||
|
||||
export const bulkMemberImportSchema = z.object({
|
||||
members: z.array(z.object({
|
||||
name: z.string().min(1).max(200),
|
||||
email: z.string().trim().toLowerCase().email(),
|
||||
circle: z.enum(['community', 'founder', 'practitioner']),
|
||||
contributionTier: z.enum(['0', '5', '15', '30', '50'])
|
||||
})).min(1).max(100)
|
||||
})
|
||||
|
||||
export const memberInviteSchema = z.object({
|
||||
memberIds: z.array(z.string().min(1)).min(1).max(100),
|
||||
emailTemplate: z.string().min(1).max(10000)
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue