A CS50x 2024 Final Project
Video Demo: Youtube Link
Kinot is a full-stack CRUD expense tracking system built as a final project for Harvardβs CS50x 2024: Introduction to CS. It allows students to manage personal finances, log expenses and income and set saving goals.
The project emphasizes secure authentication, database-driven persistence, and interactive UI/UX. With a Flask backend and React + Vite frontend, Kinot demonstrates modern web development practices while remaining approachable for students and self-learners.
- React 19 + TypeScript β Component-based UI
- Vite β Fast build tool and dev server
- TailwindCSS β Utility-first styling
- React Router v7 β Client-side routing
- React Icons β Icons for UI
- Flask 3 β Python microframework
- Flask-SQLAlchemy β ORM for relational database
- Flask-JWT-Extended β JSON Web Token auth
- Flask-CORS β Cross-origin requests
- Werkzeug β Secure file handling
- SQLAlchemy ORM β portable across SQLite / PostgreSQL / MySQL
- Schema models:
Users,MonthlyFinances,Transactions,Goals,GoalContributions
kinot-cs50x/
βββ backend/ # Flask API
β βββ run.py # App entrypoint
β βββ app/
β βββ models.py # SQLAlchemy models
β βββ routes.py # API endpoints
β βββ services/ # Business logic
β βββ utils.py # JWT + hashing utilities
βββ frontend/ # React + Vite frontend
β βββ src/
β β βββ components/ # Reusable UI
β β βββ pages/ # Page-level views
β β βββ context/ # Global auth + toast
β β βββ hooks/ # Data hooks
βββ requirements.txt # Python backend deps
Kinot is relational, normalized around the User entity.
| Field | Type | Notes |
|---|---|---|
| id (PK) | Integer | Unique ID |
| fullname | String(100) | Userβs full name |
| username (unique) | String(50) | Login credential |
| _password_hashed | String(128) | Secured password hash |
| password_salt | Binary(16) | Random salt |
| secret_question | String(150) | Recovery Q |
| _secret_answer_hashed | String(150) | Recovery answer hash |
| secret_answer_salt | Binary(16) | Salt for answer |
| profile_path | String(255) | Profile image |
| Field | Type | Notes |
|---|---|---|
| id (PK) | Integer | Unique ID |
| user_id (FK) | Integer | Links to User |
| date | DateTime | Defaults to now |
| year | Integer | Extracted from date |
| month | Integer | Extracted from date |
| savings | Float | Total savings |
| spendings | Float | Total expenses |
| allowance | Float | Income/allowance |
Constraint: unique per (user_id, year, month)
| Field | Type | Notes |
|---|---|---|
| id (PK) | Integer | |
| user_id (FK) | Integer | |
| category | String(50) | e.g., savings, expenses |
| amount | Float | Transaction value |
| created_at | DateTime | Defaults now |
| method | String(30) | e.g., Cash, Card |
| description | String(100) | Optional |
| is_deleted | Boolean | Soft-delete |
| Field | Type | Notes |
|---|---|---|
| id (PK) | Integer | |
| user_id (FK) | Integer | |
| title | String(30) | Goal name |
| description | String(100) | Optional |
| created_at | DateTime | Timestamp |
| required_amount | Float | Target |
| current_amount | Float | Progress |
| is_deleted | Boolean | Soft-delete |
| image_path | String(255) | Goal image |
| Field | Type | Notes |
|---|---|---|
| id (PK) | Integer | |
| goal_id (FK) | Integer | |
| amount | Float | Contribution |
| added_at | DateTime | Timestamp |
- JWT-based login & registration
- Password hashing with unique salt
- Secret Q&A recovery with reset token
- Profile image upload (secured with
werkzeug)
Kinot uses JSON Web Tokens (JWTs) for authentication rather than server-side session storage because JWTs align with the project's goals of being API-first, easily scalable, and client-agnostic:
- Stateless: JWTs are self-contained tokens that eliminate the need for the server to keep a session record per user.
- API-first: The app exposes REST endpoints consumed by a React SPA. Bearer tokens in the Authorization header are standard and portable across clients.
- Clear lifecycle control: Combining short-lived access tokens with refresh tokens provides a good balance between security and usability.
Trade-offs:
- Revocation: Because access JWTs are stateless, immediate revocation is non-trivial. Kinot mitigates this by issuing short-lived access tokens (default 1 hour) and relying on refresh token controls for longer sessions. Additional strategies can include refresh-token rotation, server-side tracking of refresh tokens, or a revocation blacklist for critical cases.
- Token exposure: Tokens must be handled securely on clients (in-memory where possible, Secure HttpOnly cookies for refresh tokens in browser flows, Keychain/Keystore for mobile). All API traffic must use HTTPS.
- Track monthly savings, spendings, allowance
- Compare current vs previous month with percentage changes
- Visual progress cards for quick overview
- Add/edit/delete transactions
- Filter by category (
savings,allowance,expenses) - See recent 5 transactions on home page
- Searchable transactions in
/transactionspage - Export transactions to CSV File
- Create savings goals with images
- Contribute funds to goals
- Delete/archive completed goals
- Track contributions history
- Searchable goals in
/goalspage
- Protected routes (auth-required)
- Toast notifications for errors/success
- Modals for confirmations and forms
- Context API hooks for auth state
- Proxy Setup for accessing Flask routes from React frontend
- Directory Aliasing for quick imports instead of relative paths
- Register β Set username, password, secret Q&A
- Login β JWT token issued
- Home Dashboard β
- See current finances vs last month
- Quick list of latest transactions
- Active goals progress
- Add Transaction β e.g., β±500 expense on food
- Create Goal β βNew Laptopβ β±40,000 target
- Contribute to Goal β β±2,000 monthly
- Account Page β Update profile, change password, delete account
POST /registerβ Create userPOST /loginβ Authenticate userGET /homeβ Dashboard summary (finances, transactions, goals)POST /finance-updateβ Add income/expenseGET /transactionsβ Fetch all user transactionsGET /goalsβ Fetch all goalsPOST /update-goal/<id>β Contribute to goalPOST /delete-goal/<id>β Delete goalGET /accountβ User infoPUT /accountβ Update profileDELETE /accountβ Remove user
- Passwords never stored in plain text β salted SHA256 hashing
- JWT tokens expire (1 hour), used with role claims
- Profile uploads sanitized with
secure_filename - User-owned resources enforced with
user_requireddecorator
-
Data Visualization
- Add charts for income vs expenses trends
- Pie charts for category breakdown
-
Budgeting
- Allow monthly budget limits
- Alerts when nearing overspending
-
Sharing / Collaboration
- Family/group budgets with shared accounts
- Goal co-contribution
-
Accessibility
- Improve keyboard navigation
- Screen-reader compatibility
cd backend
python -m venv venv
source venv/bin/activate # or venv\Scripts\activate
pip install -r requirements.txt
python run.pyBackend runs at: http://127.0.0.1:5000
cd frontend
npm install
npm run devFrontend runs at: http://127.0.0.1:5173
Configured in vite.config.ts:
server: {
proxy: {
'/api': {
target: 'http://127.0.0.1:5000',
changeOrigin: true,
}
}
}- GitHub: ChocoCodes
- Developed as part of CS50x 2024 Final Project
- Contact: johnrlnd1704@gmail.com
MIT License. Free for personal and educational use.