From e0e0cc65f18f997d132165644738fc2d3b9c9fa3 Mon Sep 17 00:00:00 2001 From: Kushal Gaywala Date: Sat, 28 Feb 2026 19:16:26 +0100 Subject: [PATCH] chore: convert to Turborepo + npm workspaces monorepo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Move React/Vite frontend to apps/web/ (@budgetwise/web) - Add apps/appwrite/ (@budgetwise/appwrite) to source-control the Appwrite backend: declarative schema in appwrite.json (5 collections), CLI-based deploy.sh for containerized use, functions/ dir for future Appwrite Functions - Add turbo.json for task orchestration (build, deploy, dev) - Replace .gitlab-ci.yml with Woodpecker CI pipelines in .woodpecker/: web-production.yml — push to main → build + rsync to prod web-staging.yml — push to staging → build + rsync to staging web-preview.yml — PR open → deploy to {pr}.{domain}; PR close → cleanup appwrite.yml — schema changes in apps/appwrite/ → CLI deploy - All secrets injected via Woodpecker CI (no committed .env files) --- .gitignore | 23 ++- .gitlab-ci.yml | 174 ---------------- .woodpecker/appwrite.yml | 58 ++++++ .woodpecker/web-preview.yml | 78 ++++++++ .woodpecker/web-production.yml | 50 +++++ .woodpecker/web-staging.yml | 50 +++++ apps/appwrite/appwrite.json | 169 ++++++++++++++++ apps/appwrite/functions/.gitkeep | 0 apps/appwrite/package.json | 12 ++ apps/appwrite/scripts/deploy.sh | 58 ++++++ .../appwrite/scripts}/setup-appwrite.cjs | 2 +- index.html => apps/web/index.html | 0 apps/web/package.json | 34 ++++ .../web/postcss.config.js | 0 {public => apps/web/public}/icon.svg | 0 {src => apps/web/src}/App.tsx | 0 {src => apps/web/src}/appwrite/auth.ts | 0 {src => apps/web/src}/appwrite/config.ts | 0 {src => apps/web/src}/appwrite/db.ts | 0 .../web/src}/components/layout/BottomNav.tsx | 0 .../web/src}/components/layout/Header.tsx | 0 .../web/src}/components/layout/Layout.tsx | 0 {src => apps/web/src}/components/ui/Badge.tsx | 0 .../web/src}/components/ui/Button.tsx | 0 {src => apps/web/src}/components/ui/Card.tsx | 0 {src => apps/web/src}/components/ui/Input.tsx | 0 {src => apps/web/src}/components/ui/Modal.tsx | 0 {src => apps/web/src}/index.css | 0 {src => apps/web/src}/lib/calculations.ts | 0 {src => apps/web/src}/lib/utils.ts | 0 {src => apps/web/src}/main.tsx | 0 {src => apps/web/src}/pages/Auth.tsx | 0 {src => apps/web/src}/pages/BalanceSheet.tsx | 0 {src => apps/web/src}/pages/BucketDetail.tsx | 0 {src => apps/web/src}/pages/Buckets.tsx | 0 {src => apps/web/src}/pages/Dashboard.tsx | 0 {src => apps/web/src}/pages/Debts.tsx | 0 .../web/src}/pages/LoanCalculator.tsx | 0 {src => apps/web/src}/pages/More.tsx | 0 {src => apps/web/src}/pages/Settings.tsx | 0 {src => apps/web/src}/store/index.ts | 0 {src => apps/web/src}/types/index.ts | 0 .../web/tailwind.config.js | 0 tsconfig.json => apps/web/tsconfig.json | 0 .../web/tsconfig.node.json | 0 vite.config.ts => apps/web/vite.config.ts | 0 package-lock.json | 185 +++++++++++++++--- package.json | 36 +--- turbo.json | 26 +++ 49 files changed, 729 insertions(+), 226 deletions(-) delete mode 100644 .gitlab-ci.yml create mode 100644 .woodpecker/appwrite.yml create mode 100644 .woodpecker/web-preview.yml create mode 100644 .woodpecker/web-production.yml create mode 100644 .woodpecker/web-staging.yml create mode 100644 apps/appwrite/appwrite.json create mode 100644 apps/appwrite/functions/.gitkeep create mode 100644 apps/appwrite/package.json create mode 100755 apps/appwrite/scripts/deploy.sh rename {scripts => apps/appwrite/scripts}/setup-appwrite.cjs (98%) rename index.html => apps/web/index.html (100%) create mode 100644 apps/web/package.json rename postcss.config.js => apps/web/postcss.config.js (100%) rename {public => apps/web/public}/icon.svg (100%) rename {src => apps/web/src}/App.tsx (100%) rename {src => apps/web/src}/appwrite/auth.ts (100%) rename {src => apps/web/src}/appwrite/config.ts (100%) rename {src => apps/web/src}/appwrite/db.ts (100%) rename {src => apps/web/src}/components/layout/BottomNav.tsx (100%) rename {src => apps/web/src}/components/layout/Header.tsx (100%) rename {src => apps/web/src}/components/layout/Layout.tsx (100%) rename {src => apps/web/src}/components/ui/Badge.tsx (100%) rename {src => apps/web/src}/components/ui/Button.tsx (100%) rename {src => apps/web/src}/components/ui/Card.tsx (100%) rename {src => apps/web/src}/components/ui/Input.tsx (100%) rename {src => apps/web/src}/components/ui/Modal.tsx (100%) rename {src => apps/web/src}/index.css (100%) rename {src => apps/web/src}/lib/calculations.ts (100%) rename {src => apps/web/src}/lib/utils.ts (100%) rename {src => apps/web/src}/main.tsx (100%) rename {src => apps/web/src}/pages/Auth.tsx (100%) rename {src => apps/web/src}/pages/BalanceSheet.tsx (100%) rename {src => apps/web/src}/pages/BucketDetail.tsx (100%) rename {src => apps/web/src}/pages/Buckets.tsx (100%) rename {src => apps/web/src}/pages/Dashboard.tsx (100%) rename {src => apps/web/src}/pages/Debts.tsx (100%) rename {src => apps/web/src}/pages/LoanCalculator.tsx (100%) rename {src => apps/web/src}/pages/More.tsx (100%) rename {src => apps/web/src}/pages/Settings.tsx (100%) rename {src => apps/web/src}/store/index.ts (100%) rename {src => apps/web/src}/types/index.ts (100%) rename tailwind.config.js => apps/web/tailwind.config.js (100%) rename tsconfig.json => apps/web/tsconfig.json (100%) rename tsconfig.node.json => apps/web/tsconfig.node.json (100%) rename vite.config.ts => apps/web/vite.config.ts (100%) create mode 100644 turbo.json diff --git a/.gitignore b/.gitignore index 14be480..905eaee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,29 @@ +# Dependencies node_modules -dist -dist-ssr -*.local + +# Turbo cache +.turbo + +# Build outputs +apps/web/dist +apps/web/dist-ssr + +# Local env files — never commit .env .env.local .env.production .env.staging .env.preview +apps/**/.env +apps/**/.env.local +apps/**/.env.production +apps/**/.env.staging +apps/**/.env.preview + +# OS .DS_Store *.pem + +# Coverage coverage +*.local diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index bde5e10..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,174 +0,0 @@ -# ───────────────────────────────────────────────────────────────────────────── -# BudgetWise CI/CD Pipeline -# -# Branch → Environment mapping: -# main → production (https://{PROD_URL}) -# staging → staging (https://{STAGING_URL}) -# merge request → preview (https://{MR_IID}.{BASE_DOMAIN}) -# -# All config lives in GitLab CI/CD variables (Settings → CI/CD → Variables). -# No .env files are committed — the build generates them at runtime. -# -# Required variables: -# SSH_PRIVATE_KEY Private key with access to DEPLOY_HOST -# DEPLOY_HOST Server hostname or IP -# DEPLOY_USER SSH username on the server -# DEPLOY_PATH Base deploy path e.g. /var/www/budget-app -# -# PROD_URL e.g. https://budget.kushalgaywala.com -# PROD_APPWRITE_ENDPOINT e.g. https://appwrite.example.com/v1 -# PROD_APPWRITE_PROJECT_ID -# -# STAGING_URL e.g. https://staging.budget.kushalgaywala.com -# STAGING_APPWRITE_ENDPOINT -# STAGING_APPWRITE_PROJECT_ID -# -# BASE_DOMAIN MR preview base e.g. budget.kushalgaywala.com -# Preview URLs: https://{mr-iid}.{BASE_DOMAIN} -# Preview shares staging Appwrite credentials. -# -# Server directory layout: -# $DEPLOY_PATH/production/ ← prod -# $DEPLOY_PATH/staging/ ← staging -# $DEPLOY_PATH/mr-{iid}/ ← MR preview (removed on MR close) -# ───────────────────────────────────────────────────────────────────────────── - -stages: - - build - - deploy - -.deploy_base: - image: alpine:latest - before_script: - - apk add --no-cache rsync openssh-client - - eval $(ssh-agent -s) - - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - - - mkdir -p ~/.ssh && chmod 700 ~/.ssh - - ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts - -# ── Production ──────────────────────────────────────────────────────────────── - -build:production: - stage: build - image: node:20-alpine - variables: - VITE_APP_URL: $PROD_URL - VITE_APPWRITE_ENDPOINT: $PROD_APPWRITE_ENDPOINT - VITE_APPWRITE_PROJECT_ID: $PROD_APPWRITE_PROJECT_ID - script: - - npm ci - - npm run build - - echo "VITE_APP_URL=$PROD_URL" > deploy.env - artifacts: - paths: - - dist/ - reports: - dotenv: deploy.env - expire_in: 1 hour - rules: - - if: $CI_COMMIT_BRANCH == "main" - -deploy:production: - extends: .deploy_base - stage: deploy - environment: - name: production - url: $VITE_APP_URL - script: - - rsync -avz --delete dist/ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/production/" - needs: - - job: build:production - artifacts: true - rules: - - if: $CI_COMMIT_BRANCH == "main" - -# ── Staging ─────────────────────────────────────────────────────────────────── - -build:staging: - stage: build - image: node:20-alpine - variables: - VITE_APP_URL: $STAGING_URL - VITE_APPWRITE_ENDPOINT: $STAGING_APPWRITE_ENDPOINT - VITE_APPWRITE_PROJECT_ID: $STAGING_APPWRITE_PROJECT_ID - script: - - npm ci - - npx tsc --noEmit - - npx vite build --mode staging - - echo "VITE_APP_URL=$STAGING_URL" > deploy.env - artifacts: - paths: - - dist/ - reports: - dotenv: deploy.env - expire_in: 1 hour - rules: - - if: $CI_COMMIT_BRANCH == "staging" - -deploy:staging: - extends: .deploy_base - stage: deploy - environment: - name: staging - url: $VITE_APP_URL - script: - - rsync -avz --delete dist/ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/staging/" - needs: - - job: build:staging - artifacts: true - rules: - - if: $CI_COMMIT_BRANCH == "staging" - -# ── Preview / MR ────────────────────────────────────────────────────────────── -# One ephemeral environment per MR: https://{mr-iid}.{BASE_DOMAIN} -# Uses staging Appwrite credentials. -# Environment is torn down automatically when the MR is closed. - -build:preview: - stage: build - image: node:20-alpine - variables: - VITE_APPWRITE_ENDPOINT: $STAGING_APPWRITE_ENDPOINT - VITE_APPWRITE_PROJECT_ID: $STAGING_APPWRITE_PROJECT_ID - script: - - npm ci - - npx tsc --noEmit - - export MR_URL="https://$CI_MERGE_REQUEST_IID.$BASE_DOMAIN" - - VITE_APP_URL=$MR_URL npx vite build --mode preview - - echo "VITE_APP_URL=$MR_URL" > deploy.env - artifacts: - paths: - - dist/ - reports: - dotenv: deploy.env - expire_in: 1 day - rules: - - if: $CI_PIPELINE_SOURCE == "merge_request_event" - -deploy:preview: - extends: .deploy_base - stage: deploy - environment: - name: review/mr-$CI_MERGE_REQUEST_IID - url: $VITE_APP_URL - on_stop: stop:preview - script: - - rsync -avz --delete dist/ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/mr-$CI_MERGE_REQUEST_IID/" - needs: - - job: build:preview - artifacts: true - rules: - - if: $CI_PIPELINE_SOURCE == "merge_request_event" - -stop:preview: - extends: .deploy_base - stage: deploy - environment: - name: review/mr-$CI_MERGE_REQUEST_IID - action: stop - script: - - ssh "$DEPLOY_USER@$DEPLOY_HOST" "rm -rf $DEPLOY_PATH/mr-$CI_MERGE_REQUEST_IID" - rules: - - if: $CI_PIPELINE_SOURCE == "merge_request_event" - when: manual - allow_failure: true diff --git a/.woodpecker/appwrite.yml b/.woodpecker/appwrite.yml new file mode 100644 index 0000000..d506991 --- /dev/null +++ b/.woodpecker/appwrite.yml @@ -0,0 +1,58 @@ +# ── Appwrite: Schema Deploy ──────────────────────────────────────────────────── +# Triggered only when files inside apps/appwrite/ change. +# main branch → deploys to production Appwrite project +# staging branch → deploys to staging Appwrite project +# +# Uses the Appwrite CLI (via apps/appwrite/scripts/deploy.sh) to push the +# schema defined in apps/appwrite/appwrite.json declaratively. +# +# Required Woodpecker secrets: +# prod_appwrite_endpoint +# prod_appwrite_project_id +# prod_appwrite_api_key needs databases.write + collections.write scopes +# staging_appwrite_endpoint +# staging_appwrite_project_id +# staging_appwrite_api_key +# ───────────────────────────────────────────────────────────────────────────── + +when: + - event: push + branch: + - main + - staging + path: + include: + - apps/appwrite/** + +steps: + - name: deploy-production + image: node:20-alpine + environment: + APPWRITE_ENDPOINT: + from_secret: prod_appwrite_endpoint + APPWRITE_PROJECT_ID: + from_secret: prod_appwrite_project_id + APPWRITE_API_KEY: + from_secret: prod_appwrite_api_key + commands: + - npm ci + - npm run deploy -w @budgetwise/appwrite + when: + - branch: main + event: push + + - name: deploy-staging + image: node:20-alpine + environment: + APPWRITE_ENDPOINT: + from_secret: staging_appwrite_endpoint + APPWRITE_PROJECT_ID: + from_secret: staging_appwrite_project_id + APPWRITE_API_KEY: + from_secret: staging_appwrite_api_key + commands: + - npm ci + - npm run deploy -w @budgetwise/appwrite + when: + - branch: staging + event: push diff --git a/.woodpecker/web-preview.yml b/.woodpecker/web-preview.yml new file mode 100644 index 0000000..5ce5528 --- /dev/null +++ b/.woodpecker/web-preview.yml @@ -0,0 +1,78 @@ +# ── Web: Preview (Pull Requests) ────────────────────────────────────────────── +# Triggered on pull_request open/sync → deploys to https://{pr-number}.{base_domain} +# Triggered on pull_request_closed → cleans up the preview directory +# +# Preview environments share staging Appwrite credentials. +# PR number is available as $CI_COMMIT_PULL_REQUEST. +# +# Required Woodpecker secrets: +# base_domain e.g. budget.kushalgaywala.com +# staging_appwrite_endpoint +# staging_appwrite_project_id +# ssh_private_key +# deploy_host +# deploy_user +# deploy_path +# ───────────────────────────────────────────────────────────────────────────── + +when: + - event: [pull_request, pull_request_closed] + +steps: + - name: build + image: node:20-alpine + environment: + BASE_DOMAIN: + from_secret: base_domain + VITE_APPWRITE_ENDPOINT: + from_secret: staging_appwrite_endpoint + VITE_APPWRITE_PROJECT_ID: + from_secret: staging_appwrite_project_id + commands: + - export MR_URL="https://$CI_COMMIT_PULL_REQUEST.$BASE_DOMAIN" + - npm ci + - VITE_APP_URL=$MR_URL npm run build:preview -w @budgetwise/web + when: + - event: pull_request + + - name: deploy + image: alpine + environment: + SSH_PRIVATE_KEY: + from_secret: ssh_private_key + DEPLOY_HOST: + from_secret: deploy_host + DEPLOY_USER: + from_secret: deploy_user + DEPLOY_PATH: + from_secret: deploy_path + commands: + - apk add --no-cache rsync openssh-client + - eval $(ssh-agent -s) + - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - + - mkdir -p ~/.ssh && chmod 700 ~/.ssh + - ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts + - rsync -avz --delete apps/web/dist/ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/mr-$CI_COMMIT_PULL_REQUEST/" + when: + - event: pull_request + + - name: cleanup + image: alpine + environment: + SSH_PRIVATE_KEY: + from_secret: ssh_private_key + DEPLOY_HOST: + from_secret: deploy_host + DEPLOY_USER: + from_secret: deploy_user + DEPLOY_PATH: + from_secret: deploy_path + commands: + - apk add --no-cache openssh-client + - eval $(ssh-agent -s) + - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - + - mkdir -p ~/.ssh && chmod 700 ~/.ssh + - ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts + - ssh "$DEPLOY_USER@$DEPLOY_HOST" "rm -rf $DEPLOY_PATH/mr-$CI_COMMIT_PULL_REQUEST" + when: + - event: pull_request_closed diff --git a/.woodpecker/web-production.yml b/.woodpecker/web-production.yml new file mode 100644 index 0000000..3ee508c --- /dev/null +++ b/.woodpecker/web-production.yml @@ -0,0 +1,50 @@ +# ── Web: Production ─────────────────────────────────────────────────────────── +# Triggered on every push to `main`. +# Builds the Vite app with production credentials then rsyncs to the server. +# +# Required Woodpecker secrets (Repository → Settings → Secrets): +# prod_url e.g. https://budget.kushalgaywala.com +# prod_appwrite_endpoint e.g. https://appwrite.example.com/v1 +# prod_appwrite_project_id +# ssh_private_key +# deploy_host +# deploy_user +# deploy_path e.g. /var/www/budget-app +# ───────────────────────────────────────────────────────────────────────────── + +when: + - event: push + branch: main + +steps: + - name: build + image: node:20-alpine + environment: + VITE_APP_URL: + from_secret: prod_url + VITE_APPWRITE_ENDPOINT: + from_secret: prod_appwrite_endpoint + VITE_APPWRITE_PROJECT_ID: + from_secret: prod_appwrite_project_id + commands: + - npm ci + - npm run build -w @budgetwise/web + + - name: deploy + image: alpine + environment: + SSH_PRIVATE_KEY: + from_secret: ssh_private_key + DEPLOY_HOST: + from_secret: deploy_host + DEPLOY_USER: + from_secret: deploy_user + DEPLOY_PATH: + from_secret: deploy_path + commands: + - apk add --no-cache rsync openssh-client + - eval $(ssh-agent -s) + - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - + - mkdir -p ~/.ssh && chmod 700 ~/.ssh + - ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts + - rsync -avz --delete apps/web/dist/ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/production/" diff --git a/.woodpecker/web-staging.yml b/.woodpecker/web-staging.yml new file mode 100644 index 0000000..a561cfe --- /dev/null +++ b/.woodpecker/web-staging.yml @@ -0,0 +1,50 @@ +# ── Web: Staging ────────────────────────────────────────────────────────────── +# Triggered on every push to `staging`. +# Builds with --mode staging (uses .env.staging locally; CI uses secrets). +# +# Required Woodpecker secrets: +# staging_url +# staging_appwrite_endpoint +# staging_appwrite_project_id +# ssh_private_key +# deploy_host +# deploy_user +# deploy_path +# ───────────────────────────────────────────────────────────────────────────── + +when: + - event: push + branch: staging + +steps: + - name: build + image: node:20-alpine + environment: + VITE_APP_URL: + from_secret: staging_url + VITE_APPWRITE_ENDPOINT: + from_secret: staging_appwrite_endpoint + VITE_APPWRITE_PROJECT_ID: + from_secret: staging_appwrite_project_id + commands: + - npm ci + - npm run build:staging -w @budgetwise/web + + - name: deploy + image: alpine + environment: + SSH_PRIVATE_KEY: + from_secret: ssh_private_key + DEPLOY_HOST: + from_secret: deploy_host + DEPLOY_USER: + from_secret: deploy_user + DEPLOY_PATH: + from_secret: deploy_path + commands: + - apk add --no-cache rsync openssh-client + - eval $(ssh-agent -s) + - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - + - mkdir -p ~/.ssh && chmod 700 ~/.ssh + - ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts + - rsync -avz --delete apps/web/dist/ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/staging/" diff --git a/apps/appwrite/appwrite.json b/apps/appwrite/appwrite.json new file mode 100644 index 0000000..336bf48 --- /dev/null +++ b/apps/appwrite/appwrite.json @@ -0,0 +1,169 @@ +{ + "$schema": "https://appwrite.io/sdk/schemas/appwrite.json", + "projectId": "", + "projectName": "BudgetWise", + "databases": [ + { + "databaseId": "budget_db", + "name": "BudgetWise", + "enabled": true, + "collections": [ + { + "databaseId": "budget_db", + "collectionId": "balance_sheets", + "name": "Balance Sheets", + "enabled": true, + "documentSecurity": false, + "permissions": [ + "read(\"users\")", + "create(\"users\")", + "update(\"users\")", + "delete(\"users\")" + ], + "attributes": [ + { "key": "month", "type": "integer", "required": true, "array": false }, + { "key": "year", "type": "integer", "required": true, "array": false }, + { "key": "buffer_type", "type": "string", "size": 20, "required": true, "array": false }, + { "key": "buffer_value", "type": "float", "required": true, "default": 0, "array": false }, + { "key": "user_id", "type": "string", "size": 64, "required": true, "array": false } + ], + "indexes": [ + { + "key": "user_month_year", + "type": "key", + "attributes": ["user_id", "month", "year"], + "orders": ["ASC", "DESC", "DESC"] + } + ] + }, + { + "databaseId": "budget_db", + "collectionId": "incomes", + "name": "Incomes", + "enabled": true, + "documentSecurity": false, + "permissions": [ + "read(\"users\")", + "create(\"users\")", + "update(\"users\")", + "delete(\"users\")" + ], + "attributes": [ + { "key": "balance_sheet_id", "type": "string", "size": 64, "required": true, "array": false }, + { "key": "name", "type": "string", "size": 128,"required": true, "array": false }, + { "key": "amount", "type": "float", "required": true, "array": false }, + { "key": "frequency", "type": "string", "size": 20, "required": true, "array": false }, + { "key": "user_id", "type": "string", "size": 64, "required": true, "array": false } + ], + "indexes": [ + { + "key": "user_sheet", + "type": "key", + "attributes": ["user_id", "balance_sheet_id"], + "orders": [] + } + ] + }, + { + "databaseId": "budget_db", + "collectionId": "buckets", + "name": "Buckets", + "enabled": true, + "documentSecurity": false, + "permissions": [ + "read(\"users\")", + "create(\"users\")", + "update(\"users\")", + "delete(\"users\")" + ], + "attributes": [ + { "key": "name", "type": "string", "size": 128, "required": true, "array": false }, + { "key": "description", "type": "string", "size": 512, "required": false, "default": "", "array": false }, + { "key": "type", "type": "string", "size": 32, "required": true, "array": false }, + { "key": "current_balance", "type": "float", "required": true, "default": 0, "array": false }, + { "key": "goal_amount", "type": "float", "required": false, "default": 0, "array": false }, + { "key": "goal_type", "type": "string", "size": 20, "required": false, "default": "amount", "array": false }, + { "key": "goal_frequency", "type": "string", "size": 20, "required": false, "default": "monthly", "array": false }, + { "key": "return_percent", "type": "float", "required": false, "default": 0, "array": false }, + { "key": "return_frequency", "type": "string", "size": 20, "required": false, "default": "yearly", "array": false }, + { "key": "color", "type": "string", "size": 16, "required": true, "array": false }, + { "key": "sort_order", "type": "integer", "required": false, "default": 0, "array": false }, + { "key": "user_id", "type": "string", "size": 64, "required": true, "array": false } + ], + "indexes": [ + { + "key": "user_order", + "type": "key", + "attributes": ["user_id", "sort_order"], + "orders": ["ASC", "ASC"] + } + ] + }, + { + "databaseId": "budget_db", + "collectionId": "debts", + "name": "Debts", + "enabled": true, + "documentSecurity": false, + "permissions": [ + "read(\"users\")", + "create(\"users\")", + "update(\"users\")", + "delete(\"users\")" + ], + "attributes": [ + { "key": "name", "type": "string", "size": 128, "required": true, "array": false }, + { "key": "principal", "type": "float", "required": true, "array": false }, + { "key": "remaining_balance", "type": "float", "required": true, "array": false }, + { "key": "interest_rate", "type": "float", "required": true, "array": false }, + { "key": "interest_frequency", "type": "string", "size": 20, "required": true, "array": false }, + { "key": "term_months", "type": "integer", "required": true, "array": false }, + { "key": "monthly_payment", "type": "float", "required": true, "array": false }, + { "key": "is_auto_calculated", "type": "boolean", "required": true, "default": false, "array": false }, + { "key": "start_date", "type": "string", "size": 24, "required": true, "array": false }, + { "key": "user_id", "type": "string", "size": 64, "required": true, "array": false } + ], + "indexes": [ + { + "key": "user_id_idx", + "type": "key", + "attributes": ["user_id"], + "orders": [] + } + ] + }, + { + "databaseId": "budget_db", + "collectionId": "transactions", + "name": "Transactions", + "enabled": true, + "documentSecurity": false, + "permissions": [ + "read(\"users\")", + "create(\"users\")", + "update(\"users\")", + "delete(\"users\")" + ], + "attributes": [ + { "key": "bucket_id", "type": "string", "size": 64, "required": true, "array": false }, + { "key": "type", "type": "string", "size": 20, "required": true, "array": false }, + { "key": "amount", "type": "float", "required": true, "array": false }, + { "key": "date", "type": "string", "size": 24, "required": true, "array": false }, + { "key": "notes", "type": "string", "size": 512, "required": false, "default": "", "array": false }, + { "key": "balance_after","type": "float", "required": true, "array": false }, + { "key": "user_id", "type": "string", "size": 64, "required": true, "array": false } + ], + "indexes": [ + { + "key": "user_bucket_date", + "type": "key", + "attributes": ["user_id", "bucket_id", "date"], + "orders": ["ASC", "ASC", "DESC"] + } + ] + } + ] + } + ], + "functions": [] +} diff --git a/apps/appwrite/functions/.gitkeep b/apps/appwrite/functions/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/apps/appwrite/package.json b/apps/appwrite/package.json new file mode 100644 index 0000000..a36dd7d --- /dev/null +++ b/apps/appwrite/package.json @@ -0,0 +1,12 @@ +{ + "name": "@budgetwise/appwrite", + "private": true, + "version": "1.0.0", + "scripts": { + "deploy": "bash scripts/deploy.sh", + "setup": "node scripts/setup-appwrite.cjs" + }, + "devDependencies": { + "node-appwrite": "^12.0.0" + } +} diff --git a/apps/appwrite/scripts/deploy.sh b/apps/appwrite/scripts/deploy.sh new file mode 100755 index 0000000..f40c761 --- /dev/null +++ b/apps/appwrite/scripts/deploy.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# ───────────────────────────────────────────────────────────────────────────── +# BudgetWise – Appwrite Deploy Script +# +# Deploys the schema defined in appwrite.json (databases, collections) using +# the Appwrite CLI. Runs functions deploy if functions/ contains any functions. +# +# Works locally and in CI (Woodpecker, Docker, etc.) — credentials come from +# environment variables, never from committed files. +# +# Required environment variables: +# APPWRITE_ENDPOINT e.g. https://appwrite.example.com/v1 +# APPWRITE_PROJECT_ID The Appwrite project ID +# APPWRITE_API_KEY API key with databases.write + collections.write scopes +# +# Local usage: +# export APPWRITE_ENDPOINT=... APPWRITE_PROJECT_ID=... APPWRITE_API_KEY=... +# bash scripts/deploy.sh +# +# Or with a local .env file at apps/appwrite/.env: +# set -a && source .env && set +a && bash scripts/deploy.sh +# ───────────────────────────────────────────────────────────────────────────── + +set -euo pipefail + +APPWRITE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +CLI="npx --yes appwrite@6" + +# ── Validate required env vars ──────────────────────────────────────────────── +: "${APPWRITE_ENDPOINT:?APPWRITE_ENDPOINT is required}" +: "${APPWRITE_PROJECT_ID:?APPWRITE_PROJECT_ID is required}" +: "${APPWRITE_API_KEY:?APPWRITE_API_KEY is required}" + +echo "==> Deploying Appwrite schema" +echo " Endpoint : $APPWRITE_ENDPOINT" +echo " Project : $APPWRITE_PROJECT_ID" + +# ── Configure CLI session ───────────────────────────────────────────────────── +# Writes to ~/.appwrite/prefs.json — ephemeral in CI runners. +$CLI client \ + --endpoint "$APPWRITE_ENDPOINT" \ + --project-id "$APPWRITE_PROJECT_ID" \ + --key "$APPWRITE_API_KEY" + +# ── Deploy databases + collections ──────────────────────────────────────────── +cd "$APPWRITE_DIR" +echo "==> Deploying databases..." +$CLI deploy database --all --yes + +# ── Deploy functions (skipped if functions/ is empty) ───────────────────────── +if [ -d "functions" ] && [ -n "$(ls -A functions 2>/dev/null | grep -v '\.gitkeep')" ]; then + echo "==> Deploying functions..." + $CLI deploy function --all --yes +else + echo "==> No functions to deploy, skipping." +fi + +echo "==> Appwrite deploy complete." diff --git a/scripts/setup-appwrite.cjs b/apps/appwrite/scripts/setup-appwrite.cjs similarity index 98% rename from scripts/setup-appwrite.cjs rename to apps/appwrite/scripts/setup-appwrite.cjs index 2f819f8..d3eba81 100644 --- a/scripts/setup-appwrite.cjs +++ b/apps/appwrite/scripts/setup-appwrite.cjs @@ -19,7 +19,7 @@ const path = require('path'); function loadEnv() { const envPath = path.join(__dirname, '..', '.env'); if (!fs.existsSync(envPath)) { - console.error('❌ .env file not found. Copy .env.example to .env and fill in the values.'); + console.error('❌ .env file not found. Copy .env.example from the repo root to apps/appwrite/.env and fill in the values.'); process.exit(1); } const lines = fs.readFileSync(envPath, 'utf8').split('\n'); diff --git a/index.html b/apps/web/index.html similarity index 100% rename from index.html rename to apps/web/index.html diff --git a/apps/web/package.json b/apps/web/package.json new file mode 100644 index 0000000..cd6a9bd --- /dev/null +++ b/apps/web/package.json @@ -0,0 +1,34 @@ +{ + "name": "@budgetwise/web", + "private": true, + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "build:staging": "tsc && vite build --mode staging", + "build:preview": "tsc && vite build --mode preview", + "preview": "vite preview" + }, + "dependencies": { + "appwrite": "^16.0.0", + "date-fns": "^3.3.1", + "lucide-react": "^0.344.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^6.22.0", + "zustand": "^4.5.0" + }, + "devDependencies": { + "@types/react": "^18.3.1", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.2.1", + "autoprefixer": "^10.4.17", + "postcss": "^8.4.35", + "tailwindcss": "^3.4.1", + "typescript": "^5.4.0", + "vite": "^5.1.0", + "vite-plugin-pwa": "^0.19.1", + "workbox-window": "^7.0.0" + } +} diff --git a/postcss.config.js b/apps/web/postcss.config.js similarity index 100% rename from postcss.config.js rename to apps/web/postcss.config.js diff --git a/public/icon.svg b/apps/web/public/icon.svg similarity index 100% rename from public/icon.svg rename to apps/web/public/icon.svg diff --git a/src/App.tsx b/apps/web/src/App.tsx similarity index 100% rename from src/App.tsx rename to apps/web/src/App.tsx diff --git a/src/appwrite/auth.ts b/apps/web/src/appwrite/auth.ts similarity index 100% rename from src/appwrite/auth.ts rename to apps/web/src/appwrite/auth.ts diff --git a/src/appwrite/config.ts b/apps/web/src/appwrite/config.ts similarity index 100% rename from src/appwrite/config.ts rename to apps/web/src/appwrite/config.ts diff --git a/src/appwrite/db.ts b/apps/web/src/appwrite/db.ts similarity index 100% rename from src/appwrite/db.ts rename to apps/web/src/appwrite/db.ts diff --git a/src/components/layout/BottomNav.tsx b/apps/web/src/components/layout/BottomNav.tsx similarity index 100% rename from src/components/layout/BottomNav.tsx rename to apps/web/src/components/layout/BottomNav.tsx diff --git a/src/components/layout/Header.tsx b/apps/web/src/components/layout/Header.tsx similarity index 100% rename from src/components/layout/Header.tsx rename to apps/web/src/components/layout/Header.tsx diff --git a/src/components/layout/Layout.tsx b/apps/web/src/components/layout/Layout.tsx similarity index 100% rename from src/components/layout/Layout.tsx rename to apps/web/src/components/layout/Layout.tsx diff --git a/src/components/ui/Badge.tsx b/apps/web/src/components/ui/Badge.tsx similarity index 100% rename from src/components/ui/Badge.tsx rename to apps/web/src/components/ui/Badge.tsx diff --git a/src/components/ui/Button.tsx b/apps/web/src/components/ui/Button.tsx similarity index 100% rename from src/components/ui/Button.tsx rename to apps/web/src/components/ui/Button.tsx diff --git a/src/components/ui/Card.tsx b/apps/web/src/components/ui/Card.tsx similarity index 100% rename from src/components/ui/Card.tsx rename to apps/web/src/components/ui/Card.tsx diff --git a/src/components/ui/Input.tsx b/apps/web/src/components/ui/Input.tsx similarity index 100% rename from src/components/ui/Input.tsx rename to apps/web/src/components/ui/Input.tsx diff --git a/src/components/ui/Modal.tsx b/apps/web/src/components/ui/Modal.tsx similarity index 100% rename from src/components/ui/Modal.tsx rename to apps/web/src/components/ui/Modal.tsx diff --git a/src/index.css b/apps/web/src/index.css similarity index 100% rename from src/index.css rename to apps/web/src/index.css diff --git a/src/lib/calculations.ts b/apps/web/src/lib/calculations.ts similarity index 100% rename from src/lib/calculations.ts rename to apps/web/src/lib/calculations.ts diff --git a/src/lib/utils.ts b/apps/web/src/lib/utils.ts similarity index 100% rename from src/lib/utils.ts rename to apps/web/src/lib/utils.ts diff --git a/src/main.tsx b/apps/web/src/main.tsx similarity index 100% rename from src/main.tsx rename to apps/web/src/main.tsx diff --git a/src/pages/Auth.tsx b/apps/web/src/pages/Auth.tsx similarity index 100% rename from src/pages/Auth.tsx rename to apps/web/src/pages/Auth.tsx diff --git a/src/pages/BalanceSheet.tsx b/apps/web/src/pages/BalanceSheet.tsx similarity index 100% rename from src/pages/BalanceSheet.tsx rename to apps/web/src/pages/BalanceSheet.tsx diff --git a/src/pages/BucketDetail.tsx b/apps/web/src/pages/BucketDetail.tsx similarity index 100% rename from src/pages/BucketDetail.tsx rename to apps/web/src/pages/BucketDetail.tsx diff --git a/src/pages/Buckets.tsx b/apps/web/src/pages/Buckets.tsx similarity index 100% rename from src/pages/Buckets.tsx rename to apps/web/src/pages/Buckets.tsx diff --git a/src/pages/Dashboard.tsx b/apps/web/src/pages/Dashboard.tsx similarity index 100% rename from src/pages/Dashboard.tsx rename to apps/web/src/pages/Dashboard.tsx diff --git a/src/pages/Debts.tsx b/apps/web/src/pages/Debts.tsx similarity index 100% rename from src/pages/Debts.tsx rename to apps/web/src/pages/Debts.tsx diff --git a/src/pages/LoanCalculator.tsx b/apps/web/src/pages/LoanCalculator.tsx similarity index 100% rename from src/pages/LoanCalculator.tsx rename to apps/web/src/pages/LoanCalculator.tsx diff --git a/src/pages/More.tsx b/apps/web/src/pages/More.tsx similarity index 100% rename from src/pages/More.tsx rename to apps/web/src/pages/More.tsx diff --git a/src/pages/Settings.tsx b/apps/web/src/pages/Settings.tsx similarity index 100% rename from src/pages/Settings.tsx rename to apps/web/src/pages/Settings.tsx diff --git a/src/store/index.ts b/apps/web/src/store/index.ts similarity index 100% rename from src/store/index.ts rename to apps/web/src/store/index.ts diff --git a/src/types/index.ts b/apps/web/src/types/index.ts similarity index 100% rename from src/types/index.ts rename to apps/web/src/types/index.ts diff --git a/tailwind.config.js b/apps/web/tailwind.config.js similarity index 100% rename from tailwind.config.js rename to apps/web/tailwind.config.js diff --git a/tsconfig.json b/apps/web/tsconfig.json similarity index 100% rename from tsconfig.json rename to apps/web/tsconfig.json diff --git a/tsconfig.node.json b/apps/web/tsconfig.node.json similarity index 100% rename from tsconfig.node.json rename to apps/web/tsconfig.node.json diff --git a/vite.config.ts b/apps/web/vite.config.ts similarity index 100% rename from vite.config.ts rename to apps/web/vite.config.ts diff --git a/package-lock.json b/package-lock.json index 2181d56..1e362c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,31 @@ { - "name": "budgetwise", + "name": "budget-app", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "budgetwise", + "name": "budget-app", + "version": "1.0.0", + "workspaces": [ + "apps/*" + ], + "devDependencies": { + "turbo": "^2.3.3" + }, + "engines": { + "node": ">=20" + } + }, + "apps/appwrite": { + "name": "@budgetwise/appwrite", + "version": "1.0.0", + "devDependencies": { + "node-appwrite": "^12.0.0" + } + }, + "apps/web": { + "name": "@budgetwise/web", "version": "1.0.0", "dependencies": { "appwrite": "^16.0.0", @@ -21,7 +41,6 @@ "@types/react-dom": "^18.3.1", "@vitejs/plugin-react": "^4.2.1", "autoprefixer": "^10.4.17", - "node-appwrite": "^12.0.0", "postcss": "^8.4.35", "tailwindcss": "^3.4.1", "typescript": "^5.4.0", @@ -92,6 +111,7 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1617,6 +1637,14 @@ "node": ">=6.9.0" } }, + "node_modules/@budgetwise/appwrite": { + "resolved": "apps/appwrite", + "link": true + }, + "node_modules/@budgetwise/web": { + "resolved": "apps/web", + "link": true + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -2655,6 +2683,7 @@ "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -2724,6 +2753,7 @@ "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -2836,9 +2866,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.24", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.24.tgz", - "integrity": "sha512-uHZg7N9ULTVbutaIsDRoUkoS8/h3bdsmVJYZ5l3wv8Cp/6UIIoRDm90hZ+BwxUj/hGBEzLxdHNSKuFpn8WOyZw==", + "version": "10.4.27", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz", + "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==", "dev": true, "funding": [ { @@ -2857,7 +2887,7 @@ "license": "MIT", "dependencies": { "browserslist": "^4.28.1", - "caniuse-lite": "^1.0.30001766", + "caniuse-lite": "^1.0.30001774", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" @@ -2967,9 +2997,9 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", - "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "dev": true, "license": "MIT", "dependencies": { @@ -3012,6 +3042,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -3094,9 +3125,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001772", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001772.tgz", - "integrity": "sha512-mIwLZICj+ntVTw4BT2zfp+yu/AqV6GMKfJVJMx3MwPxs+uk/uj2GLl2dH8LQbjiLDX66amCga5nKFyDgRR43kg==", + "version": "1.0.30001774", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001774.tgz", + "integrity": "sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA==", "dev": true, "funding": [ { @@ -3692,9 +3723,9 @@ } }, "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3719,9 +3750,9 @@ } }, "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.7.tgz", - "integrity": "sha512-FjiwU9HaHW6YB3H4a1sFudnv93lvydNjz2lmyUXR6IwKhGI+bgL3SOZrBGn6kvvX2pJvhEkGSGjyTHN47O4rqA==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", "dev": true, "license": "ISC", "dependencies": { @@ -4627,6 +4658,7 @@ "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "dev": true, "license": "MIT", + "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -4827,9 +4859,9 @@ } }, "node_modules/minimatch": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", - "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -5120,6 +5152,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -5322,6 +5355,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -5334,6 +5368,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -6184,6 +6219,7 @@ "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", @@ -6268,6 +6304,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -6305,6 +6342,108 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/turbo": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/turbo/-/turbo-2.8.12.tgz", + "integrity": "sha512-auUAMLmi0eJhxDhQrxzvuhfEbICnVt0CTiYQYY8WyRJ5nwCDZxD0JG8bCSxT4nusI2CwJzmZAay5BfF6LmK7Hw==", + "dev": true, + "license": "MIT", + "bin": { + "turbo": "bin/turbo" + }, + "optionalDependencies": { + "turbo-darwin-64": "2.8.12", + "turbo-darwin-arm64": "2.8.12", + "turbo-linux-64": "2.8.12", + "turbo-linux-arm64": "2.8.12", + "turbo-windows-64": "2.8.12", + "turbo-windows-arm64": "2.8.12" + } + }, + "node_modules/turbo-darwin-64": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/turbo-darwin-64/-/turbo-darwin-64-2.8.12.tgz", + "integrity": "sha512-EiHJmW2MeQQx+21x8hjMHw/uPhXt9PIxvDrxzOtyVwrXzL0tQmsxtO4qHf2l7uA+K6PUJ4+TjY1MHZDuCvWXrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/turbo-darwin-arm64": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/turbo-darwin-arm64/-/turbo-darwin-arm64-2.8.12.tgz", + "integrity": "sha512-cbqqGN0vd7ly2TeuaM8k9AK9u1CABO4kBA5KPSqovTiLL3sORccn/mZzJSbvQf0EsYRfU34MgW5FotfwW3kx8Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/turbo-linux-64": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/turbo-linux-64/-/turbo-linux-64-2.8.12.tgz", + "integrity": "sha512-jXKw9j4r4q6s0goSXuKI3aKbQK2qiNeP25lGGEnq018TM6SWRW1CCpPMxyG91aCKrub7wDm/K45sGNT4ZFBcFQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/turbo-linux-arm64": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/turbo-linux-arm64/-/turbo-linux-arm64-2.8.12.tgz", + "integrity": "sha512-BRJCMdyXjyBoL0GYpvj9d2WNfMHwc3tKmJG5ATn2Efvil9LsiOsd/93/NxDqW0jACtHFNVOPnd/CBwXRPiRbwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/turbo-windows-64": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/turbo-windows-64/-/turbo-windows-64-2.8.12.tgz", + "integrity": "sha512-vyFOlpFFzQFkikvSVhVkESEfzIopgs2J7J1rYvtSwSHQ4zmHxkC95Q8Kjkus8gg+8X2mZyP1GS5jirmaypGiPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/turbo-windows-arm64": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/turbo-windows-arm64/-/turbo-windows-arm64-2.8.12.tgz", + "integrity": "sha512-9nRnlw5DF0LkJClkIws1evaIF36dmmMEO84J5Uj4oQ8C0QTHwlH7DNe5Kq2Jdmu8GXESCNDNuUYG8Cx6W/vm3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/type-fest": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", @@ -6573,6 +6712,7 @@ "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -6941,6 +7081,7 @@ "integrity": "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "rollup": "dist/bin/rollup" }, diff --git a/package.json b/package.json index 2b1c293..7bb616d 100644 --- a/package.json +++ b/package.json @@ -1,34 +1,18 @@ { - "name": "budgetwise", + "name": "budget-app", "private": true, "version": "1.0.0", - "type": "module", + "workspaces": [ + "apps/*" + ], "scripts": { - "dev": "vite", - "build": "tsc && vite build", - "preview": "vite preview", - "setup:appwrite": "node scripts/setup-appwrite.cjs" - }, - "dependencies": { - "appwrite": "^16.0.0", - "date-fns": "^3.3.1", - "lucide-react": "^0.344.0", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-router-dom": "^6.22.0", - "zustand": "^4.5.0" + "build": "turbo run build", + "dev": "turbo run dev" }, "devDependencies": { - "@types/react": "^18.3.1", - "@types/react-dom": "^18.3.1", - "@vitejs/plugin-react": "^4.2.1", - "autoprefixer": "^10.4.17", - "node-appwrite": "^12.0.0", - "postcss": "^8.4.35", - "tailwindcss": "^3.4.1", - "typescript": "^5.4.0", - "vite": "^5.1.0", - "vite-plugin-pwa": "^0.19.1", - "workbox-window": "^7.0.0" + "turbo": "^2.3.3" + }, + "engines": { + "node": ">=20" } } diff --git a/turbo.json b/turbo.json new file mode 100644 index 0000000..ac97982 --- /dev/null +++ b/turbo.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://turbo.build/schema.json", + "tasks": { + "build": { + "inputs": [ + "src/**", + "public/**", + "index.html", + "*.config.*", + "tsconfig*.json", + "$VITE_APP_URL", + "$VITE_APPWRITE_ENDPOINT", + "$VITE_APPWRITE_PROJECT_ID" + ], + "outputs": ["dist/**"] + }, + "deploy": { + "cache": false, + "outputs": [] + }, + "dev": { + "persistent": true, + "cache": false + } + } +}