A complete, production-ready web application for Ham Radio Net Control Operators built for the York County Amateur Radio Society.
- ✅ Modern React frontend with responsive design
- ✅ RESTful Node.js/Express backend API
- ✅ MongoDB database for data persistence
- ✅ JWT-based authentication system
- ✅ Docker containerization for easy deployment
- Admin "Check for Updates" feature with GitHub integration
- Real-time progress via SSE during updates
- Version info panel on Admin page
- Backend endpoints:
/api/updates/check,/api/updates/perform,/api/updates/version
- Start/stop net operations
- Real-time check-in tracking
- Automatic callsign lookup via QRZ.com API
- Add/remove check-ins with callsign, name, and notes
- Support for multiple operators
- Interactive calendar view
- Visual indicators for days with operations
- View all operations for any selected date
- Support for multiple nets per day
- One-click PDF export for any operation
- Dark/Light theme toggle with persistence
- Custom logo upload for branding
- QRZ.com API key configuration
- User preferences management
- Secure user registration
- Login/logout functionality
- JWT token-based authentication
- Protected routes
- Password hashing with bcrypt
✅ Express.js server
✅ MongoDB with Mongoose ODM
✅ JWT authentication middleware
✅ QRZ.com API integration
✅ PDF generation with PDFKit
✅ File upload handling (logos)
✅ RESTful API design
✅ Error handling
✅ React 18 with Vite
✅ React Router for navigation
✅ Context API for state management
✅ Axios for HTTP requests
✅ React Calendar component
✅ Toast notifications
✅ Responsive CSS design
✅ Dark/Light theme support
✅ Docker Compose orchestration
✅ Separate containers for frontend, backend, and database
✅ Nginx web server for frontend
✅ Volume persistence for database
✅ Network isolation
✅ Environment variable configuration
NetControlApp/
├── backend/
│ ├── src/
│ │ ├── config/
│ │ │ └── database.js
│ │ ├── controllers/
│ │ │ ├── authController.js
│ │ │ ├── netOperationController.js
│ │ │ └── settingsController.js
│ │ ├── middleware/
│ │ │ ├── auth.js
│ │ │ └── upload.js
│ │ ├── models/
│ │ │ ├── User.js
│ │ │ ├── NetOperation.js
│ │ │ └── Settings.js
│ │ ├── routes/
│ │ │ ├── authRoutes.js
│ │ │ ├── netOperationRoutes.js
│ │ │ └── settingsRoutes.js
│ │ ├── services/
│ │ │ ├── qrzService.js
│ │ │ └── pdfService.js
│ │ └── server.js
│ ├── Dockerfile
│ └── package.json
├── frontend/
│ ├── src/
│ │ ├── components/
│ │ │ ├── CheckInForm.jsx
│ │ │ ├── Navbar.jsx
│ │ │ └── PrivateRoute.jsx
│ │ ├── contexts/
│ │ │ ├── AuthContext.jsx
│ │ │ └── ThemeContext.jsx
│ │ ├── pages/
│ │ │ ├── Dashboard.jsx
│ │ │ ├── Login.jsx
│ │ │ ├── Register.jsx
│ │ │ ├── NetControl.jsx
│ │ │ ├── Schedule.jsx
│ │ │ └── Settings.jsx
│ │ ├── services/
│ │ │ └── api.js
│ │ ├── styles/
│ │ │ └── index.css
│ │ ├── App.jsx
│ │ └── main.jsx
│ ├── Dockerfile
│ ├── nginx.conf
│ ├── vite.config.js
│ └── package.json
├── docker-compose.yml
├── .dockerignore
├── .gitignore
├── README.md
├── SETUP_INSTRUCTIONS.md
├── CONTRIBUTING.md
├── LICENSE
├── start.sh (Linux/Mac)
└── start.bat (Windows)
macOS/Linux:
./start.shWindows:
start.bat
docker-compose up -dThen visit: http://localhost
- username (unique)
- callsign (unique, uppercase)
- email (unique)
- password (hashed)
- role (operator/admin)
- operatorId (reference to User)
- operatorCallsign
- startTime / endTime
- checkIns (array)
- callsign
- name
- notes
- timestamp
- netName
- frequency
- notes
- status (active/completed)
- userId (reference to User)
- theme (light/dark)
- qrzApiKey
- logo (file path)
Authentication:
- POST
/api/auth/register - POST
/api/auth/login - GET
/api/auth/me
Net Operations:
- POST
/api/net-operations - GET
/api/net-operations - GET
/api/net-operations/:id - PUT
/api/net-operations/:id - PUT
/api/net-operations/:id/complete - POST
/api/net-operations/:id/checkins - DELETE
/api/net-operations/:id/checkins/:checkinId - GET
/api/net-operations/lookup/:callsign - GET
/api/net-operations/:id/pdf
Settings:
- GET
/api/settings - PUT
/api/settings - POST
/api/settings/logo - DELETE
/api/settings/logo
- express: Web framework
- mongoose: MongoDB ODM
- bcryptjs: Password hashing
- jsonwebtoken: JWT authentication
- cors: CORS middleware
- dotenv: Environment variables
- multer: File upload handling
- axios: HTTP client for QRZ API
- pdfkit: PDF generation
- xml2js: XML parsing for QRZ responses
- react: UI framework
- react-router-dom: Routing
- axios: HTTP client
- react-calendar: Calendar component
- react-toastify: Notifications
- date-fns: Date utilities
✅ Password hashing with bcrypt ✅ JWT token authentication ✅ Protected API routes ✅ CORS configuration ✅ Input validation ✅ File type validation for uploads ✅ File size limits ✅ Secure headers
✅ Production-optimized builds ✅ Environment variable configuration ✅ Docker containerization ✅ Nginx reverse proxy ✅ Volume persistence ✅ Automated startup scripts ✅ Comprehensive documentation
- Email notifications
- Advanced reporting and analytics
- Multi-club support
- Mobile app version
- Real-time collaboration features
- Integration with logging software
- Automated net scheduling
- SMS notifications
- Weather alerts integration
- Repeater information database
README.md- Complete documentationSETUP_INSTRUCTIONS.md- Quick setup guideCONTRIBUTING.md- Contribution guidelinesLICENSE- MIT License
Before deploying to production:
- Test user registration and login
- Test net operation creation
- Test check-in functionality
- Test QRZ lookup with valid API key
- Test PDF export
- Test theme switching
- Test logo upload
- Test calendar navigation
- Test on mobile devices
- Verify data persistence after restart
- Change JWT_SECRET to a strong, unique value
- Set up SSL/TLS certificates (HTTPS)
- Configure firewall rules
- Set up automated backups for MongoDB
- Configure monitoring and logging
- Update QRZ API key in settings
- Test all features in production environment
- Create admin user account
- Document operator procedures
- Set up regular database backups
Built with ❤️ for the York County Amateur Radio Society by K4HEF.
73! 📻