A minimalist, modern, and highly polished Todo List application built with Next.js
🌐 Live Demo (demo data is ephemeral — not persisted) · Preview
-
🎨 Modern UI/UX Design
- Beautiful animated logo with smooth transitions
- Glassmorphism cards with backdrop blur effects
- Smooth animations powered by Framer Motion
- Color-coded task statistics (Blue, Orange, Green)
-
🌓 Theme Support
- Light mode with vibrant gradients
- Dark mode with professional aesthetics
- System preference detection
- Seamless theme switching
-
📱 Responsive & PWA
- Desktop top tabs + mobile bottom navigation, touch-optimized
- iPhone safe-area aware (Dynamic Island / home indicator),
100dvhlayouts - Installable PWA with a per-user dynamic manifest icon
- Native iOS/Android packaging via Capacitor (separate build)
-
⚙️ Customization
- Multi-language (English & Chinese), customizable logo text
- Per-user custom browser tab / PWA icon — upload by drag, paste, or file picker
- Timezone selection, optional priority & groups, API-docs toggle, theme preferences
-
📊 Task Management
- Create, complete, edit, and delete tasks; groups, priorities (P0/P1/P2), and due dates
- Quick actions: ⌘/Ctrl+Enter to add, double-click to edit, light toast on completion
- Creation/completion timestamps; soft deletion (logical delete, data preserved)
- Filter by status (All, Active, Completed) and by time (Today / Overdue / Upcoming)
-
🤖 AI Assistant (optional)
- Conversational task management — add / complete / update / delete via natural language
- Voice input support
- Works with any OpenAI-compatible gateway; model list fetched dynamically from
/v1/models
-
📊 Interactive Analytics Dashboard
- Daily Activity trend charts (Creation vs Completion)
- Task completion timeline (Gantt-style visual)
- Real-time KPI statistics (Total, Completed, Success Rate)
- Flexible date ranges (7 Days, 30 Days, All Time)
-
🔐 Multi-user & Security
- Registration / login with HttpOnly cookie sessions; change password (revokes other sessions)
- Each user has fully isolated todos, groups, and AI chat history
- scrypt-hashed passwords, same-origin (CSRF) checks on writes, in-memory rate limiting, controlled / invite-code registration
-
💾 Data Persistence & Maintenance
- SQLite database (better-sqlite3, WAL mode), per-user isolation
- Automated maintenance sidecar — expired-session cleanup, soft-delete GC, periodic SQLite backups
- Data survives restarts; fully traceable task history
- Node.js 18+ (for local development)
- Docker (optional, for containerized deployment)
-
Clone the repository
git clone https://github.com/uniStark/todos.git cd todos -
Install dependencies
npm install # or pnpm install -
Start development server
npm run dev # or pnpm dev -
Open your browser
http://localhost:3000
-
Start with Docker Compose
docker compose up -d --build
Docker Compose builds the app, starts the container, and stores data in a persistent Docker volume.
-
Quick update (rebuild and restart)
./docker-update.sh
This script will:
- Stop the current container
- Pull latest code (if Git repository)
- Rebuild the Docker image (no cache)
- Start the new container
- Display logs automatically
-
Create the first account / control registration
# By default the first account can register when the DB is empty; public # registration is then closed. To open public registration: echo "ALLOW_REGISTRATION=true" > .env # Or switch to invite-code registration (open, but a correct code is required): echo "INVITE_CODE=your_invite_code" >> .env
On first visit you land on the login page — just register the first account; or bootstrap it from legacy JSON data with the migration script (see "Data migration" below).
Auth uses HttpOnly cookie sessions, passwords are scrypt-hashed, and write endpoints enforce same-origin (CSRF) checks. Each user's data is isolated.
-
Configure AI features (optional)
# Leave unset to disable AI-backed features. # Works with any OpenAI-compatible gateway (OpenAI / self-hosted proxy / 3rd-party); # the model list is fetched dynamically from /v1/models. echo "AI_API_KEY=your_api_key" >> .env echo "AI_BASE_URL=https://api.openai.com/v1" >> .env # any OpenAI-compatible endpoint echo "AI_DEFAULT_MODEL=gpt-4o-mini" >> .env # default model
AI_API_KEYis optional for base Todo usage. If it is empty, AI-backed features may be unavailable, but Docker Compose configuration still works. -
(Optional) Migrate from legacy JSON data
# Import old todos.json/groups.json/chat-history.json into SQLite and create the first account. # Local: point DATA_DIR at the data dir; Docker: run inside the container with DATA_DIR=/app/data DATA_DIR=./data node scripts/migrate-json-to-sqlite.mjs <username> <password>
-
Access the application
http://localhost:3002 -
Management commands
# Stop containers docker compose down # Restart containers docker compose restart # View logs docker compose logs -f # Quick update (rebuild everything) ./docker-update.sh
-
Data Persistence & Safe Updates
- Data is stored in a Docker volume named
todos-data, which holds the SQLite database (todos.db+ WAL files) and periodic backups underbackups/. - Important: Always use
./docker-update.shfor updates. Avoid runningdocker compose down -vmanually, as the-vflag will permanently delete your data volumes. - To backup data:
docker run --rm -v todos_web_todos-data:/data -v $(pwd):/backup alpine tar czf /backup/todos-backup.tar.gz -C /data .
- To restore data:
docker run --rm -v todos_web_todos-data:/data -v $(pwd):/backup alpine tar xzf /backup/todos-backup.tar.gz -C /data
- Data is stored in a Docker volume named
-
Clean up (removes data)
docker compose down -v
Todos/
├── src/
│ ├── app/ # Next.js App Router
│ │ ├── api/ # API routes
│ │ │ ├── todos/ groups/ stats/ # CRUD + visit stats
│ │ │ ├── auth/ # register / login / logout / me / change-password / icon
│ │ │ ├── ai/ # AI chat & actions
│ │ │ └── health/ # health check
│ │ ├── analytics/ # Insights & charts page
│ │ ├── settings/ # Settings page
│ │ ├── api-docs/ # Interactive API docs
│ │ ├── manifest.webmanifest/ # Per-user dynamic PWA manifest
│ │ └── page.tsx · layout.tsx · globals.css
│ ├── components/ # TodoItem, AnalyticsDashboard, AIChat, VoiceButton, AuthModal, StarkLogo, ...
│ ├── contexts/ # Auth / Settings / Toast contexts
│ └── lib/
│ ├── db/ # SQLite layer: index.ts + userRepo / todosRepo / groupsRepo / chatRepo / sessionRepo / statsRepo
│ ├── auth/ # session & password (scrypt)
│ └── translations.ts · timezones.ts · validation.ts · rateLimit.ts · chatStorage.ts
├── public/ # Static assets
├── scripts/ # maintenance.mjs (sidecar), migrate-json-to-sqlite.mjs, restore-sqlite.mjs, ...
├── docker-compose.yml # app + maintenance sidecar
├── Dockerfile # Multi-stage build (.next/cache mount)
├── docker-update.sh # Rebuild/update script
└── package.json
- Framework: Next.js 15 (App Router) + React 19
- Language: TypeScript 5
- Styling: Tailwind CSS 3.4
- Database: SQLite (better-sqlite3, WAL)
- Animation: Framer Motion · Charts: Recharts · Icons: Lucide React
- Mobile: Capacitor (iOS/Android packaging)
- Containerization: Docker & Docker Compose
Data is stored in SQLite (todos.db) with per-user isolation. Each todo row looks like:
{
"id": "uuid",
"userId": "owner-uuid",
"text": "Task description",
"completed": false,
"createdAt": 1705392000000,
"completedAt": null,
"deleted": false,
"deletedAt": null,
"groupId": "default",
"priority": "P2",
"dueDate": "2026-01-31"
}Legacy JSON files (
todos.json/groups.json/chat-history.json) are only used as a one-time import source — see the migration script in Quick Start.
The application provides a RESTful API for programmatic access. View the interactive API documentation at /api-docs in your browser.
All todos / groups / AI endpoints require login. Call the login endpoint to obtain a session cookie, then send that cookie on subsequent requests:
# Log in and save the session cookie to a cookie jar
curl -X POST https://your-domain/api/auth/login \
-H "Content-Type: application/json" \
-c cookies.txt \
-d '{"username": "alice", "password": "your_password"}'
# Subsequent requests carry the cookie
curl https://your-domain/api/todos -b cookies.txtSessions are HttpOnly cookies (
todo_session); write endpoints require same-origin (CSRF check). Each user can only access their own data.
| Method | Auth | Description |
|---|---|---|
| GET | ✅ | Get the current user's active todos |
| POST | ✅ | Create a new todo |
| PUT | ✅ | Update an existing todo |
| DELETE | ✅ | Soft delete a todo |
GET /api/todos
curl https://your-domain/api/todos -b cookies.txtPOST /api/todos
curl -X POST https://your-domain/api/todos \
-H "Content-Type: application/json" \
-b cookies.txt \
-d '{"text": "New task", "groupId": "default", "priority": "P1"}'PUT /api/todos
curl -X PUT https://your-domain/api/todos \
-H "Content-Type: application/json" \
-b cookies.txt \
-d '{"id": "uuid", "completed": true, "text": "Updated text"}'DELETE /api/todos
curl -X DELETE "https://your-domain/api/todos?id=uuid" -b cookies.txt| Method | Auth | Description |
|---|---|---|
| GET | ✅ | Get the current user's groups |
| POST | ✅ | Create a new group |
| DELETE | ✅ | Delete a group |
GET /api/groups
curl https://your-domain/api/groups -b cookies.txtPOST /api/groups
curl -X POST https://your-domain/api/groups \
-H "Content-Type: application/json" \
-b cookies.txt \
-d '{"name": "Work"}'DELETE /api/groups
curl -X DELETE "https://your-domain/api/groups?id=uuid" -b cookies.txt| Method | Auth | Description |
|---|---|---|
| GET | ❌ | Get PV/UV statistics (public) |
| POST | same-origin | Record a visit (same-origin + rate-limited) |
GET /api/stats
curl https://your-domain/api/stats| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /api/auth/register |
❌ | Register (gated by ALLOW_REGISTRATION / INVITE_CODE, first user allowed); body may include inviteCode; sets session cookie |
| POST | /api/auth/login |
❌ | Log in; sets session cookie |
| POST | /api/auth/logout |
✅ | Destroy current session |
| GET | /api/auth/me |
— | Query current login state and whether registration is open |
| POST | /api/auth/change-password |
✅ | Change password (revokes other sessions) |
| GET / POST | /api/auth/icon |
✅ | Get / upload the current user's custom icon |
POST /api/auth/login
curl -X POST https://your-domain/api/auth/login \
-H "Content-Type: application/json" \
-c cookies.txt \
-d '{"username": "alice", "password": "your_password"}'Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
For more details, see CONTRIBUTING.md.
This project is licensed under the MIT License - see the LICENSE file for details.
- Powered by Next.js
- UI Design inspired by modern minimalism
- Built with ❤️ by Adrian Stark
Made with ❤️ by Adrian Stark | Powered by Next.js