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.