- Created complete project directory structure: - Frontend with Next.js 15.2.3 app router structure - Backend with Express API endpoints - Docker configurations with security best practices - MongoDB initialization with schema validation - Nginx configuration with security headers - Implemented core file templates: - Custom video player with transcript display options - MongoDB schemas with validation for Videos and Members - JWT authentication middleware with role-based access control - Docker Compose with resource limits and security - Frontend/backend package.json with dependencies - Updated documentation: - Enhanced TECH_STACK with latest version details - Updated TO_DO.txt with WCAG 2.2 AA requirements - Updated planned_implementation.txt with current best practices - Refreshed all Memory Bank files with current state - Security and accessibility improvements: - Added MongoDB 7.0 schema validation with - Enhanced video requirements with mandatory transcripts and thumbnails - Updated accessibility to WCAG 2.2 AA standards - Added security best practices for Docker deployments
217 lines
6.4 KiB
JavaScript
217 lines
6.4 KiB
JavaScript
// MongoDB initialization script - runs when container starts
|
|
|
|
// Create database
|
|
db = db.getSiblingDB('ocd_db');
|
|
|
|
// Create admin user if it doesn't exist
|
|
if (db.getUser('admin') == null) {
|
|
db.createUser({
|
|
user: 'admin',
|
|
pwd: process.env.MONGO_ADMIN_PASSWORD || 'admin',
|
|
roles: [{ role: 'readWrite', db: 'ocd_db' }]
|
|
});
|
|
}
|
|
|
|
// Create video collection with validation
|
|
db.createCollection('videos', {
|
|
validator: {
|
|
$jsonSchema: {
|
|
bsonType: 'object',
|
|
required: ['title', 'fileUrl', 'thumbnailUrl', 'transcriptText'],
|
|
properties: {
|
|
title: {
|
|
bsonType: 'string',
|
|
minLength: 3,
|
|
description: 'Title is required and must be at least 3 characters'
|
|
},
|
|
description: {
|
|
bsonType: 'string',
|
|
description: 'Description of the video'
|
|
},
|
|
fileUrl: {
|
|
bsonType: 'string',
|
|
description: 'URL to the video file (required)'
|
|
},
|
|
thumbnailUrl: {
|
|
bsonType: 'string',
|
|
description: 'URL to the thumbnail image (required)'
|
|
},
|
|
duration: {
|
|
bsonType: 'number',
|
|
description: 'Duration of the video in seconds'
|
|
},
|
|
uploadDate: {
|
|
bsonType: 'date',
|
|
description: 'Date when the video was uploaded'
|
|
},
|
|
category: {
|
|
bsonType: 'string',
|
|
description: 'Category of the video'
|
|
},
|
|
subtitleUrl: {
|
|
bsonType: 'string',
|
|
description: 'URL to the WebVTT subtitle file'
|
|
},
|
|
transcriptText: {
|
|
bsonType: 'string',
|
|
description: 'Full text transcript of the video (required)'
|
|
},
|
|
transcriptFormat: {
|
|
enum: ['plain', 'html', 'json'],
|
|
description: 'Format of the transcript (plain, html, or json)'
|
|
},
|
|
isPublished: {
|
|
bsonType: 'bool',
|
|
description: 'Whether the video is published or not'
|
|
},
|
|
relatedPageId: {
|
|
bsonType: 'objectId',
|
|
description: 'Reference to a related page'
|
|
},
|
|
tags: {
|
|
bsonType: 'array',
|
|
items: { bsonType: 'string' },
|
|
description: 'Tags for categorizing the video'
|
|
}
|
|
}
|
|
}
|
|
},
|
|
validationLevel: 'strict'
|
|
});
|
|
|
|
// Create indexes for video collection
|
|
db.videos.createIndex({ 'title': 'text', 'transcriptText': 'text' });
|
|
db.videos.createIndex({ 'category': 1, 'uploadDate': -1 });
|
|
db.videos.createIndex({ 'tags': 1 });
|
|
|
|
// Create members collection with validation
|
|
db.createCollection('members', {
|
|
validator: {
|
|
$jsonSchema: {
|
|
bsonType: 'object',
|
|
required: ['firstName', 'lastName', 'email', 'membershipType', 'joinDate', 'status'],
|
|
properties: {
|
|
firstName: {
|
|
bsonType: 'string',
|
|
description: 'First name is required'
|
|
},
|
|
lastName: {
|
|
bsonType: 'string',
|
|
description: 'Last name is required'
|
|
},
|
|
email: {
|
|
bsonType: 'string',
|
|
pattern: '^.+@.+\\..+$',
|
|
description: 'Email must be a valid email address'
|
|
},
|
|
phone: {
|
|
bsonType: 'string',
|
|
description: 'Phone number'
|
|
},
|
|
address: {
|
|
bsonType: 'object',
|
|
properties: {
|
|
street: { bsonType: 'string' },
|
|
city: { bsonType: 'string' },
|
|
state: { bsonType: 'string' },
|
|
zip: { bsonType: 'string' }
|
|
}
|
|
},
|
|
membershipType: {
|
|
enum: ['regular', 'lifetime', 'honorary'],
|
|
description: 'Membership type must be one of: regular, lifetime, honorary'
|
|
},
|
|
joinDate: {
|
|
bsonType: 'date',
|
|
description: 'Date when the member joined'
|
|
},
|
|
expirationDate: {
|
|
bsonType: 'date',
|
|
description: 'Date when the membership expires'
|
|
},
|
|
status: {
|
|
enum: ['active', 'expired', 'pending'],
|
|
description: 'Status must be one of: active, expired, pending'
|
|
},
|
|
notificationPreference: {
|
|
enum: ['email', 'sms', 'both'],
|
|
description: 'Notification preference must be one of: email, sms, both'
|
|
},
|
|
lastRenewalDate: {
|
|
bsonType: 'date',
|
|
description: 'Date of the last membership renewal'
|
|
},
|
|
boardMember: {
|
|
bsonType: 'bool',
|
|
description: 'Whether the member is part of the board'
|
|
},
|
|
boardPosition: {
|
|
bsonType: 'string',
|
|
description: 'Position on the board, if applicable'
|
|
},
|
|
emergencyContact: {
|
|
bsonType: 'object',
|
|
properties: {
|
|
name: { bsonType: 'string' },
|
|
relationship: { bsonType: 'string' },
|
|
phone: { bsonType: 'string' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
validationLevel: 'strict'
|
|
});
|
|
|
|
// Create indexes for members collection
|
|
db.members.createIndex({ 'email': 1 }, { unique: true });
|
|
db.members.createIndex({ 'lastName': 1, 'firstName': 1 });
|
|
db.members.createIndex({ 'status': 1, 'expirationDate': 1 });
|
|
db.members.createIndex({ 'boardMember': 1 });
|
|
|
|
// Create admin user collection with validation
|
|
db.createCollection('adminUsers', {
|
|
validator: {
|
|
$jsonSchema: {
|
|
bsonType: 'object',
|
|
required: ['username', 'passwordHash', 'email'],
|
|
properties: {
|
|
username: {
|
|
bsonType: 'string',
|
|
minLength: 3,
|
|
maxLength: 50,
|
|
description: 'Username is required and must be between 3 and 50 characters'
|
|
},
|
|
passwordHash: {
|
|
bsonType: 'string',
|
|
description: 'Password hash is required'
|
|
},
|
|
email: {
|
|
bsonType: 'string',
|
|
pattern: '^.+@.+\\..+$',
|
|
description: 'Email must be a valid email address'
|
|
},
|
|
lastLogin: {
|
|
bsonType: 'date',
|
|
description: 'Date of the last login'
|
|
},
|
|
resetToken: {
|
|
bsonType: 'string',
|
|
description: 'Token for password reset'
|
|
},
|
|
resetTokenExpiry: {
|
|
bsonType: 'date',
|
|
description: 'Expiry date for password reset token'
|
|
}
|
|
}
|
|
}
|
|
},
|
|
validationLevel: 'strict'
|
|
});
|
|
|
|
// Create indexes for admin users collection
|
|
db.adminUsers.createIndex({ 'username': 1 }, { unique: true });
|
|
db.adminUsers.createIndex({ 'email': 1 }, { unique: true });
|
|
db.adminUsers.createIndex({ 'resetToken': 1 }, { sparse: true });
|
|
|
|
console.log('MongoDB initialization completed');
|