A web application for Triangle Curling Club members to find and offer spares when unable to make their scheduled games.
- Authentication: Simple email/SMS-based login with 6-digit codes
- Spare Availability: Members can set their availability by league and position preferences
- Spare Requests: Request spares publicly (all available members) or privately (specific members)
- Real-time Notifications: Email and SMS notifications for spare requests and responses
- Admin Management: Manage members, leagues, and send welcome emails
- Responsive Design: Works seamlessly on desktop and mobile devices
- Node.js with Fastify
- SQLite database with better-sqlite3
- TypeScript
- Azure Communication Services (email)
- Twilio (SMS)
- JWT authentication
- React 18
- TypeScript
- TailwindCSS
- React Router
- Axios
- Vite (build tool with HMR)
- ESLint & Prettier
- Workspaces (monorepo structure)
- Node.js 20 or higher
- npm or yarn
- Clone the repository:
git clone <repository-url>
cd spares- Install dependencies:
npm install- Set up environment variables:
Copy backend/env.template to backend/.env and fill in your configuration:
cp backend/env.template backend/.envRequired environment variables:
JWT_SECRET: A long random string for JWT signingSERVER_ADMINS: Comma-separated list of server admin email addressesAZURE_COMMUNICATION_CONNECTION_STRING: Azure Communication Services connection stringAZURE_COMMUNICATION_SENDER_EMAIL: Verified sender email addressTWILIO_ACCOUNT_SID: Twilio account SIDTWILIO_AUTH_TOKEN: Twilio auth tokenTWILIO_PHONE_NUMBER: Twilio phone number for SMS
Run both frontend and backend in development mode:
npm run devOr run them separately:
# Frontend (http://localhost:5173)
npm run dev:frontend
# Backend (http://localhost:3001)
npm run dev:backendInitialize the database:
npm run db:init --workspace=backendnpm run buildThis builds both the frontend and backend.
spares/
├── backend/
│ ├── src/
│ │ ├── db/ # Database schema and migrations
│ │ ├── middleware/ # Auth middleware
│ │ ├── routes/ # API routes
│ │ ├── services/ # Email and SMS services
│ │ ├── utils/ # Utility functions
│ │ ├── config.ts # Configuration
│ │ ├── types.ts # TypeScript types
│ │ └── index.ts # Server entry point
│ ├── package.json
│ └── tsconfig.json
├── frontend/
│ ├── src/
│ │ ├── components/ # Reusable components
│ │ ├── contexts/ # React contexts
│ │ ├── pages/ # Page components
│ │ │ └── admin/ # Admin pages
│ │ ├── utils/ # Utility functions
│ │ ├── App.tsx # Main app component
│ │ ├── main.tsx # Entry point
│ │ └── index.css # Global styles
│ ├── package.json
│ ├── tsconfig.json
│ ├── vite.config.ts
│ └── tailwind.config.js
├── .github/
│ └── workflows/ # GitHub Actions for CI/CD
├── DEPLOYMENT.md # Deployment instructions
├── QUESTIONS.txt # Questions and clarifications
├── package.json # Root package.json (workspace)
└── README.md # This file
See DEPLOYMENT.md for detailed deployment instructions.
- Production: Push to
mainbranch → Deploys to spares.tccnc.club - Staging: Push to
previewbranch → Deploys to spares-preview.tccnc.club
- Add your email address to the
SERVER_ADMINSenvironment variable in.env - Restart the backend server
- Log in using your email address
Admins specified in the .env file cannot have their admin rights removed through the UI.
- Manage members (add, edit, delete, send welcome emails)
- Manage leagues (create, edit, delete leagues and draw times)
- View all spare requests
- Create private spare requests and select specific members
POST /api/auth/request-code- Request login codePOST /api/auth/verify-code- Verify login codePOST /api/auth/select-member- Select member (when multiple share contact)GET /api/auth/verify- Verify JWT token
GET /api/members/me- Get current member profilePATCH /api/members/me- Update current member profilePOST /api/members/me/complete-first-login- Complete first login flowPOST /api/members/me/unsubscribe- Unsubscribe from emailsGET /api/members- Get all members (admin only)POST /api/members- Create member (admin only)PATCH /api/members/:id- Update member (admin only)DELETE /api/members/:id- Delete member (admin only)POST /api/members/:id/send-welcome- Send welcome email (admin only)
GET /api/leagues- Get all leaguesPOST /api/leagues- Create league (admin only)PATCH /api/leagues/:id- Update league (admin only)DELETE /api/leagues/:id- Delete league (admin only)
GET /api/availability- Get current member's availabilityPOST /api/availability/league- Set availability for a leaguePOST /api/availability/can-skip- Set skip preference
GET /api/spares- Get all open spare requests (public + private invitations)GET /api/spares/my-requests- Get current member's spare requestsPOST /api/spares- Create spare requestPOST /api/spares/:id/respond- Respond to spare requestPOST /api/spares/:id/cancel- Cancel spare request
This project uses ESLint and Prettier for code formatting. Run:
npm run lint # Check for issues
npm run format # Auto-format codeThe application uses SQLite for simplicity and ease of deployment. The database file is created automatically on first run.
Location:
- Development:
backend/data/spares.sqlite - Production: Set via
DATABASE_PATHenvironment variable
During development, if Azure Communication Services or Twilio credentials are not configured, the application will log the messages to console instead of sending them.
For questions or issues, please refer to QUESTIONS.txt or contact the development team.
MIT License - See LICENSE file for details