A web-based todo application that uses Google Calendar as its backend storage. Tasks are automatically scheduled to free time slots on your calendar, making it easy to manage what you need to do and when you'll do it.
- Google OAuth Authentication - Sign in with your Google account
- Automatic Scheduling - Tasks are scheduled to the first available free slot within your work hours
- Drag-and-Drop Reordering - Prioritize tasks by dragging them; the app swaps their calendar time slots
- Urgent Tasks - Mark tasks as urgent to push them to the front of the schedule
- Task Completion + Redo - Mark tasks complete and undo when needed
- Calendar Sync + Reschedule All - Pull latest event times and reschedule all incomplete tasks
- Calendar Action Links - Event descriptions include "Mark Complete" and "Reschedule" links
- Settings - Configure your preferred calendar, work hours, timezone, default task duration, and event color
- Theme Toggle - Switch between light and dark modes
- Midnight Rescheduling - Nightly cron job reschedules incomplete tasks (server time)
- Action Token Cleanup - Daily cron job removes expired and old-used action tokens
├── client/ # React frontend
│ ├── src/
│ │ ├── components/ # Reusable UI components (shadcn/ui)
│ │ ├── hooks/ # Custom React hooks
│ │ ├── lib/ # Utilities (API client, query client)
│ │ ├── pages/ # Page components (auth, main, settings, calendar view, not found)
│ │ └── App.tsx # Root component with routing
│ └── index.html
│
├── server/ # Express backend
│ ├── auth.ts # Passport.js Google OAuth setup
│ ├── calendar.ts # Google Calendar API integration
│ ├── config.ts # Environment secret validation
│ ├── cron.ts # Midnight rescheduling cron job (runs at 00:00)
│ ├── crypto.ts # Token encryption utilities
│ ├── csrf.ts # CSRF token middleware
│ ├── db.ts # Database connection
│ ├── index.ts # Server entry point
│ ├── routes.ts # API route handlers
│ ├── static.ts # Static file serving (production)
│ ├── storage.ts # Database operations (Drizzle ORM)
│ ├── tokens.ts # Action tokens for calendar event links
│ └── vite.ts # Vite dev server integration
│
├── shared/ # Shared between frontend and backend
│ ├── schema.ts # Drizzle database schema and types
│ └── types.ts # Shared API types
│
├── drizzle.config.ts # Drizzle ORM configuration
├── vite.config.ts # Vite bundler configuration
└── package.json # Dependencies and scripts
- Frontend: React 19, TypeScript, Vite, TanStack Query, Wouter (routing), shadcn/ui, Tailwind CSS
- Backend: Node.js, Express, TypeScript, Passport.js
- Database: PostgreSQL with Drizzle ORM
- APIs: Google Calendar API, Google OAuth 2.0
- Frontend: Wouter for routing, TanStack React Query for server state, shadcn/ui (Radix UI), Tailwind CSS with theme variables, @hello-pangea/dnd for reordering, React Hook Form + Zod for validation.
- Backend: TypeScript (ESM), tsx in development, esbuild for production bundling, express-session with PostgreSQL store (connect-pg-simple), node-cron for midnight rescheduling and action token cleanup.
- Data: Drizzle ORM with schema in
shared/schema.ts; shared types inshared/are used by both frontend and backend; tasks live in Google Calendar and are mapped toCalendarTaskon read; tables includeusers,user_settings,user_sessions. - Security: OAuth tokens are encrypted at rest; action tokens expire after 7 days.
- Event handling: App-created calendar events are marked with
[Todo]to avoid touching unrelated events. - Google access: OAuth scopes include
profile,email,calendar, andcalendar.events.
- Auth:
/api/auth/google,/api/auth/google/callback,/api/auth/logout,/api/auth/user - Tasks:
/api/tasks(list + create),/api/tasks/:id(complete/redo),/api/tasks/reorder,/api/tasks/reschedule-all,/api/tasks/reload,/api/tasks/:id/complete,/api/tasks/:id/reschedule,/api/tasks/bulk-complete - Settings:
/api/settings,/api/calendars - Action links:
/api/action/:token(complete/reschedule) - CSRF: send
X-CSRF-Tokenfrom/api/auth/useron non-GET requests
-
Node.js (v20 or later)
# Using Homebrew brew install node # Or using nvm nvm install 20 nvm use 20
-
PostgreSQL
brew install postgresql@15 brew services start postgresql@15 # Create a database createdb todo -
Google Cloud Project
- Go to Google Cloud Console
- Create a new project
- Enable the Google Calendar API
- Go to APIs & Services > Credentials
- Create an OAuth 2.0 Client ID (Web application type)
- Add authorized redirect URI:
http://localhost:5000/api/auth/google/callback - Note your Client ID and Client Secret
Create a .env file in the project root:
DATABASE_URL=postgresql://localhost:5432/todo
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-client-secret
SESSION_SECRET=32+_char_random_session_secret
ACTION_TOKEN_SECRET=32+_char_random_action_secret
TOKEN_ENCRYPTION_KEY=32+_char_random_key_for_token_encryptionNotes:
SESSION_SECRETandACTION_TOKEN_SECRETmust be different values and at least 32 characters long.
# Install dependencies
npm install
# Run database migrations
npm run db:migrate
# Start development server
npm run devThe app will be available at http://localhost:5000.
npm run dev- Start development server (frontend + backend)npm run lint- Run strict ESLint quality gates (includes complexity thresholds)npm run quality:deadcode- Detect unused files/exports/dependencies with Knipnpm run check- Run type and code-style checksnpm run test- Run the test suite oncenpm run test:integration- Run integration tests onlynpm run test:watch- Run tests in watch modenpm run test:coverage- Run tests with coverage reportnpm run verify- Run full quality gate (lint,quality:deadcode,test:coverage,check,build)npm run db:generate- Create a new migration from schema changesnpm run db:migrate- Run pending migrations manuallynpm run build- Build for productionnpm run start- Run production build (also runs pending migrations and starts cron jobs in-process)
- Node.js Runtime - Node.js 20+ environment
- PostgreSQL Database - Managed instance or self-hosted
- HTTPS - Required for OAuth security and session cookies
- Persistent Process Manager - configured for automatic restarts
DATABASE_URL=postgresql://user:password@host:5432/dbname?sslmode=require
GOOGLE_CLIENT_ID=your-production-client-id
GOOGLE_CLIENT_SECRET=your-production-client-secret
SESSION_SECRET=strong-random-secret-min-32-chars
ACTION_TOKEN_SECRET=another-strong-random-secret-min-32-chars
TOKEN_ENCRYPTION_KEY=strong-32-char-key-for-encryption
NODE_ENV=production
PRODUCTION_APP_URL=https://your-domain.com- In Google Cloud Console, add your production domain to authorized redirect URIs:
https://your-domain.com/api/auth/google/callback
- If your app is public, you may need to complete Google's OAuth verification process
# Install dependencies (includes dev dependencies needed for build)
npm ci
# Build the application
npm run build
# Start the server
npm start- PostgreSQL database provisioned and accessible
- Environment variables configured
- Google OAuth redirect URI updated for production domain
- HTTPS configured (required for secure cookies)
- Process manager configured for automatic restarts
- Web process startup runs DB migrations successfully
- In-process cron jobs run for midnight rescheduling and daily action token cleanup (set
TZif needed)
See CONTRIBUTING.md for development workflow and design guidelines.
Privacy Policy and Terms of Service are available on the deployed site at /privacy and /tos.
MIT