diff --git a/.cursorignore b/.cursorignore new file mode 100644 index 0000000..d2c9860 --- /dev/null +++ b/.cursorignore @@ -0,0 +1,16 @@ +# Hide generated/local files from Cursor (mirrors .gitignore) +node_modules/ +.pnpm-store/ +dist/ +build/ +.tanstack/ +*.tsbuildinfo +.DS_Store +.env +.env.* +.terraform/ +*.tfstate +*.tfstate.* +*.tfplan +**/*.auto.tfvars +**/terraform.local.tfvars diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ff718f1 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,41 @@ +name: Verify build & types + +on: + push: + branches: + - main + pull_request: + branches: + - main + +concurrency: + group: verify-build-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + verify: + name: Lint & build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + version: 9.15.9 + + - uses: actions/setup-node@v4 + with: + node-version: "20" + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Typecheck + run: pnpm run lint + + - name: Build + run: pnpm run build diff --git a/.gitignore b/.gitignore index 2525243..7c76bfa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,18 @@ # dependencies -node_modules -.pnpm-store +node_modules/ +.pnpm-store/ # build -dist -build +dist/ +build/ + +# dev caches +.tanstack/ +*.tsbuildinfo # misc .DS_Store +Thumbs.db *.local .env .env.* @@ -16,18 +21,33 @@ build *.log npm-debug.log* pnpm-debug.log* +yarn-debug.log* +yarn-error.log* -# editor +# editor / IDE .vscode/* !.vscode/extensions.json +!.vscode/settings.json +.idea/ +*.swp +*.swo -# typescript -*.tsbuildinfo +# terraform (local state + secrets — never commit) +.terraform/ +*.tfstate +*.tfstate.* +*.tfplan +*.tfplan.* +crash.log +crash.*.log +override.tf +override.tf.json +*_override.tf +*_override.tf.json +.terraformrc +terraform.rc +**/terraform.local.tfvars +**/*.auto.tfvars -# terraform (local overrides only — never commit secrets) -infra/**/*.local.tfvars -infra/**/*.auto.tfvars -infra/**/.terraform/ -infra/**/terraform.tfstate* -infra/**/tfplan -infra/**/destroy.tfplan +# deployment platform configs (not used — App Platform via infra/) +vercel.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a1aa3e4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "files.exclude": { + "**/node_modules": true, + "**/.pnpm-store": true, + "**/dist": true, + "**/.tanstack": true, + "**/.terraform": true, + "**/*.tfstate": true, + "**/*.tfstate.*": true, + "**/*.auto.tfvars": true + } +} diff --git a/infra/README.md b/infra/README.md index b899c8e..2f780c1 100644 --- a/infra/README.md +++ b/infra/README.md @@ -14,7 +14,7 @@ environments/ modules/ cloudflare/ # CF zone, zone settings, lightweight WAF rulesets cloudflare-dns/ # CF apex/www CNAME records → App Platform - dns/ # DigitalOcean DNS (apex zone only); disabled once CF DNS is active + dns/ # (optional) DO apex zone — unused; App Platform uses an existing DO DNS zone frontend/ # App Platform static site (Vite build → dist/) ``` @@ -65,7 +65,13 @@ terraform init terraform apply ``` -With Cloudflare flags all `false`, the first apply creates the DO DNS zone + App Platform app + custom domains. +With Cloudflare flags all `false`, the first apply creates the App Platform app + custom domains. The apex domain must already exist under **DigitalOcean → Networking → Domains** (add it in the console if needed). + +### Troubleshooting: `domain 'nmandiveyi.com': name already exists` + +The DO DNS zone already exists in your account — skip creating it in Terraform (the prod root module no longer manages `digitalocean_domain`). Run `terraform apply` again. + +If App Platform still fails to attach the domain, confirm `nmandiveyi.com` appears under **Networking → Domains** in the DO dashboard. Add it there manually if missing, then re-apply. Verify the default ingress URL serves the built site: diff --git a/infra/environments/prod/main.tf b/infra/environments/prod/main.tf index 2003bde..bf4898f 100644 --- a/infra/environments/prod/main.tf +++ b/infra/environments/prod/main.tf @@ -13,13 +13,6 @@ locals { ) } -module "dns" { - source = "../../modules/dns" - count = var.cloudflare_dns_enabled ? 0 : 1 - - domain = local.root_domain -} - module "frontend" { source = "../../modules/frontend" @@ -32,8 +25,6 @@ module "frontend" { www_domain = local.www_domain manage_dns_zone = !var.cloudflare_dns_enabled - - depends_on = [module.dns] } module "cloudflare" { diff --git a/infra/modules/frontend/main.tf b/infra/modules/frontend/main.tf index ace5266..d56dc92 100644 --- a/infra/modules/frontend/main.tf +++ b/infra/modules/frontend/main.tf @@ -4,8 +4,13 @@ resource "digitalocean_app" "this" { region = var.region static_site { - name = "web" - source_dir = var.source_dir + name = "web" + source_dir = var.source_dir + environment_slug = "node-js" + build_command = var.build_command + output_dir = var.output_dir + index_document = "index.html" + catchall_document = "index.html" github { repo = var.github_repo @@ -13,10 +18,17 @@ resource "digitalocean_app" "this" { deploy_on_push = true } - build_command = var.build_command - output_dir = var.output_dir - index_document = "index.html" - catchall_document = "index.html" + env { + key = "PNPM_SKIP_PRUNING" + value = "true" + scope = "BUILD_TIME" + } + + env { + key = "PNPM_VERSION" + value = "9.15.9" + scope = "BUILD_TIME" + } } domain { diff --git a/package.json b/package.json index 09ce2fb..97c862f 100644 --- a/package.json +++ b/package.json @@ -38,5 +38,10 @@ }, "pnpm": { "onlyBuiltDependencies": ["esbuild"] + }, + "packageManager": "pnpm@9.15.9", + "engines": { + "node": "20.x", + "pnpm": "9.15.9" } } diff --git a/vercel.json b/vercel.json deleted file mode 100644 index 0f32683..0000000 --- a/vercel.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }] -}