Track your travels across the globe with a beautiful calendar interface.
Live Demo: https://countrycalendar.app
Country Calendar is a full-stack web application that enables users to visually track countries visited on specific dates. Using an interactive calendar interface, users can log their travel history, view statistics, and export reports.
- Visual Calendar - Track which countries you visited on each day with an intuitive calendar UI
- 249 Countries - Full country database with unique colors for easy identification
- Travel Reports - Generate statistics and insights from your travel history
- Data Export - Export travel data to CSV or XLSX formats
- OAuth Authentication - Sign in with Google, Facebook, or Apple
- Mobile-First Design - Responsive interface optimized for all devices
- Admin Panel - User management and support ticket handling
- Email Notifications - Transactional emails for verification and password reset
- Runtime: Node.js 20 LTS
- Framework: Express.js 4.x
- Language: TypeScript
- Database: PostgreSQL 15+
- ORM: Prisma 5.x
- Caching: Redis 7.x
- Authentication: Passport.js + JWT
- Validation: Zod
- Email: Nodemailer + Mailgun
- Error Tracking: Sentry
- Framework: React 18.x
- Build Tool: Vite 5.x
- Styling: Tailwind CSS 3.x
- Component Library: shadcn/ui (Radix UI)
- State Management: Zustand 4.x
- Data Fetching: TanStack Query 5.x
- Routing: React Router 6.x
- Forms: React Hook Form + Zod
- Containerization: Docker + Docker Compose
- Production Hosting: Heroku
- CI/CD: GitHub Actions
- Node.js 20+
- Docker and Docker Compose
- npm 10+
-
Clone the repository:
git clone https://github.com/yourusername/ccalendar.git cd ccalendar -
Copy the environment file:
cp .env.example .env.development
-
Start all services:
docker compose up
-
In a separate terminal, run database migrations:
docker compose exec api npx prisma migrate dev -
Access the application:
- Web App: http://localhost:3000
- API: http://localhost:3001/api/v1
- Health Check: http://localhost:3001/api/v1/health
-
Install dependencies:
npm install
-
Start database services via Docker:
docker compose up db redis
-
Run database migrations:
npm run db:migrate
-
Start the development servers:
npm run dev
Or start them separately:
npm run dev:api # API on port 3001 npm run dev:web # Web on port 3000 (in another terminal)
| Service | Port |
|---|---|
| Web | 3000 |
| API | 3001 |
| PostgreSQL | 5434 |
| Redis | 6379 |
ccalendar/
├── .github/
│ └── workflows/ # GitHub Actions CI/CD
│ ├── ci.yml # Lint, test on PRs
│ ├── deploy.yml # Deploy to Heroku
│ └── e2e.yml # E2E tests
├── docker/
│ ├── Dockerfile.api # Backend container
│ └── Dockerfile.web # Frontend container
├── packages/
│ ├── api/ # Express.js backend
│ │ ├── prisma/ # Database schema & migrations
│ │ ├── data/ # Static data (countries.json)
│ │ ├── tests/ # Unit & integration tests
│ │ └── src/
│ │ ├── config/ # Configuration management
│ │ ├── controllers/ # Request handlers
│ │ ├── middleware/ # Express middleware
│ │ ├── routes/ # API route definitions
│ │ ├── services/ # Business logic
│ │ ├── validators/ # Input validation (Zod)
│ │ ├── utils/ # Utilities (logger, prisma, redis)
│ │ └── jobs/ # Background jobs
│ ├── web/ # React frontend
│ │ ├── public/ # Static assets
│ │ └── src/
│ │ ├── components/ # Reusable React components
│ │ ├── pages/ # Page components
│ │ ├── hooks/ # Custom React hooks
│ │ ├── stores/ # Zustand state stores
│ │ ├── services/ # API client services
│ │ ├── lib/ # Utility libraries
│ │ ├── styles/ # CSS/Tailwind styling
│ │ └── types/ # TypeScript definitions
│ └── e2e/ # Playwright E2E tests
│ ├── tests/ # Test files
│ ├── page-objects/ # Page Object Models
│ └── fixtures/ # Test fixtures
├── openspec/ # Specification & feature tracking
├── docker-compose.yml # Development environment
├── Procfile # Heroku configuration
├── app.json # Heroku app manifest
└── package.json # Root workspace config
| Command | Description |
|---|---|
npm run dev |
Start all services in development mode |
npm run dev:api |
Start API only |
npm run dev:web |
Start Web only |
npm run build |
Build all packages |
npm run build:production |
Build for production with asset copying |
npm run lint |
Lint all packages |
npm run lint:fix |
Fix linting issues |
npm run test |
Run all unit/integration tests |
npm run test:e2e |
Run Playwright E2E tests |
npm run test:e2e:ui |
Run E2E tests in UI mode |
npm run test:e2e:headed |
Run E2E tests with visible browser |
npm run db:migrate |
Run database migrations |
npm run db:migrate:deploy |
Deploy migrations (production) |
npm run db:generate |
Generate Prisma client |
npm run db:studio |
Open Prisma Studio |
npm run db:seed |
Seed database with initial data |
GET /api/v1/health- Health check with service status
GET /api/v1/countries- Get all countriesGET /api/v1/countries/:code- Get country by ISO code
POST /api/v1/auth/register- Register new userPOST /api/v1/auth/login- Login with email/passwordPOST /api/v1/auth/logout- LogoutPOST /api/v1/auth/refresh- Refresh access tokenPOST /api/v1/auth/forgot-password- Request password resetPOST /api/v1/auth/reset-password- Reset passwordGET /api/v1/auth/google- Google OAuthGET /api/v1/auth/facebook- Facebook OAuthGET /api/v1/auth/apple- Apple Sign In
GET /api/v1/travel-records- Get user's travel recordsPOST /api/v1/travel-records- Create travel recordPUT /api/v1/travel-records/:id- Update travel recordDELETE /api/v1/travel-records/:id- Delete travel record
GET /api/v1/reports/summary- Get travel summaryGET /api/v1/reports/by-year- Get records by yearGET /api/v1/reports/by-country- Get records by country
GET /api/v1/users/me- Get current user profilePUT /api/v1/users/me- Update profileDELETE /api/v1/users/me- Delete account
Copy .env.example to .env.development and configure:
NODE_ENV=development
PORT=3001
DATABASE_URL=postgresql://postgres:postgres@localhost:5434/ccalendar?schema=public
REDIS_URL=redis://localhost:6379
JWT_SECRET=your-secret-key-at-least-32-characters-long
JWT_ACCESS_EXPIRY=15m
JWT_REFRESH_EXPIRY=7d
FRONTEND_URL=http://localhost:3000
VITE_API_URL=http://localhost:3001/api/v1# Email (Mailgun)
MAILGUN_API_KEY=your-mailgun-api-key
MAILGUN_DOMAIN=your-mailgun-domain
FROM_EMAIL=noreply@countrycalendar.app
REPLY_TO_EMAIL=support@countrycalendar.app
# OAuth Providers
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
FACEBOOK_APP_ID=your-facebook-app-id
FACEBOOK_APP_SECRET=your-facebook-app-secret
APPLE_CLIENT_ID=your-apple-client-id
APPLE_TEAM_ID=your-apple-team-id
APPLE_KEY_ID=your-apple-key-id
APPLE_PRIVATE_KEY=your-apple-private-key
# Error Tracking
SENTRY_DSN=your-sentry-dsn
# reCAPTCHA
RECAPTCHA_PUBLIC_KEY=your-recaptcha-public-key
RECAPTCHA_PRIVATE_KEY=your-recaptcha-private-keyThe application is configured for deployment on Heroku with automatic CI/CD via GitHub Actions.
- Heroku account with billing enabled
- GitHub repository with Actions enabled
- Domain configured (optional)
-
Create Heroku app:
heroku create your-app-name
-
Add required add-ons:
heroku addons:create heroku-postgresql:essential-0 heroku addons:create heroku-redis:mini
-
Configure required environment variables:
heroku config:set NODE_ENV=production heroku config:set JWT_SECRET=$(openssl rand -base64 32) heroku config:set FRONTEND_URL=https://your-app-name.herokuapp.com -
Configure optional variables for full functionality:
# Email heroku config:set MAILGUN_API_KEY=your-key heroku config:set MAILGUN_DOMAIN=your-domain # OAuth heroku config:set GOOGLE_CLIENT_ID=your-id heroku config:set GOOGLE_CLIENT_SECRET=your-secret # Error tracking heroku config:set SENTRY_DSN=your-dsn
Add these secrets in your repository (Settings > Secrets and variables > Actions):
| Secret | Description |
|---|---|
HEROKU_API_KEY |
Heroku API key (from Account Settings) |
HEROKU_EMAIL |
Heroku account email |
HEROKU_APP_NAME |
Heroku app name |
On every push to main:
- CI tests run (lint, type check, unit tests)
- If tests pass, automatic deployment to Heroku
- Database migrations run via Procfile
releasephase - Health check verification
# Build production version
npm run build:production
# Deploy to Heroku
git push heroku main
# Run migrations manually if needed
heroku run npm run db:migrate:deploy --workspace=@ccalendar/api# View real-time logs
heroku logs --tail
# Check app status
heroku ps
# Verify database
heroku pg:info
# Check Redis
heroku redis:info
# Create backup before migrations
heroku pg:backups:capture# List recent releases
heroku releases
# Rollback to a specific release
heroku rollback v123# Run all tests
npm run test
# Run API tests only
npm run test -w packages/api
# Run Web tests only
npm run test -w packages/web# Run E2E tests (headless)
npm run test:e2e
# Run E2E tests with UI
npm run test:e2e:ui
# Run E2E tests with visible browser
npm run test:e2e:headedThe /api/v1/health endpoint returns:
{
"status": "ok",
"timestamp": "2024-01-21T12:00:00.000Z",
"version": "1.0.0",
"environment": "production",
"uptime": 3600,
"services": {
"database": "connected",
"redis": "connected"
}
}- JWT_SECRET is at least 32 characters
- All OAuth credentials are from production apps
- Sentry DSN is configured for error tracking
- HTTPS is enforced (automatic on Heroku)
- Custom domain has SSL certificate (if applicable)
- Rate limiting is enabled
- Input validation is in place
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Commit changes:
git commit -m "Add my feature" - Push to branch:
git push origin feature/my-feature - Open a Pull Request
MIT