diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..2651f61 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,93 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + setup-backend: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.8' + + - name: Install backend dependencies + run: | + cd backend + pip install -r requirements.txt + + - name: Run backend tests + run: | + cd backend + python -m unittest discover -s tests + + setup-frontend: + needs: setup-backend + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: '20' + + - name: Install frontend dependencies + run: | + cd frontend + npm install + + - name: Run frontend lint + run: | + cd frontend + npm run lint + + - name: Run frontend tests + run: | + cd frontend + npm test + + check-docker-compose: + needs: [setup-backend, setup-frontend] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Start services + run: docker-compose up -d + + - name: Wait for vue-frontend service to become healthy + run: | + echo "Waiting for vue-frontend service to become healthy..." + RETRIES=10 + while [ $RETRIES -gt 0 ]; do + HEALTH_STATUS=$(docker inspect --format='{{.State.Health.Status}}' $(docker-compose ps -q vue-frontend)) + if [ "$HEALTH_STATUS" == "healthy" ]; then + echo "vue-frontend service is healthy." + break + else + echo "Waiting for vue-frontend service to become healthy. Current status: $HEALTH_STATUS" + sleep 10 + RETRIES=$((RETRIES-1)) + fi + done + + if [ $RETRIES -le 0 ]; then + echo "vue-frontend service did not become healthy in time." + exit 1 + fi + + - name: Shutdown services + run: docker-compose down \ No newline at end of file diff --git a/README.md b/README.md index 7dff23f..5e7e3b1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Self-Budgeting Tool +# Self-Budgeting Tool ![ workflow](https://github.com/devzero-inc/self-budgeting-tool/actions/workflows/main.yml/badge.svg) ## Overview The Self-Budgeting Tool is a comprehensive web application designed to empower users with complete control over their finances. Track income, expenses, recurring and fixed costs, savings, loans, and more in one intuitive interface. Make informed decisions and achieve your financial goals with ease. @@ -54,3 +54,18 @@ MySQL is the world's most popular open-source database. With its proven performa **Caching and Performance Optimization**: While not specified in the initial setup, implementing caching with tools such as Redis could significantly enhance the application's performance. It would be particularly beneficial for quickly accessing frequently used data, such as monthly expense summaries or savings progress reports. **Response Handling**: After the backend processes a request, including data storage, updates, or retrieval, it generates a response that is sent back to the frontend via the middleware. The Vue.js application then updates the user interface accordingly, reflecting the new or modified data. This ensures that users have real-time access to their financial information, contributing to a seamless and efficient user experience. + +## Installation + +### Prerequisites +- Docker + +Run locally: +```bash +git clone https://github.com/devzero-inc/self-budgeting-tool.git +cd self-budgeting-tool +docker compose up +``` +App will be running on ```PORT:4173``` -> [http://localhost:4173/](http://localhost:4173/) + +Now just go to [http://localhost:4173/](http://localhost:4173/) and explore the application. diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000..a71434a --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,7 @@ +__pycache__ +venv +.dockerignore +Dockerfile +.env +.gitignore +docker-compose.yml \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..d9897ce --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.8-slim + +WORKDIR /app + +COPY . /app + +RUN apt-get update && \ + apt-get install -y curl && \ + rm -rf /var/lib/apt/lists/* && \ + pip install --no-cache-dir -r requirements.txt + +EXPOSE 5000 + +ENV FLASK_ENV=production + +CMD ["flask", "run", "--host=0.0.0.0"] diff --git a/backend/init-db/db-init.sql b/backend/init-db/db-init.sql new file mode 100644 index 0000000..ef918b2 --- /dev/null +++ b/backend/init-db/db-init.sql @@ -0,0 +1,56 @@ +CREATE TABLE accounts ( + id char(36) NOT NULL DEFAULT (UUID()), + account_number varchar(20) NOT NULL, + account_holder varchar(255) NOT NULL, + account_type enum('savings','checking','loan','credit_card') NOT NULL, + balance decimal(15,2) NOT NULL, + bank_name varchar(255) NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (id) +); + +CREATE TABLE transactions ( + id char(36) NOT NULL DEFAULT (UUID()), + date date NOT NULL, + account_id char(36) NOT NULL, + bank_name varchar(255) NOT NULL, + payee varchar(255) DEFAULT NULL, + category varchar(255) DEFAULT NULL, + amount decimal(10,2) NOT NULL, + type enum('debit','credit') NOT NULL, + PRIMARY KEY (id), + FOREIGN KEY (account_id) REFERENCES accounts(id) +); + +INSERT INTO accounts (account_number, account_holder, account_type, balance, bank_name) VALUES +('1234567890', 'John Doe', 'savings', 1000, 'HSBC Bank'), +('2345678901', 'John Doe', 'checking', 2000, 'Chase Bank'), +('3456789012', 'John Doe', 'loan', 3000, 'Wells Fargo'), +('4567890123', 'John Doe', 'savings', 4000, 'Bank of America'); + +INSERT INTO transactions (date, account_id, bank_name, payee, category, amount, type) VALUES +('2024-03-01', (SELECT id FROM accounts WHERE bank_name = 'HSBC Bank'), 'HSBC Bank', 'Netflix', 'Entertainment', -50.00, 'debit'), +('2024-03-02', (SELECT id FROM accounts WHERE bank_name = 'Chase Bank'), 'Chase Bank', 'Amazon', 'Shopping', -120.00, 'debit'), +('2024-03-03', (SELECT id FROM accounts WHERE bank_name = 'Wells Fargo'), 'Wells Fargo', 'Apple', 'Electronics', -200.00, 'debit'), +('2024-03-04', (SELECT id FROM accounts WHERE bank_name = 'Bank of America'), 'Bank of America', 'Spotify', 'Entertainment', -10.00, 'debit'), +('2024-03-05', (SELECT id FROM accounts WHERE bank_name = 'HSBC Bank'), 'HSBC Bank', 'Adobe', 'Software', -50.00, 'debit'), +('2024-03-06', (SELECT id FROM accounts WHERE bank_name = 'Chase Bank'), 'Chase Bank', 'Local Cafe', 'Dining', -30.00, 'debit'), +('2024-03-07', (SELECT id FROM accounts WHERE bank_name = 'Wells Fargo'), 'Wells Fargo', 'Gym', 'Health', -60.00, 'debit'), +('2024-03-08', (SELECT id FROM accounts WHERE bank_name = 'Bank of America'), 'Bank of America', 'Electric Company', 'Utilities', -150.00, 'debit'), +('2024-03-09', (SELECT id FROM accounts WHERE bank_name = 'HSBC Bank'), 'HSBC Bank', 'Water Supply', 'Utilities', -80.00, 'debit'), +('2024-03-10', (SELECT id FROM accounts WHERE bank_name = 'Chase Bank'), 'Chase Bank', 'Internet Provider', 'Utilities', -60.00, 'debit'), +('2024-03-11', (SELECT id FROM accounts WHERE bank_name = 'HSBC Bank'), 'HSBC Bank', 'Mortgage', 'Deposit', 2000.00, 'credit'), +('2024-03-12', (SELECT id FROM accounts WHERE bank_name = 'Chase Bank'), 'Chase Bank', 'House Asset', 'Deposit', 1500.00, 'credit'), +('2024-03-13', (SELECT id FROM accounts WHERE bank_name = 'HSBC Bank'), 'HSBC Bank', 'Salary', 'Deposit', 3000.00, 'credit'), +('2024-03-14', (SELECT id FROM accounts WHERE bank_name = 'Bank of America'), 'Bank of America', 'Investment Return', 'Deposit', 1200.00, 'credit'), +('2024-03-15', (SELECT id FROM accounts WHERE bank_name = 'HSBC Bank'), 'HSBC Bank', 'Mortgage', 'Deposit', 2200.00, 'credit'), +('2024-03-16', (SELECT id FROM accounts WHERE bank_name = 'Chase Bank'), 'Chase Bank', 'House Asset', 'Deposit', 1800.00, 'credit'), +('2024-03-17', (SELECT id FROM accounts WHERE bank_name = 'Wells Fargo'), 'Wells Fargo', 'Salary', 'Deposit', 2500.00, 'credit'), +('2024-03-18', (SELECT id FROM accounts WHERE bank_name = 'Bank of America'), 'Bank of America', 'Investment Return', 'Deposit', 1300.00, 'credit'), +('2024-03-19', (SELECT id FROM accounts WHERE bank_name = 'HSBC Bank'), 'HSBC Bank', 'Mortgage', 'Deposit', 2100.00, 'credit'), +('2024-03-20', (SELECT id FROM accounts WHERE bank_name = 'Chase Bank'), 'Chase Bank', 'House Asset', 'Deposit', 1600.00, 'credit'), +('2024-03-21', (SELECT id FROM accounts WHERE bank_name = 'Wells Fargo'), 'Wells Fargo', 'Salary', 'Deposit', 2800.00, 'credit'), +('2024-03-22', (SELECT id FROM accounts WHERE bank_name = 'Bank of America'), 'Bank of America', 'Investment Return', 'Deposit', 1500.00, 'credit'), +('2024-03-23', (SELECT id FROM accounts WHERE bank_name = 'HSBC Bank'), 'HSBC Bank', 'Mortgage', 'Deposit', 2300.00, 'credit'), +('2024-03-24', (SELECT id FROM accounts WHERE bank_name = 'Chase Bank'), 'Chase Bank', 'House Asset', 'Deposit', 1700.00, 'credit'); \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..496a7a8 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,56 @@ +version: '3.8' + +services: + flask-backend: + build: ./backend + ports: + - "5000:5000" + environment: + - DB_HOST=db + - DB_PASSWORD=password + - DB_DATABASE=budget + - DB_USER=root + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:5000/health"] + interval: 10s + timeout: 10s + retries: 20 + depends_on: + db: + condition: service_healthy + + vue-frontend: + build: ./frontend + ports: + - "4173:4173" + depends_on: + flask-backend: + condition: service_healthy + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:4173/"] + interval: 10s + timeout: 10s + retries: 20 + start_period: 10s + + db: + image: mysql + environment: + MYSQL_USER: 'user' + MYSQL_PASSWORD: 'password' + MYSQL_ROOT_PASSWORD: 'password' + MYSQL_DATABASE: budget + ports: + - "3307:3306" + volumes: + - db-data:/var/lib/mysql + - ./backend/init-db:/docker-entrypoint-initdb.d + healthcheck: + test: ["CMD-SHELL", 'mysql --user=user --password=password --database=budget -e "SELECT 1"'] + interval: 10s + timeout: 10s + retries: 20 + start_period: 10s + +volumes: + db-data: \ No newline at end of file diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 0000000..b45a31c --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,13 @@ +./node_modules +Dockerfile +.dockerignore +docker-compose.yml +.eslintrc.cjs +.gitignore +DZ_RECIPE.yml +./jest.config.ts +./README.md +./__tests__ +./.github +.vscode +dist \ No newline at end of file diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..80c5b38 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,15 @@ +FROM node:20-alpine + +RUN apk add --no-cache curl jq + +WORKDIR /usr/src/app + +COPY package*.json . + +RUN npm install + +COPY . . + +EXPOSE 4173 + +CMD [ "npm", "run", "start" ] \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index 6df0c46..4769bb2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,7 +10,8 @@ "build-only": "vite build", "type-check": "vue-tsc --build --force", "lint": "eslint . --ext .vue,.js,.jsx,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", - "test": "jest" + "test": "jest", + "start": "npm run build && npm run preview" }, "dependencies": { "axios": "^1.6.7", diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 5c45e1d..3629093 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -12,5 +12,8 @@ export default defineConfig({ alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } + }, + server:{ + host: true } })