A full-stack habits tracking application with Node.js/Express backend, PostgreSQL database, and interactive CLI client. Features JWT authentication, per-user data ownership, comprehensive input validation, rate limiting, and a complete integration test suite.
- User Authentication: JWT-based login/register with password hashing
- Per-User Data Ownership: Users only access their own habits, categories, reminders
- Habit Management: Create, read, update, delete habits with frequency tracking
- Categories & Organization: Group habits into categories with linking
- Daily Tracking: Log daily habit status (done/skipped/pending/missed) with streaks
- Reminders: Set time-based reminders for habits (HH:MM format)
- Admin Interface: Admin menu for system management (cleanup, user viewing)
- CLI Interface: Interactive menu-driven console client with persistent submenus
- Security: Input validation, rate limiting (100 req/15min per IP), request size limits (10KB)
- Testing: 35+ integration tests covering auth, CRUD, ownership, validation
- Role-Based Access: Admin and user roles with different menu options
- Navigate to the project:
cd dvop-zp-2025-2026-filo- Install dependencies:
npm install- Create
.envfile with required variables:
# PostgreSQL Connection
DB_HOST=localhost
DB_PORT=5432
DB_NAME=habits_db
DB_USER=postgres
DB_PASSWORD=your_password
# JWT Configuration
JWT_SECRET=your-secret-key-here
NODE_ENV=development
PORT=4444
Default values: If not set, JWT_SECRET defaults to "dev-secret" and PORT to 4444.
- Initialize database:
npx tsx create_table.jsCreates all tables: users, habits, categories, reminders, habitlog, habitdaystatus, habitcategory.
node server.js- Listens on port 4444 (configurable via
PORTenv var) - Serves API endpoints at
http://localhost:4444/
Use these settings in Railway:
- Railway will read
nixpacks.tomlfrom the repo root and use:- Install:
npm ci - Build:
npm run build - Start:
npm start
- Install:
Set these environment variables from your Railway PostgreSQL service:
DB_HOSTDB_PORTDB_NAMEDB_USERDB_PASSWORDJWT_SECRETNODE_ENV=production
The CLI client is local-only, so Railway should run the API server, not client.js.
Public API URL for remote users:
https://host-production-0dd6.up.railway.app
How a foreign user connects:
- Clone the repository locally.
- Create a local
.envfile with your own credentials andAPI_URL=https://host-production-0dd6.up.railway.app. - Start the CLI client with
npm run client. - Register or log in through the menu, and the client will send requests to the hosted API instead of
localhost.
In another terminal:
npm run client- Interactive menu-driven interface
- Requires login (register new account or use existing credentials)
- Different menus for admin vs regular users
- To point the client at Railway, add
API_URL=https://host-production-0dd6.up.railway.appto your local.envbefore starting the client.
.
├── server/
│ ├── server.js # Express app initialization
│ ├── constants.js # Shared validation rules and config
│ ├── app.js # Route mounting and middleware setup
│ ├── middleware/
│ │ └── requestLimits.js # Rate limiting and request size validation
│ ├── routes/
│ │ ├── users.js # POST /register, POST /login
│ │ ├── habits.js # CRUD for habits
│ │ ├── categories.js # CRUD for categories
│ │ ├── habitCategories.js # Link/unlink habits to categories
│ │ ├── reminders.js # CRUD for reminders
│ │ ├── habitLogs.js # Habit completion logs
│ │ └── habitDayStatus.js # Daily habit status tracking
│ ├── utils/
│ │ └── validators.js # Input validation functions
│ └── db.js # PostgreSQL connection pool
├── cli/
│ ├── client.js # CLI entry point
│ ├── menus.js # Menu system (admin, user, submenus)
│ └── actions.js # API interaction logic
├── tests/
│ ├── integration.test.js # 35+ integration tests
│ └── run-integration-tests.js # Test runner with server auto-start
├── create_table.js # Database schema initialization
├── .env # Configuration (git-ignored)
├── package.json # Dependencies and scripts
└── README.md
- Token Expiry: 7 days
- Format: Bearer token in
Authorizationheader - Example:
Authorization: Bearer eyJhbGciOiJIUzI1NiIs... - Storage: Stored in client's
.envfile or memory during session
- Password Requirements:
- Minimum: 1 character
- Maximum: 24 characters
- Password Hashing: bcryptjs with 10 salt rounds
- Email Format: RFC compliant (user@domain.com)
- Input Validation: All user inputs validated before processing
- Rate Limiting: 100 requests per 15-minute window per IP (returns 429)
- Request Size Limit: 10KB max body size (returns 413)
- SQL Injection Prevention: Parameterized queries via pg driver
- XSS Prevention: No unsafe HTML rendering
- Per-User Ownership: All endpoints verify userid matches requesting user
| Field | Min | Max | Format |
|---|---|---|---|
| - | - | user@domain.com | |
| Password | 1 | 24 | Any characters |
| Habit Name | 2 | 80 | Alphanumeric + spaces |
| Habit Frequency | 2 | 30 | Text (e.g., "daily", "3x week") |
| Category Name | 2 | 50 | Alphanumeric + spaces |
| Reminder Time | - | - | HH:MM (24-hour format) |
| Habit Day Status | - | - | "pending", "done", "skipped", "missed" |
- Launch CLI with
npx tsx client.js - Choose "Register" or "Login"
- Enter email and password
- Two menu options appear:
- Admin Menu: System management (if role = "admin")
- Basic User Menu: Habit tracking (if role = "user")
- View Users # List all users
- Manage Habits # CRUD for any habit (admin access)
- Manage Categories # CRUD for any category (admin access)
- Manage Reminders # CRUD for any reminder (admin access)
- Habit Logs # View all logs
- Today's Checklist # View all today's habits (with cleanup option)
- Logout
- My Habits
├─ Create Habit # Prompts: name → frequency → category (opt) → reminder (opt)
├─ View Habits # Display all user's habits
├─ Update Habit # Modify habit details
└─ Delete Habit
- Habit Categories
├─ Create Category # Enter category name
├─ View Categories # List all categories
├─ Update Category
├─ Delete Category
└─ Link/Unlink Habits
- Today Checklist
├─ View Today's Habits # Show status for today
└─ Log Habit Status # Mark habit as done/skipped/missed
- Logout
Note: All submenus persist after actions (you remain in the submenu until choosing "back").
npm run test:full- Auto-starts server on port 4444
- Runs 35+ integration tests
- Auto-kills server after tests complete
- Returns exit code indicating pass/fail
- ✅ Authentication (register, login, token validation)
- ✅ CRUD operations (habits, categories, reminders)
- ✅ Per-user ownership (users cannot access others' data)
- ✅ Input validation (email, password, habit name, frequency)
- ✅ Rate limiting (100 req/15min per IP)
- ✅ Request size limits (10KB max)
- ✅ Habit streaks and statistics
- ✅ Daily status tracking
- ✅ Habit-category relationships
- ✅ Error handling (404, 400, 401, 403, 429, 413)
tests/
├── integration.test.js # All 35 test cases
└── run-integration-tests.js # Test runner (server auto-start)
.envfile (highest priority)- Environment variables
- Defaults (JWT_SECRET = "dev-secret", PORT = 4444)
- Uses pg library with connection pooling
- Connection string:
postgres://user:password@host:port/dbname - All queries use parameterized statements to prevent SQL injection
- Create route file in
server/routes/ - Add middleware/validation as needed
- Mount route in
server/app.js(specific routes before generic routes) - Add corresponding test cases in
tests/integration.test.js
- All submenus use
while(true)loop withcontinuefor persistence - Use
break;to stay in menu,return;to exit to parent menu - Menu system in
cli/menus.jshandles routing between admin and user views
- ✅ Core authentication and authorization
- ✅ Database schema with all relationships
- ✅ Full CRUD API endpoints
- ✅ Input validation and security measures
- ✅ CLI interface with persistent menus
- ✅ Integration test suite (35+ tests)
⚠️ Some test failures requiring investigation (validation, rate limiting edge cases)
Last Updated: May 2026 Version: 1.1.0