From 8ec4e5e15adb495818a46e77bf63a4b58068c882 Mon Sep 17 00:00:00 2001 From: oratis Date: Sun, 10 May 2026 23:23:45 +0800 Subject: [PATCH 1/6] chore(deploy): make cloudbuild safe + add ignore files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - cloudbuild.yaml deploy step now uses --update-env-vars / --update-secrets instead of --set-env-vars. The previous version would have wiped the 6 production secret bindings (DATABASE_URL, NEXTAUTH_SECRET, GOOGLE/APPLE OAuth, RESEND, CRON) and replaced them with literal "your-secret-here" placeholders on every deploy — potential data/auth outage on any trigger. - Tag images by $BUILD_ID (immutable, unique per build) plus :latest, so rollback is one gcloud run deploy --image=...:$BUILD_ID away. - Mirror current Cloud Run config: cpu=1, memory=512Mi, max-instances=20. - Push happens via images: block (no separate push step). - Add .dockerignore to keep local node_modules / .next / .env / secrets out of the build context — avoids macOS native binaries clobbering the Linux ones produced by the deps stage. - Add .gcloudignore so gcloud builds submit uploads ~3 MiB instead of 1 GB. Co-Authored-By: Claude Opus 4.7 (1M context) --- .dockerignore | 41 +++++++++++++++++++++++++ .gcloudignore | 40 ++++++++++++++++++++++++ cloudbuild.yaml | 81 +++++++++++++++++++++++++++++-------------------- 3 files changed, 129 insertions(+), 33 deletions(-) create mode 100644 .dockerignore create mode 100644 .gcloudignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..f1a179866 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,41 @@ +# Build artifacts and dependencies — installed cleanly inside the image. +# Including local node_modules (especially macOS-built native binaries) would +# overwrite the linux ones from the deps stage and break the runtime. +node_modules +.next +.next-build-cache + +# Source control + editor + OS noise +.git +.gitignore +.gitattributes +.vscode +.idea +.DS_Store +**/.DS_Store + +# Local databases and env (never bake credentials into the image) +prisma/dev.db +prisma/test.db +*.db +.env +.env.local +.env.*.local + +# Project meta — not needed at runtime +README.md +CHANGELOG.md +LICENSE +docs +e2e +scripts/local +.github + +# Logs and TS incremental cache +*.log +tsconfig.tsbuildinfo + +# Cloud Build / deployment configs — they live outside the image +cloudbuild.yaml +.gcloudignore +.dockerignore diff --git a/.gcloudignore b/.gcloudignore new file mode 100644 index 000000000..6cd74530e --- /dev/null +++ b/.gcloudignore @@ -0,0 +1,40 @@ +# Files NOT uploaded to Cloud Build's source bundle. +# Without this file, gcloud auto-derives one from .gitignore — BUT explicit +# is better, especially for safety-critical exclusions like .env. +# +# Reference: https://cloud.google.com/sdk/gcloud/reference/topic/gcloudignore + +# Build artifacts (rebuilt inside the container; don't ship local copies) +node_modules +.next +.next-build-cache + +# Local databases (have prod credentials in URL; never upload) +prisma/dev.db +prisma/test.db +*.db + +# Secrets — defense in depth even though they shouldn't exist in the repo +.env +.env.local +.env.*.local +*.pem +*.key + +# Source control + editor + OS noise +.git +.gitignore +.gitattributes +.vscode +.idea +.DS_Store +**/.DS_Store + +# Logs / caches +*.log +tsconfig.tsbuildinfo + +# Big stuff that doesn't belong in a build context +docs +e2e +scripts/local diff --git a/cloudbuild.yaml b/cloudbuild.yaml index 81a714dab..0ddf20287 100644 --- a/cloudbuild.yaml +++ b/cloudbuild.yaml @@ -1,43 +1,58 @@ -steps: - # Build the container image - - name: 'gcr.io/cloud-builders/docker' - args: - - 'build' - - '-t' - - 'us-central1-docker.pkg.dev/$PROJECT_ID/takoapi-repo/takoapi:latest' - - '.' +# Cloud Build pipeline for takoapi (Cloud Run, us-central1). +# +# Build → deploy. The deploy step uses --update-env-vars / --update-secrets +# (NOT --set-env-vars), so any existing bindings on the Cloud Run service — +# secrets, traffic splits, manual env tweaks — are preserved. Running this +# pipeline against the live service never wipes production credentials. +# +# Manual trigger: +# gcloud builds submit --project=takoapi-491505 --config=cloudbuild.yaml . +# +# Build-only (rare): comment out the "deploy" step. Both image tags still +# get pushed via the `images:` block at the bottom. +# +# Rollback: each build pushes us-central1-docker.pkg.dev/.../takoapi:$BUILD_ID, +# which is immutable. To roll back, redeploy the desired BUILD_ID: +# gcloud run deploy takoapi --region=us-central1 \ +# --image=us-central1-docker.pkg.dev/takoapi-491505/takoapi-repo/takoapi: - # Push the container image to Artifact Registry - - name: 'gcr.io/cloud-builders/docker' +steps: + - id: build + name: gcr.io/cloud-builders/docker args: - - 'push' - - 'us-central1-docker.pkg.dev/$PROJECT_ID/takoapi-repo/takoapi:latest' + - build + - --tag=us-central1-docker.pkg.dev/$PROJECT_ID/takoapi-repo/takoapi:$BUILD_ID + - --tag=us-central1-docker.pkg.dev/$PROJECT_ID/takoapi-repo/takoapi:latest + - . - # Deploy container image to Cloud Run - - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk' + - id: deploy + name: gcr.io/google.com/cloudsdktool/cloud-sdk + waitFor: [build] entrypoint: gcloud args: - - 'run' - - 'deploy' - - 'takoapi' - - '--image' - - 'us-central1-docker.pkg.dev/$PROJECT_ID/takoapi-repo/takoapi:latest' - - '--region' - - 'us-central1' - - '--platform' - - 'managed' - - '--allow-unauthenticated' - - '--port' - - '8080' - - '--memory' - - '512Mi' - - '--add-cloudsql-instances' - - 'takoapi-491505:us-central1:takoapi-db' - - '--set-env-vars' - - 'NODE_ENV=production,AUTH_TRUST_HOST=true,DATABASE_URL=postgresql://USER:PASSWORD@localhost/takoapi?host=/cloudsql/PROJECT:REGION:INSTANCE,NEXTAUTH_URL=https://takoapi.com,NEXTAUTH_SECRET=your-secret-here,GOOGLE_CLIENT_ID=your-google-client-id,GOOGLE_CLIENT_SECRET=your-google-client-secret,APPLE_CLIENT_ID=your-apple-client-id,APPLE_CLIENT_SECRET=your-apple-client-secret' + - run + - deploy + - takoapi + - --region=us-central1 + - --image=us-central1-docker.pkg.dev/$PROJECT_ID/takoapi-repo/takoapi:$BUILD_ID + - --platform=managed + - --allow-unauthenticated + - --port=8080 + - --cpu=1 + - --memory=512Mi + - --max-instances=20 + - --add-cloudsql-instances=$PROJECT_ID:us-central1:takoapi-db + # --update-* (not --set-*): only the listed keys are touched. + # Secrets are bound by reference; their values never appear in this file + # or in build logs. Manage values via: + # gcloud secrets versions add --data-file=- + - --update-env-vars=NODE_ENV=production,AUTH_TRUST_HOST=true,NEXTAUTH_URL=https://takoapi.com,GOOGLE_CLIENT_ID=429522911261-t3s9v8av3n4saed95u4vkij5oqlfjoti.apps.googleusercontent.com,APPLE_CLIENT_ID=com.takoapi.auth + - --update-secrets=DATABASE_URL=tako-database-url:latest,NEXTAUTH_SECRET=tako-nextauth-secret:latest,GOOGLE_CLIENT_SECRET=tako-google-client-secret:latest,APPLE_CLIENT_SECRET=tako-apple-client-secret:latest,RESEND_API_KEY=tako-resend-api-key:latest,CRON_SECRET=tako-cron-secret:latest +# Both tags are pushed automatically once all steps succeed. images: - - 'us-central1-docker.pkg.dev/$PROJECT_ID/takoapi-repo/takoapi:latest' + - us-central1-docker.pkg.dev/$PROJECT_ID/takoapi-repo/takoapi:$BUILD_ID + - us-central1-docker.pkg.dev/$PROJECT_ID/takoapi-repo/takoapi:latest options: logging: CLOUD_LOGGING_ONLY From 5dae47a7da9e9137044043784c8b1a44f698b827 Mon Sep 17 00:00:00 2001 From: oratis Date: Sun, 10 May 2026 23:24:30 +0800 Subject: [PATCH 2/6] chore: add tsx as devDependency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lets local prisma seed / one-off TypeScript scripts run via npx tsx without a separate ts-node toolchain. devDependency only — does not affect the runtime image (Next.js standalone build strips dev deps). Co-Authored-By: Claude Opus 4.7 (1M context) --- package-lock.json | 524 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 1 + 2 files changed, 523 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 739eb9bc9..40a4b9e4e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,8 @@ "prisma": "^6.19.2", "react": "19.2.4", "react-dom": "19.2.4", - "tailwind-merge": "^3.5.0" + "tailwind-merge": "^3.5.0", + "zod": "^4.3.6" }, "devDependencies": { "@tailwindcss/postcss": "^4", @@ -33,6 +34,7 @@ "eslint-config-next": "16.2.1", "puppeteer-core": "^24.40.0", "tailwindcss": "^4", + "tsx": "^4.21.0", "typescript": "^5" } }, @@ -364,6 +366,448 @@ "tslib": "^2.4.0" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", @@ -3670,6 +4114,48 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -4378,6 +4864,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -7728,6 +8229,26 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -8210,7 +8731,6 @@ "version": "4.3.6", "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", - "dev": true, "license": "MIT", "peer": true, "funding": { diff --git a/package.json b/package.json index a9214a59a..78da0346d 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "eslint-config-next": "16.2.1", "puppeteer-core": "^24.40.0", "tailwindcss": "^4", + "tsx": "^4.21.0", "typescript": "^5" } } From 050c43b5022f02c83e9ed20d69cf30f76cf81e60 Mon Sep 17 00:00:00 2001 From: oratis Date: Sat, 5 Sep 2026 22:19:30 +0800 Subject: [PATCH 3/6] docs: UX + technical review with phased optimization plans (2026-09-05) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Full-project review against origin/main b868319c: UX audit (IA, home, discovery, detail pages, funnels, a11y/i18n, copy checklist, U0–U3 plan) and technical audit (P0 security/ops incl. leaked DB password and Apple secret expiry, correctness defects not covered by PR #52, performance, engineering, T0–T3 plan). Indexed from docs/README.md. Co-Authored-By: Claude Fable 5.1 --- docs/07-ux-review-2026-09-05.md | 239 ++++++++++++++++++++++++++++++ docs/08-tech-review-2026-09-05.md | 227 ++++++++++++++++++++++++++++ docs/README.md | 7 + 3 files changed, 473 insertions(+) create mode 100644 docs/07-ux-review-2026-09-05.md create mode 100644 docs/08-tech-review-2026-09-05.md diff --git a/docs/07-ux-review-2026-09-05.md b/docs/07-ux-review-2026-09-05.md new file mode 100644 index 000000000..66b757372 --- /dev/null +++ b/docs/07-ux-review-2026-09-05.md @@ -0,0 +1,239 @@ +# UX 全面 Review 与优化计划(2026-09-05) + +> 范围:takoapi.com 全部面向用户的页面(首页 / 市集 / 详情 / 技能 / 榜单 / 安装 / 博客 / 场景 / 登录注册 / 开发者控制台 / 提交流程 / 后台)+ 全站文案(`messages/en.json` 618 键,抽样 zh / ja / de)+ 邮件模板。 +> 方法:逐文件走读 `src/app/[locale]/**`、`src/components/**`;线上实测(curl 计时、HTML 结构、公开 API 返回);对照 `docs/agent-marketplace/` 已拍板的产品决策(D1–D8)。 +> 配套技术审查见 [08-tech-review-2026-09-05.md](08-tech-review-2026-09-05.md)。 + +--- + +## 0. 一句话结论 + +产品定位已经从「OpenClaw skills 市集」转向「One API to access all agents」,**但用户看到的仍是两个产品缝在一起**:导航、搜索、首页版块、登录文案、邮件都在两种叙事之间来回切换;而对已拍板的「开发者优先」(D4)这条主线,从首页到详情页到控制台,**缺一条清晰的「看到 → 拿 key → 30 秒调通」路径**。此外有一批影响信任的细节(首页精选中挂着探活为 down 的 agent、详情页仍写着「网关 Phase 2 才上线」、Skill 详情页纯客户端渲染),修复成本低但优先级高。 + +线上基线(2026-09-05 实测): + +| 指标 | 数值 | +|---|---| +| 目录规模(首页 hero 展示) | 52 个 HOSTED agent · 1,558 个开源项目 · 5,385 个 skill | +| 首页 HTML | 271 KB,68 个 `