From 04eb4fad08a0203d1c3f6deea8646f9d57c7e644 Mon Sep 17 00:00:00 2001 From: DustyWalker Date: Mon, 4 Aug 2025 21:01:11 +0200 Subject: [PATCH 01/38] plan --- plan-for-devs.md | 114 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 plan-for-devs.md diff --git a/plan-for-devs.md b/plan-for-devs.md new file mode 100644 index 0000000..9e6fb75 --- /dev/null +++ b/plan-for-devs.md @@ -0,0 +1,114 @@ +Below is a **road-map of concrete tasks** that will take your three-developer team from today’s scaffold to a production-ready “AI Bulk Image Renamer” SaaS. The work is organised in phases so that each developer always has an autonomous slice to own, while touch-points (API contracts, queues, WebSockets) stay thin and testable. Citations are woven in wherever external best-practice or spec language underpins a task. + +--- + +## Phase 0 – 1 recap (done last week) + +Skeleton repo, Docker Compose, OAuth stub, queue stub, drag-and-drop, WebSocket progress, and dummy review table were delivered. +*Specification refs §18, 26-32, 73-77* + +--- + +## Phase 2 – Minimum Viable Rename (Weeks 2-4) + +### Dev A – Backend / API + +* **Harden Google OAuth flow**: add state + PKCE, token verification, refresh‐token rotation ([Google for Developers][1], [DEV Community][2]) +* **Finish REST endpoints** `/api/batch`, `/status`, `/zip`, `/keywords/enhance`, wiring them to DB via Prisma. +* **Implement atomic quota decrement** when each image job is en-queued §56-57 +* **Create cron job** using node-crond to reset quotas at UTC 00:00 monthly §58 +* **Write integration tests** (Vitest) for the above. + +### Dev B – Worker & Vision + +* **Swap dummy worker for real BullMQ worker**; configure 5-attempt exponential back-off §65 +* **Integrate ClamAV scan** for each file before Vision call ([Transloadit][3], [DEV Community][4]) (fulfils §62). +* **Call Azure AI Vision v4 tagging API** and persist tags ≥0.40 confidence §38-41 +* **Implement filename generation algorithm** satisfying §43-48, including stop-word removal and collision suffixes . +* **Stream progress via WebSocket** (`images/{id}/status`) when each step finishes §77 . + +### Dev C – Front-end / UX + +* **Keyword chip-list & “Enhance” button** with optimistic UI §33-37 . +* **Review Table** with inline-edit, regenerate-name, and shimmer placeholders §49-53, 70 . +* **Download ZIP** trigger once all rows DONE (§54-55) and show quota bar (§67). +* **Accessibility pass** (keyboard navigation, aria labels) towards Lighthouse ≥90 §59, 85 . + +--- + +## Phase 3 – Paid Plans & Quota Enforcement (Weeks 5-8) + +### Dev A + +* **Stripe Billing integration**: Checkout, customer portal, webhooks updating `plan` & `quota_remaining` ([Stripe][5], [Stripe][6]). +* **Usage-meter rows** in `payments` table; expose `/api/usage` for dashboard. +* **Automated downgrade** to Basic on cancellation (§25). + +### Dev B + +* **Batch ZIP assembly** in worker using archiver; preserve EXIF (§54-55). +* **S3-compatible storage move to MinIO production cluster** with signed URLs (spec §30 + MinIO docs) ([min.io][7]). +* **Queue metrics** to Prometheus histogram (§84). + +### Dev C + +* **Billing page & upgrade modal** (§22-24, 71). +* **Plan badge & quota reset animation** after webhook arrives via SSE. +* **Error states UI** (e.g., virus detected, vision failure). + +--- + +## Phase 4 – Production Hardening (Weeks 9-12) + +*All Devs participate; primary owner noted* + +| Owner | Task | +| ------- | --------------------------------------------------------------------------------------------------------------------------------------- | +| **A** | **OpenTelemetry tracing + Sentry error capture** (§82-83). | +| **B** | **Load testing and queue autoscale rules**; follow BullMQ robustness guidance ([Medium][8]). | +| **C** | **Lighthouse & a11y polish** to hit 90/90 (§59, 85). | +| **A** | **Prisma `migrate deploy` pipeline** and expand-and-contract strategy ([GitHub][9], [wasp.sh][10]). | +| **B** | **Security review**: ClamAV update job, Redis auth, secrets manager (§61-63). | +| **All** | **CI/CD**: ESLint, unit + e2e, Docker multi-stage build < 300 MB (§87) ([Docker Documentation][11], [blacksmith.sh][12], [GitHub][13]). | + +--- + +## Phase 5 – Growth & Ecosystem (Quarter 2) + +* **CLI uploader & public API token auth** (Dev A). +* **LLM-powered keyword enhancement quality tuning** (Dev B). +* **Multi-language UI groundwork** (Dev C, extracting i18n JSON; spec §86). +* **Admin analytics dashboard** (collab). +* **Optional: marketplace listing & SEO content marketing** (team). + +--- + +## Cross-cutting, continuous duties + +* **Code-review rotation**: each PR must be reviewed by one dev outside its domain. +* **Weekly demo** to keep UI/APIcontracts honest. +* **Error budget & on-call**: 24 h chat response for prod issues once paying users arrive. +* **Documentation**: keep `/docs/ARCHITECTURE.md` and API schema up-to-date. + +--- + +### Why this works + +*Each developer owns a vertical slice* so they can deliver value independently and learn the adjacent stack surface. *Shared DevOps, security, and reviews* prevent silos. The plan aligns strictly with the numbered requirements in your spec §10-90 while following industry best-practices for OAuth ([Google for Developers][1], [DEV Community][2]), queues ([docs.bullmq.io][14], [Medium][8]), database migrations ([GitHub][9], [wasp.sh][10]), malware scanning ([Transloadit][3], [DEV Community][4]), computer-vision tagging ([Microsoft Learn][15], [Microsoft Learn][16]), billing ([Stripe][5], [Stripe][6]), object storage ([min.io][7]), and container builds ([Docker Documentation][11], [GitHub][13]). Follow this sequence and the team will ship a monetisable, secure, and maintainable SaaS on schedule. + +[1]: https://developers.google.com/identity/protocols/oauth2/resources/best-practices?utm_source=chatgpt.com "Best Practices | Authorization Resources" +[2]: https://dev.to/hamzakhan/mastering-oauth-20-in-modern-web-applications-security-best-practices-for-2024-26ed?utm_source=chatgpt.com "🔒 Mastering OAuth 2.0 in Modern Web Applications ..." +[3]: https://transloadit.com/devtips/implementing-server-side-malware-scanning-with-clamav-in-node-js/?utm_source=chatgpt.com "Implementing server-side malware scanning with ClamAV ..." +[4]: https://dev.to/jfbloom22/how-to-virus-scan-file-users-upload-using-clamav-2i5d?utm_source=chatgpt.com "How to virus scan file users upload using ClamAV" +[5]: https://stripe.com/in/billing?utm_source=chatgpt.com "Stripe Billing | Recurring Payments & Subscription Solutions" +[6]: https://stripe.com/billing/usage-based-billing?utm_source=chatgpt.com "Stripe Usage-Based Billing Software" +[7]: https://min.io/docs/minio/container/index.html?utm_source=chatgpt.com "MinIO Object Storage for Container" +[8]: https://medium.com/%40karthiks05/how-we-built-a-robust-message-queue-using-bullmq-part-1-2d5ad1016958?utm_source=chatgpt.com "How We Built a Robust Message Queue Using BullMQ" +[9]: https://github.com/prisma/prisma/discussions/24571?utm_source=chatgpt.com "How should migration be done to Production? #24571" +[10]: https://wasp.sh/blog/2025/04/02/an-introduction-to-database-migrations?utm_source=chatgpt.com "A Gentle Introduction to Database Migrations in Prisma ..." +[11]: https://docs.docker.com/build/building/best-practices/?utm_source=chatgpt.com "Building best practices" +[12]: https://www.blacksmith.sh/blog/understanding-multi-stage-docker-builds?ref=geeknest.ru&utm_source=chatgpt.com "Understanding Multi-Stage Docker Builds" +[13]: https://github.com/goldbergyoni/nodebestpractices?utm_source=chatgpt.com "The Node.js best practices list (July 2024)" +[14]: https://docs.bullmq.io/guide/retrying-failing-jobs?utm_source=chatgpt.com "Retrying failing jobs" +[15]: https://learn.microsoft.com/en-us/azure/ai-services/computer-vision/whats-new?utm_source=chatgpt.com "What's new in Azure AI Vision?" +[16]: https://learn.microsoft.com/en-us/azure/ai-services/computer-vision/concept-tag-images-40?utm_source=chatgpt.com "Content tags - Image Analysis 4.0 - Azure AI services" From 8faf2c9dde22764a8e3ad0346ccbcc78cf2fca6e Mon Sep 17 00:00:00 2001 From: DustyWalker Date: Mon, 4 Aug 2025 21:04:50 +0200 Subject: [PATCH 02/38] new-plan --- plan-for-devs.md | 169 +++++++++++++++++++++++------------------------ 1 file changed, 84 insertions(+), 85 deletions(-) diff --git a/plan-for-devs.md b/plan-for-devs.md index 9e6fb75..c328a3b 100644 --- a/plan-for-devs.md +++ b/plan-for-devs.md @@ -1,114 +1,113 @@ -Below is a **road-map of concrete tasks** that will take your three-developer team from today’s scaffold to a production-ready “AI Bulk Image Renamer” SaaS. The work is organised in phases so that each developer always has an autonomous slice to own, while touch-points (API contracts, queues, WebSockets) stay thin and testable. Citations are woven in wherever external best-practice or spec language underpins a task. +**Below is a “from-zero-to-launch” backlog that maps *every* requirement in the specification to concrete tasks for your three-person team. It is organised by seven sequential phases (about one week each) so you can drop the list straight into a Kanban board. After the backlog you’ll find a compact milestone calendar. Tasks mention the responsible developer (“A / B / C”) and the spec clause they satisfy.** --- -## Phase 0 – 1 recap (done last week) +## Phase 0 – Project Skeleton (Days 1-2) -Skeleton repo, Docker Compose, OAuth stub, queue stub, drag-and-drop, WebSocket progress, and dummy review table were delivered. -*Specification refs §18, 26-32, 73-77* +| Dev | Core tasks | Why / spec tie-in | Key refs | +| ----- | ---------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ------------------------------------ | +| **A** | • Initialise mono-repo (pnpm workspaces)
• Add ESLint + Prettier + Vitest + Cypress in CI pipeline (§88) | Gives every feature a common PR workflow | | +| **B** | • Write `docker-compose.dev.yml` with Postgres, Redis, MinIO, ClamAV
• Build disposable dev DB & Redis images | Local parity with prod object storage (§30) and virus-scan requirement (§62) | MinIO S3 compatibility ([min.io][1]) | +| **C** | • Scaffold Next.js + Tailwind app; configure Storybook
• Create minimal landing page shell (§66) | Proves FE tool-chain end-to-end | | --- -## Phase 2 – Minimum Viable Rename (Weeks 2-4) +## Phase 1 – Auth & Upload Skeleton (Week 1) -### Dev A – Backend / API - -* **Harden Google OAuth flow**: add state + PKCE, token verification, refresh‐token rotation ([Google for Developers][1], [DEV Community][2]) -* **Finish REST endpoints** `/api/batch`, `/status`, `/zip`, `/keywords/enhance`, wiring them to DB via Prisma. -* **Implement atomic quota decrement** when each image job is en-queued §56-57 -* **Create cron job** using node-crond to reset quotas at UTC 00:00 monthly §58 -* **Write integration tests** (Vitest) for the above. - -### Dev B – Worker & Vision - -* **Swap dummy worker for real BullMQ worker**; configure 5-attempt exponential back-off §65 -* **Integrate ClamAV scan** for each file before Vision call ([Transloadit][3], [DEV Community][4]) (fulfils §62). -* **Call Azure AI Vision v4 tagging API** and persist tags ≥0.40 confidence §38-41 -* **Implement filename generation algorithm** satisfying §43-48, including stop-word removal and collision suffixes . -* **Stream progress via WebSocket** (`images/{id}/status`) when each step finishes §77 . - -### Dev C – Front-end / UX - -* **Keyword chip-list & “Enhance” button** with optimistic UI §33-37 . -* **Review Table** with inline-edit, regenerate-name, and shimmer placeholders §49-53, 70 . -* **Download ZIP** trigger once all rows DONE (§54-55) and show quota bar (§67). -* **Accessibility pass** (keyboard navigation, aria labels) towards Lighthouse ≥90 §59, 85 . +| Dev | Core tasks | Spec link | External refs | +| ----- | --------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ------------- | +| **A** | • Implement Google OAuth 2.0 “code” flow (§18-21); store only hashed email | Google OAuth best practices ([Google for Developers][2]) | | +| **B** | • Write `/api/batch` endpoint: accept multipart form, persist originals to MinIO, enqueue stub job (§29-32, 73) | BullMQ queue skeleton ([docs.bullmq.io][3]) | | +| **C** | • Drag-and-drop component with quota gate & SHA-256 dedup (§26-28) | React D-n-D tutorial ([Medium][4]) | | --- -## Phase 3 – Paid Plans & Quota Enforcement (Weeks 5-8) +## Phase 2 – Vision Pipeline & Real-time Progress (Week 2) -### Dev A - -* **Stripe Billing integration**: Checkout, customer portal, webhooks updating `plan` & `quota_remaining` ([Stripe][5], [Stripe][6]). -* **Usage-meter rows** in `payments` table; expose `/api/usage` for dashboard. -* **Automated downgrade** to Basic on cancellation (§25). - -### Dev B - -* **Batch ZIP assembly** in worker using archiver; preserve EXIF (§54-55). -* **S3-compatible storage move to MinIO production cluster** with signed URLs (spec §30 + MinIO docs) ([min.io][7]). -* **Queue metrics** to Prometheus histogram (§84). - -### Dev C - -* **Billing page & upgrade modal** (§22-24, 71). -* **Plan badge & quota reset animation** after webhook arrives via SSE. -* **Error states UI** (e.g., virus detected, vision failure). +| Dev | Core tasks | Spec link | External refs | +| ----- | ----------------------------------------------------------------------------------- | ------------------------------------------------ | ------------- | +| **B** | • Integrate Google Cloud Vision label detection; discard < 0.40 confidence (§38-40) | Vision label API sample ([Google Cloud][5]) | | +| **A** | • `/api/batch/{id}/status` + WebSocket publisher (§74, 77) | WebSocket progress patterns ([GeeksforGeeks][6]) | | +| **C** | • Shimmer placeholder while waiting (§70); live progress bar | | | --- -## Phase 4 – Production Hardening (Weeks 9-12) +## Phase 3 – Filename Generation & Review UI (Week 3) -*All Devs participate; primary owner noted* - -| Owner | Task | -| ------- | --------------------------------------------------------------------------------------------------------------------------------------- | -| **A** | **OpenTelemetry tracing + Sentry error capture** (§82-83). | -| **B** | **Load testing and queue autoscale rules**; follow BullMQ robustness guidance ([Medium][8]). | -| **C** | **Lighthouse & a11y polish** to hit 90/90 (§59, 85). | -| **A** | **Prisma `migrate deploy` pipeline** and expand-and-contract strategy ([GitHub][9], [wasp.sh][10]). | -| **B** | **Security review**: ClamAV update job, Redis auth, secrets manager (§61-63). | -| **All** | **CI/CD**: ESLint, unit + e2e, Docker multi-stage build < 300 MB (§87) ([Docker Documentation][11], [blacksmith.sh][12], [GitHub][13]). | +| Dev | Core tasks | Spec link | External refs | +| ----- | --------------------------------------------------------------------------------------- | --------- | ------------- | +| **B** | • Implement filename algorithm (§43-48); unit-test collisions | | | +| **C** | • Review Table with inline edit, regenerate, and disabled “Download ZIP” logic (§49-55) | | | +| **A** | • `/api/batch/{id}/zip` stream preserving EXIF (§54-55) | | | --- -## Phase 5 – Growth & Ecosystem (Quarter 2) +## Phase 4 – Billing & Quota (Week 4) -* **CLI uploader & public API token auth** (Dev A). -* **LLM-powered keyword enhancement quality tuning** (Dev B). -* **Multi-language UI groundwork** (Dev C, extracting i18n JSON; spec §86). -* **Admin analytics dashboard** (collab). -* **Optional: marketplace listing & SEO content marketing** (team). +| Dev | Core tasks | Spec link | External refs | +| ----- | ------------------------------------------------------------------------------- | ------------------------------------------ | ------------- | +| **A** | • Stripe Checkout session & webhook handler (§22-24); store usage rows (§56-58) | Stripe usage-record API ([Stripe Docs][7]) | | +| **B** | • Cron job to reset quotas monthly (§58) | | | +| **C** | • Billing modal UX ≤ 3 clicks (§71) | | | --- -## Cross-cutting, continuous duties +## Phase 5 – Security, Rate-limiting & Observability (Week 5) -* **Code-review rotation**: each PR must be reviewed by one dev outside its domain. -* **Weekly demo** to keep UI/APIcontracts honest. -* **Error budget & on-call**: 24 h chat response for prod issues once paying users arrive. -* **Documentation**: keep `/docs/ARCHITECTURE.md` and API schema up-to-date. +| Dev | Core tasks | Spec link | External refs | +| ----- | --------------------------------------------------------- | --------------------------------------- | ------------- | +| **B** | • ClamAV scan in worker before Vision call (§62) | Node-ClamAV guide ([Transloadit][8]) | | +| **A** | • Redis-backed rate-limit middleware for all APIs | Redis limiter example ([webdock.io][9]) | | +| **B** | • OpenTelemetry trace IDs, Prometheus histograms (§82-84) | | | +| **C** | • ARIA labels & keyboard nav (§85) | | | --- -### Why this works +## Phase 6 – Hardening & Deploy (Week 6) -*Each developer owns a vertical slice* so they can deliver value independently and learn the adjacent stack surface. *Shared DevOps, security, and reviews* prevent silos. The plan aligns strictly with the numbered requirements in your spec §10-90 while following industry best-practices for OAuth ([Google for Developers][1], [DEV Community][2]), queues ([docs.bullmq.io][14], [Medium][8]), database migrations ([GitHub][9], [wasp.sh][10]), malware scanning ([Transloadit][3], [DEV Community][4]), computer-vision tagging ([Microsoft Learn][15], [Microsoft Learn][16]), billing ([Stripe][5], [Stripe][6]), object storage ([min.io][7]), and container builds ([Docker Documentation][11], [GitHub][13]). Follow this sequence and the team will ship a monetisable, secure, and maintainable SaaS on schedule. +| Dev | Core tasks | Spec link | External refs | +| ----- | ---------------------------------------------------------------- | ------------------------------------------------- | ------------- | +| **A** | • Multi-stage Dockerfile ≤ 300 MB (§87) | Docker multi-stage tutorial ([cloudnweb.dev][10]) | | +| **B** | • Helm charts / K8s manifests; liveness & readiness probes (§90) | | | +| **C** | • Achieve ≥ 90 Lighthouse scores (§59) | | | -[1]: https://developers.google.com/identity/protocols/oauth2/resources/best-practices?utm_source=chatgpt.com "Best Practices | Authorization Resources" -[2]: https://dev.to/hamzakhan/mastering-oauth-20-in-modern-web-applications-security-best-practices-for-2024-26ed?utm_source=chatgpt.com "🔒 Mastering OAuth 2.0 in Modern Web Applications ..." -[3]: https://transloadit.com/devtips/implementing-server-side-malware-scanning-with-clamav-in-node-js/?utm_source=chatgpt.com "Implementing server-side malware scanning with ClamAV ..." -[4]: https://dev.to/jfbloom22/how-to-virus-scan-file-users-upload-using-clamav-2i5d?utm_source=chatgpt.com "How to virus scan file users upload using ClamAV" -[5]: https://stripe.com/in/billing?utm_source=chatgpt.com "Stripe Billing | Recurring Payments & Subscription Solutions" -[6]: https://stripe.com/billing/usage-based-billing?utm_source=chatgpt.com "Stripe Usage-Based Billing Software" -[7]: https://min.io/docs/minio/container/index.html?utm_source=chatgpt.com "MinIO Object Storage for Container" -[8]: https://medium.com/%40karthiks05/how-we-built-a-robust-message-queue-using-bullmq-part-1-2d5ad1016958?utm_source=chatgpt.com "How We Built a Robust Message Queue Using BullMQ" -[9]: https://github.com/prisma/prisma/discussions/24571?utm_source=chatgpt.com "How should migration be done to Production? #24571" -[10]: https://wasp.sh/blog/2025/04/02/an-introduction-to-database-migrations?utm_source=chatgpt.com "A Gentle Introduction to Database Migrations in Prisma ..." -[11]: https://docs.docker.com/build/building/best-practices/?utm_source=chatgpt.com "Building best practices" -[12]: https://www.blacksmith.sh/blog/understanding-multi-stage-docker-builds?ref=geeknest.ru&utm_source=chatgpt.com "Understanding Multi-Stage Docker Builds" -[13]: https://github.com/goldbergyoni/nodebestpractices?utm_source=chatgpt.com "The Node.js best practices list (July 2024)" -[14]: https://docs.bullmq.io/guide/retrying-failing-jobs?utm_source=chatgpt.com "Retrying failing jobs" -[15]: https://learn.microsoft.com/en-us/azure/ai-services/computer-vision/whats-new?utm_source=chatgpt.com "What's new in Azure AI Vision?" -[16]: https://learn.microsoft.com/en-us/azure/ai-services/computer-vision/concept-tag-images-40?utm_source=chatgpt.com "Content tags - Image Analysis 4.0 - Azure AI services" +--- + +## Phase 7 – Beta, QA & Launch (Week 7+) + +1. **A / B** – Load-test queue & database; horizontal-scale workers via separate K8s deployment (§64-65). +2. **C** – Usability test with 10 external users; fix friction points in drag-and-drop and review flow. +3. **All** – Run full compliance suite (spec §91-92); smoke-test zero-downtime rolling deploy. +4. **All** – Prepare launch comms, status-page, and support SOP. + +--- + +## Milestone Calendar (gantt-style) + +| Week | Deliverable | +| ---- | ------------------------------------------------ | +| 0 | CI green, Docker Compose spins up | +| 1 | OAuth sign-in → drag-drop upload → stub progress | +| 2 | Vision tags + live progress bar | +| 3 | Review table & ZIP download | +| 4 | Stripe billing & enforced quotas | +| 5 | Security / monitoring budgets green | +| 6 | First prod deploy on Kubernetes | +| 7 | Public beta → launch | + +--- + +### Why these tasks guarantee spec coverage + +*Each numbered requirement in the specification (10-90) is matched to at least one backlog item above, ensuring nothing is missed.* External references back up every key architectural decision: OAuth hardening ([Google for Developers][2]), Stripe metered billing ([Stripe Docs][7]), BullMQ queue semantics ([docs.bullmq.io][3]), ClamAV scanning ([Transloadit][8]), Vision label extraction ([Google Cloud][5]), MinIO S3 parity ([min.io][1]), React drag-and-drop UX ([Medium][4]), WebSocket progress patterns ([GeeksforGeeks][6]), multi-stage Docker builds ([cloudnweb.dev][10]), and Redis rate-limiting ([webdock.io][9]). Together they give the three developers a clear, testable path to shipping a fully compliant, production-ready “AI Bulk Image Renamer” SaaS. + +[1]: https://min.io/product/s3-compatibility?utm_source=chatgpt.com "AWS S3 Compatible Object Storage" +[2]: https://developers.google.com/identity/protocols/oauth2/resources/best-practices?utm_source=chatgpt.com "Best Practices | Authorization Resources" +[3]: https://docs.bullmq.io/?utm_source=chatgpt.com "What is BullMQ | BullMQ" +[4]: https://medium.com/%40dprincecoder/creating-a-drag-and-drop-file-upload-component-in-react-a-step-by-step-guide-4d93b6cc21e0?utm_source=chatgpt.com "Creating a Drag-and-Drop File Upload Component in React" +[5]: https://cloud.google.com/vision/docs/labels?utm_source=chatgpt.com "Detect Labels | Cloud Vision API" +[6]: https://www.geeksforgeeks.org/reactjs/real-time-updates-with-websockets-and-react-hooks/?utm_source=chatgpt.com "Real-time Updates with WebSockets and React Hooks" +[7]: https://docs.stripe.com/billing/subscriptions/usage-based/recording-usage-api?utm_source=chatgpt.com "Record usage for billing with the API" +[8]: https://transloadit.com/devtips/implementing-server-side-malware-scanning-with-clamav-in-node-js/?utm_source=chatgpt.com "Implementing server-side malware scanning with ClamAV ..." +[9]: https://webdock.io/en/docs/how-guides/database-guides/rate-limiting-redis-and-nodejs-under-hood?srsltid=AfmBOopRz8keqWAipmUZsrIyxdtxxce_tnd7TF7GkXrJgiB6vWHXAEZY&utm_source=chatgpt.com "Rate Limiting with Redis and Node.js: Under the Hood" +[10]: https://www.cloudnweb.dev/2019/10/crafting-multi-stage-builds-with-docker-in-node-js/?utm_source=chatgpt.com "Crafting multi-stage builds with Docker in Node.js - DEV Community" From 90016254a9281b88d2d999aa094758f3dc9b3b53 Mon Sep 17 00:00:00 2001 From: DustyWalker Date: Mon, 4 Aug 2025 21:09:31 +0200 Subject: [PATCH 03/38] 23 --- plan-for-devs.md | 115 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 105 insertions(+), 10 deletions(-) diff --git a/plan-for-devs.md b/plan-for-devs.md index c328a3b..b7ea38a 100644 --- a/plan-for-devs.md +++ b/plan-for-devs.md @@ -1,3 +1,108 @@ +Below is a pragmatic branching & workflow recipe that fits a **3-developer team on Forgejo**, keeps *main* stable, and bakes security into every merge. + +--- + +## Key take-away (one-paragraph summary) + +For a small, fast-moving team the safest and simplest approach is **trunk-based development**: protect a single *main* branch, work in short-lived feature/bug-fix branches, gate every merge through mandatory reviews, signed commits, and a Forgejo Actions CI pipeline. Branch protection rules in Forgejo stop direct pushes, require 1-2 approvals, verified signatures, and green status checks, while team-level permissions ensure only reviewers (not all contributors) can override rules. This set-up gives you continuous delivery speed without the complexity of full GitFlow, yet still allows optional *release/*\*\* or *hotfix/*\*\* branches when you tag production versions. + +--- + +## 1. Choose a lightweight trunk-based flow + +* **Why trunk-based?** Comparative studies show it reduces merge debt and is easier to automate than GitFlow for small teams that deploy often. ([Assembla][1], [Graphite.dev][2]) +* **Branch lifetime:** create a branch per ticket, keep it under a few days, merge when CI passes. Long-running “develop” branches are unnecessary overhead here. ([Assembla][1]) + +--- + +## 2. Default branches & naming conventions + +| Purpose | Pattern | Notes | +| -------------------------- | ----------------------------- | --------------------------------------- | +| Production/trunk | **main** | Always deployable. | +| Working branches | **feature/\***, **bugfix/\*** | One per task. | +| Stable releases (optional) | **release/**\* | e.g. `release/2025-08-beta`. | +| Emergency fixes | **hotfix/\*** | Branched from *main*, merged back fast. | + +Clear prefixes make it obvious what can be safely deleted after merge and align with widely-used conventions. ([Medium][3], [Graphite.dev][4]) + +--- + +## 3. Protect *main* (and *release/*\*\*) branches + +* **Enable branch protection**: go to **Settings → Branches → Add rule** in Forgejo and apply to `main` and `release/**`. ([forgejo.org][5]) +* **Checks to require** + + 1. *Minimum approvals*: set to **2** so any code needs a peer review. ([docs.gitea.com][6], [GitHub Docs][7]) + 2. *Status checks*: gate merge on “ci/forgejo-actions” passing. ([forgejo.org][8]) + 3. *Signed commits*: tick “reject unsigned” so only GPG- or SSH-signed commits reach trunk. ([Gitea][9], [Gitea][10]) + 4. *Force-push & branch deletion*: disable both for protected branches to retain history. ([GitHub Docs][11]) +* **Linear history (optional)**: require “rebase-merge” to avoid merge bubbles if you prefer a tidy log. ([forgejo.org][5]) + +--- + +## 4. CI gates with Forgejo Actions + +1. **Runner**: add a `forgejo-runner` container; it spins up each job in Docker for parity with prod. ([forgejo.org][12]) +2. **Workflow file** (`.forgejo/workflows/ci.yml`): + + * lint → test → build → (optionally) Docker image scan. + * upload artifacts so reviewers can download a built ZIP. +3. **Status check**: Forgejo automatically publishes a context (e.g. `ci/forgejo-actions`); make this required in branch protection. ([forgejo.org][8]) + +--- + +## 5. Repository permissions & secret management + +* **Teams & roles**: give “Write” to developers and “Admin” only to one maintainer account; review-only roles can still approve PRs. ([forgejo.org][13]) +* **Secrets**: store test API keys in Forgejo’s encrypted Secrets UI; runners inject them at runtime so they never enter Git history. ([forgejo.org][14]) + +--- + +## 6. Merge policy & code review hygiene + +* **Pull Request template**: checklist for tests, docs, threat modelling. +* **CODEOWNERS**: although Forgejo doesn’t yet enforce mandatory owner approval, it still auto-pings the right reviewer set. ([GitHub][15]) +* **Conversation resolution**: reviewers can block merge until all comments are addressed (mirrors GitHub’s “require conversation resolution”). ([GitHub Docs][7]) + +--- + +## 7. Day-to-day workflow (example) + +1. `git switch -c feature/login-rate-limit` +2. Code & commit with `-S` to sign. +3. Push and open PR; CI runs automatically. +4. Two devs review; one fixes comments; CI re-runs. +5. Merge via “Rebase & Merge” button; branch auto-deleted. +6. CI (on *main*) builds & pushes image to registry; CD pipeline (outside Forgejo) deploys staging. + +--- + +## 8. Extra safety nets + +* **Draft PRs** for work-in-progress so CI still runs but can’t merge. +* **Feature flags** in code to dark-deploy risky features. +* **Dependabot/Gitea Mirror** for automated security updates (make those branches exempt from approval but still require CI). +* **Nightly end-to-end run** on *main* to catch hidden regressions. + +--- + +### Final checklist + +| Guardrail | Enabled? | +| --------------------------------- | -------- | +| Protected *main* & *release/*\*\* | ✅ | +| Required CI status | ✅ | +| 2 reviewer approvals | ✅ | +| Signed commit verification | ✅ | +| No force-push / deletions | ✅ | +| Secrets in Forgejo vault | ✅ | + +Follow this structure and you’ll have a **secure, review-driven workflow** that lets all three developers move quickly without breaking production. + + + + **Below is a “from-zero-to-launch” backlog that maps *every* requirement in the specification to concrete tasks for your three-person team. It is organised by seven sequential phases (about one week each) so you can drop the list straight into a Kanban board. After the backlog you’ll find a compact milestone calendar. Tasks mention the responsible developer (“A / B / C”) and the spec clause they satisfy.** --- @@ -101,13 +206,3 @@ *Each numbered requirement in the specification (10-90) is matched to at least one backlog item above, ensuring nothing is missed.* External references back up every key architectural decision: OAuth hardening ([Google for Developers][2]), Stripe metered billing ([Stripe Docs][7]), BullMQ queue semantics ([docs.bullmq.io][3]), ClamAV scanning ([Transloadit][8]), Vision label extraction ([Google Cloud][5]), MinIO S3 parity ([min.io][1]), React drag-and-drop UX ([Medium][4]), WebSocket progress patterns ([GeeksforGeeks][6]), multi-stage Docker builds ([cloudnweb.dev][10]), and Redis rate-limiting ([webdock.io][9]). Together they give the three developers a clear, testable path to shipping a fully compliant, production-ready “AI Bulk Image Renamer” SaaS. -[1]: https://min.io/product/s3-compatibility?utm_source=chatgpt.com "AWS S3 Compatible Object Storage" -[2]: https://developers.google.com/identity/protocols/oauth2/resources/best-practices?utm_source=chatgpt.com "Best Practices | Authorization Resources" -[3]: https://docs.bullmq.io/?utm_source=chatgpt.com "What is BullMQ | BullMQ" -[4]: https://medium.com/%40dprincecoder/creating-a-drag-and-drop-file-upload-component-in-react-a-step-by-step-guide-4d93b6cc21e0?utm_source=chatgpt.com "Creating a Drag-and-Drop File Upload Component in React" -[5]: https://cloud.google.com/vision/docs/labels?utm_source=chatgpt.com "Detect Labels | Cloud Vision API" -[6]: https://www.geeksforgeeks.org/reactjs/real-time-updates-with-websockets-and-react-hooks/?utm_source=chatgpt.com "Real-time Updates with WebSockets and React Hooks" -[7]: https://docs.stripe.com/billing/subscriptions/usage-based/recording-usage-api?utm_source=chatgpt.com "Record usage for billing with the API" -[8]: https://transloadit.com/devtips/implementing-server-side-malware-scanning-with-clamav-in-node-js/?utm_source=chatgpt.com "Implementing server-side malware scanning with ClamAV ..." -[9]: https://webdock.io/en/docs/how-guides/database-guides/rate-limiting-redis-and-nodejs-under-hood?srsltid=AfmBOopRz8keqWAipmUZsrIyxdtxxce_tnd7TF7GkXrJgiB6vWHXAEZY&utm_source=chatgpt.com "Rate Limiting with Redis and Node.js: Under the Hood" -[10]: https://www.cloudnweb.dev/2019/10/crafting-multi-stage-builds-with-docker-in-node-js/?utm_source=chatgpt.com "Crafting multi-stage builds with Docker in Node.js - DEV Community" From 4b82d495b21265cf795f9f93a8b8c56a345843e4 Mon Sep 17 00:00:00 2001 From: Jeen Koster Date: Mon, 4 Aug 2025 22:20:30 +0200 Subject: [PATCH 04/38] Jeens made front end design with all mvp functions without vision ai --- .env | 22 ++ index.html | 246 +++++++++++++++++++ script.js | 420 ++++++++++++++++++++++++++++++++ styles.css | 688 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1376 insertions(+) create mode 100644 .env create mode 100644 index.html create mode 100644 script.js create mode 100644 styles.css diff --git a/.env b/.env new file mode 100644 index 0000000..fb31641 --- /dev/null +++ b/.env @@ -0,0 +1,22 @@ +# === AI CONFIGURATION === + +# Your API key or access token +AI_API_KEY=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 + +# Name or ID of the AI model +AI_MODEL_NAME=deepseek/deepseek-chat-v3-0324:free + +# Endpoint or base URL for DeepSeek API (update if different) +AI_API_URL=https://api.deepseek.com/v1 + +# Optional: AI task-specific configuration +AI_TASK=keyword_generation +AI_RENAME_STRATEGY=descriptive # Options: 'timestamped', 'uuid', 'descriptive' + +# === GENERAL SETTINGS === + +# Environment type +NODE_ENV=development + +# Optional: Logging or debugging +ENABLE_LOGGING=true \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..e1faa9e --- /dev/null +++ b/index.html @@ -0,0 +1,246 @@ + + + + + + SEO Image Renamer - AI-Powered Image SEO Tool + + + + +
+
+ + +
+
+ +
+
+
+
+

Boost Your Website's SEO with AI-Powered Image Naming

+

Stop manually renaming images for SEO. Our AI tool automatically generates SEO-friendly filenames based on your keywords and image content.

+ Try It Free +
+
+ SEO Image Renamer Dashboard +
+
+
+ +
+
+

Rename Your Images with AI

+

Upload your images, provide keywords, and let our AI generate SEO-optimized filenames.

+ +
+
+
+ +

Drag & Drop your images here

+

or

+ + +
+
+
+ + + + +
+
+ +
+
+

Powerful Features for Better SEO

+

Our AI-powered tool helps you optimize your images for search engines without the manual work.

+ +
+
+ +

AI-Powered Naming

+

Our advanced AI generates SEO-friendly filenames that help your images rank higher in search results.

+
+ +
+ +

Image Recognition

+

AI analyzes your images to understand content and context for more accurate naming.

+
+ +
+ +

Keyword Enhancement

+

Enhance your keywords with AI-suggested synonyms for better SEO performance.

+
+ +
+ +

Easy Download

+

Download all your renamed images in a single ZIP file for easy implementation.

+
+
+
+
+ +
+
+

How It Works

+

Get better SEO for your images in just three simple steps.

+ +
+
+
1
+

Upload Images

+

Drag and drop your images or browse your files to upload them to our platform.

+
+ +
+
2
+

Add Keywords

+

Provide keywords that describe your images, or let our AI enhance them for better SEO.

+
+ +
+
3
+

Download & Implement

+

Download your renamed images as a ZIP file and use them on your website.

+
+
+
+
+ +
+
+

Simple, Transparent Pricing

+

Choose the plan that works best for you.

+ +
+
+

Basic

+
$0/month
+
    +
  • 50 images per month
  • +
  • AI-powered naming
  • +
  • Keyword enhancement
  • +
  • ZIP download
  • +
+ +
+ + + +
+

Max

+
$19/month
+
    +
  • 1000 images per month
  • +
  • AI-powered naming
  • +
  • Keyword enhancement
  • +
  • ZIP download
  • +
  • Priority support
  • +
  • Advanced analytics
  • +
+ +
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/script.js b/script.js new file mode 100644 index 0000000..4703f02 --- /dev/null +++ b/script.js @@ -0,0 +1,420 @@ +// Global variables +let uploadedImages = []; +let keywords = []; +let generatedNames = []; + +// DOM elements +const dropArea = document.getElementById('drop-area'); +const fileInput = document.getElementById('file-input'); +const browseBtn = document.getElementById('browse-btn'); +const keywordsSection = document.getElementById('keywords-section'); +const keywordInput = document.getElementById('keyword-input'); +const enhanceBtn = document.getElementById('enhance-btn'); +const keywordsDisplay = document.getElementById('keywords-display'); +const imagesPreview = document.getElementById('images-preview'); +const imagesContainer = document.getElementById('images-container'); +const downloadBtn = document.getElementById('download-btn'); + +// AI Configuration (in a real app, this would be loaded from .env) +const AI_CONFIG = { + API_KEY: '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', + MODEL_NAME: 'deepseek/deepseek-chat-v3-0324:free', + API_URL: 'https://openrouter.ai/api/v1/chat/completions' +}; + +// Event listeners +document.addEventListener('DOMContentLoaded', () => { + // Upload area event listeners + dropArea.addEventListener('click', () => fileInput.click()); + browseBtn.addEventListener('click', () => fileInput.click()); + fileInput.addEventListener('change', handleFileSelect); + + // Drag and drop events + ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { + dropArea.addEventListener(eventName, preventDefaults, false); + }); + + ['dragenter', 'dragover'].forEach(eventName => { + dropArea.addEventListener(eventName, highlight, false); + }); + + ['dragleave', 'drop'].forEach(eventName => { + dropArea.addEventListener(eventName, unhighlight, false); + }); + + dropArea.addEventListener('drop', handleDrop, false); + + // Keyword events + keywordInput.addEventListener('input', toggleEnhanceButton); + enhanceBtn.addEventListener('click', enhanceKeywords); + + // Download button + downloadBtn.addEventListener('click', downloadImages); +}); + +// Prevent default drag behaviors +function preventDefaults(e) { + e.preventDefault(); + e.stopPropagation(); +} + +// Highlight drop area when item is dragged over it +function highlight() { + dropArea.classList.add('dragover'); +} + +// Remove highlight when item is dragged out of drop area +function unhighlight() { + dropArea.classList.remove('dragover'); +} + +// Handle dropped files +function handleDrop(e) { + const dt = e.dataTransfer; + const files = dt.files; + handleFiles(files); +} + +// Handle file selection +function handleFileSelect(e) { + const files = e.target.files; + handleFiles(files); +} + +// Process uploaded files +function handleFiles(files) { + if (files.length === 0) return; + + // Convert FileList to Array + const filesArray = Array.from(files); + + // Filter only image files + const imageFiles = filesArray.filter(file => file.type.startsWith('image/')); + + if (imageFiles.length === 0) { + alert('Please select image files only.'); + return; + } + + // Clear previous images + uploadedImages = []; + + // Process each image file + let processedCount = 0; + imageFiles.forEach(file => { + const reader = new FileReader(); + reader.onload = (e) => { + uploadedImages.push({ + file: file, + name: file.name, + size: file.size, + type: file.type, + src: e.target.result, + newName: generateFileName(file.name) + }); + + processedCount++; + // Show keywords section after all files are processed + if (processedCount === imageFiles.length) { + keywordsSection.style.display = 'block'; + imagesPreview.style.display = 'block'; + updateImagesPreview(); + } + }; + reader.readAsDataURL(file); + }); +} + +// Generate a simple filename based on original name +function generateFileName(originalName) { + // Remove extension + const nameWithoutExt = originalName.substring(0, originalName.lastIndexOf('.')); + // Replace non-alphanumeric characters with spaces + const cleanName = nameWithoutExt.replace(/[^a-zA-Z0-9]/g, ' '); + // Capitalize first letter and make it SEO friendly + return cleanName.charAt(0).toUpperCase() + cleanName.slice(1); +} + +// Toggle enhance button based on keyword input +function toggleEnhanceButton() { + enhanceBtn.disabled = keywordInput.value.trim() === ''; +} + +// Enhance keywords with AI +async function enhanceKeywords() { + const keywordText = keywordInput.value.trim(); + if (keywordText === '') return; + + // Show loading state + enhanceBtn.innerHTML = ' Enhancing...'; + enhanceBtn.disabled = true; + + try { + // Call AI API to enhance keywords + const enhancedKeywords = await callAIKeywordEnhancement(keywordText); + + // Split keywords by comma or space + const newKeywords = enhancedKeywords.split(/[, ]+/).filter(k => k !== ''); + + // Add new keywords to the list + newKeywords.forEach(keyword => { + if (!keywords.includes(keyword)) { + keywords.push(keyword); + } + }); + + // Update keywords display + updateKeywordsDisplay(); + + // Clear input + keywordInput.value = ''; + + // Generate new filenames for images + await generateNewFileNamesWithAI(); + } catch (error) { + console.error('Error enhancing keywords:', error); + alert('An error occurred while enhancing keywords. Please try again.'); + } finally { + // Reset button + enhanceBtn.innerHTML = ' Enhance with AI'; + enhanceBtn.disabled = keywordInput.value.trim() === ''; + } +} + +// Call AI API to enhance keywords +async function callAIKeywordEnhancement(keywords) { + const prompt = `Enhance these keywords for SEO image optimization. Provide 10 additional related keywords that would help images rank better in search engines. Return only the keywords separated by commas, nothing else. Keywords: ${keywords}`; + + const response = await fetch(AI_CONFIG.API_URL, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${AI_CONFIG.API_KEY}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + model: AI_CONFIG.MODEL_NAME, + messages: [ + { + role: "user", + content: prompt + } + ] + }) + }); + + if (!response.ok) { + throw new Error(`API request failed with status ${response.status}`); + } + + const data = await response.json(); + return data.choices[0].message.content.trim(); +} + +// Generate new filenames for images based on keywords using AI +async function generateNewFileNamesWithAI() { + if (keywords.length === 0) return; + + // Show loading state for each image + document.querySelectorAll('.new-name-input').forEach(input => { + input.disabled = true; + input.placeholder = 'Generating AI filename...'; + }); + + try { + // Generate new filename for each image + for (let i = 0; i < uploadedImages.length; i++) { + const image = uploadedImages[i]; + const keywordString = keywords.slice(0, 5).join(', '); + + // Call AI to generate a descriptive filename + const aiGeneratedName = await callAIFilenameGeneration(image.name, keywordString); + + // Update the image with the new name + const nameWithoutExt = image.name.substring(0, image.name.lastIndexOf('.')); + const extension = image.name.substring(image.name.lastIndexOf('.')); + const newName = `${aiGeneratedName.substring(0, 50)}${extension}`; + image.newName = newName; + } + + // Update images preview + updateImagesPreview(); + + // Enable download button + downloadBtn.disabled = false; + } catch (error) { + console.error('Error generating filenames:', error); + alert('An error occurred while generating filenames. Please try again.'); + + // Revert to simple filename generation + generateNewFileNames(); + } finally { + // Re-enable inputs + document.querySelectorAll('.new-name-input').forEach(input => { + input.disabled = false; + input.placeholder = ''; + }); + } +} + +// Call AI API to generate filename +async function callAIFilenameGeneration(originalName, keywords) { + const prompt = `Generate an SEO-optimized filename for an image. The original filename is "${originalName}" and the keywords are: ${keywords}. Create a descriptive, SEO-friendly filename that is 3-6 words long. Use only letters, numbers, and hyphens. Do not include the file extension. Return only the filename, nothing else.`; + + const response = await fetch(AI_CONFIG.API_URL, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${AI_CONFIG.API_KEY}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + model: AI_CONFIG.MODEL_NAME, + messages: [ + { + role: "user", + content: prompt + } + ] + }) + }); + + if (!response.ok) { + throw new Error(`API request failed with status ${response.status}`); + } + + const data = await response.json(); + return data.choices[0].message.content.trim().replace(/[^a-zA-Z0-9\- ]/g, '').replace(/\s+/g, '-'); +} + +// Fallback function to generate new filenames without AI +function generateNewFileNames() { + if (keywords.length === 0) return; + + uploadedImages.forEach((image, index) => { + // Combine keywords with the original filename + const keywordString = keywords.slice(0, 3).join(' '); + const nameWithoutExt = image.name.substring(0, image.name.lastIndexOf('.')); + const extension = image.name.substring(image.name.lastIndexOf('.')); + + // Create new name + const newName = `${keywordString} ${nameWithoutExt}`.substring(0, 50) + extension; + image.newName = newName; + }); + + // Update images preview + updateImagesPreview(); + + // Enable download button + downloadBtn.disabled = false; +} + +// Update keywords display +function updateKeywordsDisplay() { + keywordsDisplay.innerHTML = ''; + + keywords.forEach((keyword, index) => { + const keywordChip = document.createElement('div'); + keywordChip.className = 'keyword-chip'; + keywordChip.innerHTML = ` + ${keyword} + + `; + keywordsDisplay.appendChild(keywordChip); + }); + + // Add event listeners to remove buttons + document.querySelectorAll('.remove-keyword').forEach(button => { + button.addEventListener('click', (e) => { + const index = parseInt(e.target.getAttribute('data-index')); + keywords.splice(index, 1); + updateKeywordsDisplay(); + generateNewFileNames(); + }); + }); +} + +// Update images preview +function updateImagesPreview() { + imagesContainer.innerHTML = ''; + + uploadedImages.forEach((image, index) => { + const imageCard = document.createElement('div'); + imageCard.className = 'image-card'; + imageCard.innerHTML = ` + ${image.name} +
+
Original: ${image.name}
+
+ + +
+
+ `; + imagesContainer.appendChild(imageCard); + }); + + // Add event listeners to name inputs + document.querySelectorAll('.new-name-input').forEach(input => { + input.addEventListener('input', (e) => { + const index = parseInt(e.target.getAttribute('data-index')); + uploadedImages[index].newName = e.target.value; + }); + }); +} + +// Download images as ZIP +async function downloadImages() { + if (uploadedImages.length === 0) return; + + // Show loading state + downloadBtn.innerHTML = ' Preparing Download...'; + downloadBtn.disabled = true; + + try { + // Create a new ZIP file + const zip = new JSZip(); + + // Add each image to the ZIP with its new name + for (const image of uploadedImages) { + // Convert data URL to blob + const blob = await fetch(image.src).then(res => res.blob()); + zip.file(image.newName, blob); + } + + // Generate the ZIP file + const content = await zip.generateAsync({type: "blob"}); + + // Create download link + const url = URL.createObjectURL(content); + const a = document.createElement('a'); + a.href = url; + a.download = 'renamed-images.zip'; + document.body.appendChild(a); + a.click(); + + // Clean up + setTimeout(() => { + document.body.removeChild(a); + URL.revokeObjectURL(url); + }, 100); + + // Reset button + downloadBtn.innerHTML = ' Download Renamed Images as ZIP'; + downloadBtn.disabled = false; + } catch (error) { + console.error('Error creating ZIP file:', error); + alert('An error occurred while creating the ZIP file. Please try again.'); + + // Reset button + downloadBtn.innerHTML = ' Download Renamed Images as ZIP'; + downloadBtn.disabled = false; + } +} + +// Initialize the page +function init() { + // Set up any initial state + downloadBtn.disabled = true; +} + +// Call init when page loads +init(); \ No newline at end of file diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..d9c322c --- /dev/null +++ b/styles.css @@ -0,0 +1,688 @@ +/* Reset and base styles */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + line-height: 1.6; + color: #333; + background-color: #f8f9fa; +} + +.container { + width: 90%; + max-width: 1200px; + margin: 0 auto; + padding: 0 15px; +} + +/* Header styles */ +header { + background-color: #fff; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + position: sticky; + top: 0; + z-index: 100; +} + +header .container { + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px 0; +} + +.logo h1 { + font-size: 1.8rem; + font-weight: 700; + color: #2c3e50; +} + +nav ul { + display: flex; + list-style: none; +} + +nav ul li { + margin-left: 30px; +} + +nav ul li a { + text-decoration: none; + color: #333; + font-weight: 500; + transition: color 0.3s; +} + +nav ul li a:hover { + color: #3498db; +} + +.btn { + display: inline-block; + padding: 10px 20px; + border-radius: 5px; + text-decoration: none; + font-weight: 600; + cursor: pointer; + transition: all 0.3s; + border: none; + font-size: 1rem; +} + +.btn-primary { + background-color: #3498db; + color: white; +} + +.btn-primary:hover { + background-color: #2980b9; + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); +} + +.btn-secondary { + background-color: #2c3e50; + color: white; +} + +.btn-secondary:hover { + background-color: #1a252f; + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); +} + +.btn-large { + padding: 15px 30px; + font-size: 1.1rem; +} + +/* Hero section */ +.hero { + padding: 80px 0; + background: linear-gradient(135deg, #3498db 0%, #2c3e50 100%); + color: white; + text-align: center; +} + +.hero .container { + display: flex; + flex-direction: column; + align-items: center; +} + +.hero-content { + max-width: 800px; + margin-bottom: 40px; +} + +.hero h1 { + font-size: 2.5rem; + margin-bottom: 20px; + font-weight: 700; +} + +.hero p { + font-size: 1.2rem; + margin-bottom: 30px; + opacity: 0.9; +} + +.hero-image { + max-width: 600px; + width: 100%; + border-radius: 10px; + overflow: hidden; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); +} + +.hero-image img { + width: 100%; + height: auto; + display: block; +} + +/* Upload section */ +.upload-section { + padding: 80px 0; + background-color: #fff; +} + +.upload-section h2 { + text-align: center; + font-size: 2.2rem; + margin-bottom: 15px; + color: #2c3e50; +} + +.section-description { + text-align: center; + font-size: 1.1rem; + color: #7f8c8d; + max-width: 700px; + margin: 0 auto 50px; +} + +.drop-area { + border: 3px dashed #3498db; + border-radius: 10px; + padding: 60px 20px; + text-align: center; + background-color: #f8f9fa; + transition: all 0.3s; + cursor: pointer; +} + +.drop-area:hover, .drop-area.dragover { + background-color: #e3f2fd; + border-color: #2980b9; +} + +.drop-area-content i { + color: #3498db; + margin-bottom: 20px; +} + +.drop-area-content h3 { + margin-bottom: 15px; + color: #2c3e50; +} + +.drop-area-content p { + margin-bottom: 20px; + color: #7f8c8d; +} + +.keywords-section { + margin-top: 50px; +} + +.keywords-section h3 { + text-align: center; + margin-bottom: 15px; + color: #2c3e50; +} + +.keywords-input { + display: flex; + max-width: 600px; + margin: 0 auto 30px; + gap: 10px; +} + +.keywords-input input { + flex: 1; + padding: 15px; + border: 1px solid #ddd; + border-radius: 5px; + font-size: 1rem; +} + +.keywords-input input:focus { + outline: none; + border-color: #3498db; + box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2); +} + +.keywords-display { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 10px; + margin-top: 20px; +} + +.keyword-chip { + background-color: #3498db; + color: white; + padding: 8px 15px; + border-radius: 20px; + display: flex; + align-items: center; + gap: 5px; +} + +.keyword-chip .remove-keyword { + background: none; + border: none; + color: white; + cursor: pointer; + font-size: 1rem; +} + +.images-preview { + margin-top: 50px; +} + +.images-preview h3 { + text-align: center; + margin-bottom: 30px; + color: #2c3e50; +} + +.images-container { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + gap: 30px; + margin-bottom: 40px; +} + +.image-card { + background: #fff; + border-radius: 10px; + overflow: hidden; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); + transition: transform 0.3s; +} + +.image-card:hover { + transform: translateY(-5px); +} + +.image-thumbnail { + width: 100%; + height: 200px; + object-fit: cover; +} + +.image-info { + padding: 20px; +} + +.image-info .original-name { + font-size: 0.9rem; + color: #7f8c8d; + margin-bottom: 10px; + word-break: break-all; +} + +.image-info .new-name { + font-weight: 600; + color: #2c3e50; + word-break: break-all; +} + +.image-info input { + width: 100%; + padding: 10px; + border: 1px solid #ddd; + border-radius: 5px; + font-size: 1rem; + margin-top: 10px; +} + +.image-info input:focus { + outline: none; + border-color: #3498db; + box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2); +} + +.actions { + text-align: center; +} + +/* Features section */ +.features { + padding: 80px 0; + background-color: #f8f9fa; +} + +.features h2 { + text-align: center; + font-size: 2.2rem; + margin-bottom: 15px; + color: #2c3e50; +} + +.features-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 30px; + margin-top: 50px; +} + +.feature-card { + background: #fff; + padding: 30px; + border-radius: 10px; + text-align: center; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05); + transition: transform 0.3s; +} + +.feature-card:hover { + transform: translateY(-10px); +} + +.feature-card i { + color: #3498db; + margin-bottom: 20px; +} + +.feature-card h3 { + margin-bottom: 15px; + color: #2c3e50; +} + +.feature-card p { + color: #7f8c8d; +} + +/* How it works section */ +.how-it-works { + padding: 80px 0; + background-color: #fff; +} + +.how-it-works h2 { + text-align: center; + font-size: 2.2rem; + margin-bottom: 15px; + color: #2c3e50; +} + +.steps { + display: flex; + justify-content: space-between; + margin-top: 50px; + flex-wrap: wrap; +} + +.step { + flex: 1; + min-width: 250px; + text-align: center; + padding: 0 20px; + position: relative; +} + +.step:not(:last-child):after { + content: ""; + position: absolute; + top: 40px; + right: 0; + width: 50%; + height: 2px; + background-color: #3498db; +} + +.step-number { + width: 80px; + height: 80px; + background-color: #3498db; + color: white; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 2rem; + font-weight: 700; + margin: 0 auto 20px; +} + +.step h3 { + margin-bottom: 15px; + color: #2c3e50; +} + +.step p { + color: #7f8c8d; +} + +/* Pricing section */ +.pricing { + padding: 80px 0; + background-color: #f8f9fa; +} + +.pricing h2 { + text-align: center; + font-size: 2.2rem; + margin-bottom: 15px; + color: #2c3e50; +} + +.pricing-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 30px; + margin-top: 50px; +} + +.pricing-card { + background: #fff; + border-radius: 10px; + padding: 40px 30px; + text-align: center; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05); + position: relative; + overflow: hidden; +} + +.pricing-card.featured { + transform: scale(1.05); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); + border: 2px solid #3498db; +} + +.featured-badge { + position: absolute; + top: 20px; + right: -30px; + background-color: #3498db; + color: white; + padding: 5px 30px; + transform: rotate(45deg); + font-size: 0.8rem; + font-weight: 600; +} + +.pricing-card h3 { + font-size: 1.8rem; + margin-bottom: 20px; + color: #2c3e50; +} + +.price { + font-size: 3rem; + font-weight: 700; + color: #2c3e50; + margin-bottom: 30px; +} + +.price span { + font-size: 1rem; + color: #7f8c8d; +} + +.pricing-card ul { + list-style: none; + margin-bottom: 30px; + text-align: left; +} + +.pricing-card ul li { + padding: 10px 0; + border-bottom: 1px solid #eee; + display: flex; + align-items: center; +} + +.pricing-card ul li:before { + content: "\f00c"; + font-family: "Font Awesome 5 Free"; + font-weight: 900; + color: #3498db; + margin-right: 10px; +} + +.pricing-card .btn { + width: 100%; +} + +/* Footer */ +footer { + background-color: #2c3e50; + color: white; + padding: 60px 0 30px; +} + +.footer-content { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + margin-bottom: 40px; +} + +.footer-logo h2 { + font-size: 1.8rem; + margin-bottom: 10px; +} + +.footer-logo p { + opacity: 0.7; +} + +.footer-links { + display: flex; + gap: 50px; +} + +.footer-column h4 { + margin-bottom: 20px; + position: relative; + padding-bottom: 10px; +} + +.footer-column h4:after { + content: ""; + position: absolute; + bottom: 0; + left: 0; + width: 50px; + height: 2px; + background-color: #3498db; +} + +.footer-column ul { + list-style: none; +} + +.footer-column ul li { + margin-bottom: 10px; +} + +.footer-column ul li a { + color: #bbb; + text-decoration: none; + transition: color 0.3s; +} + +.footer-column ul li a:hover { + color: #3498db; +} + +.footer-bottom { + text-align: center; + padding-top: 30px; + border-top: 1px solid rgba(255, 255, 255, 0.1); + color: #bbb; +} + +/* Responsive design */ +@media (max-width: 992px) { + .hero .container { + flex-direction: column; + } + + .hero-content { + margin-bottom: 50px; + } + + .steps { + flex-direction: column; + gap: 50px; + } + + .step:not(:last-child):after { + display: none; + } + + .footer-content { + flex-direction: column; + gap: 40px; + } + + .footer-links { + gap: 30px; + } +} + +@media (max-width: 768px) { + header .container { + flex-direction: column; + gap: 20px; + } + + nav ul { + flex-wrap: wrap; + justify-content: center; + } + + nav ul li { + margin: 5px 10px; + } + + .hero { + padding: 60px 0; + } + + .hero h1 { + font-size: 2rem; + } + + .hero p { + font-size: 1rem; + } + + .keywords-input { + flex-direction: column; + } + + .pricing-card.featured { + transform: scale(1); + } +} + +@media (max-width: 576px) { + .container { + width: 95%; + } + + .hero { + padding: 40px 0; + } + + .hero h1 { + font-size: 1.8rem; + } + + .upload-section, .features, .how-it-works, .pricing { + padding: 60px 0; + } + + .section-description { + font-size: 1rem; + } + + .drop-area { + padding: 40px 15px; + } + + .images-container { + grid-template-columns: 1fr; + } +} \ No newline at end of file From a829770c7e684bd91c4cb7a89ac6b7943f32d0c4 Mon Sep 17 00:00:00 2001 From: Jeen Koster Date: Tue, 5 Aug 2025 00:15:36 +0200 Subject: [PATCH 05/38] Final front-end version for day 1 by Jeen. --- index.html | 182 ++++++--- script.js | 259 ++++++++++-- styles.css | 1144 +++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 1173 insertions(+), 412 deletions(-) diff --git a/index.html b/index.html index e1faa9e..5e2c585 100644 --- a/index.html +++ b/index.html @@ -11,7 +11,7 @@
+
+ +
-
-

Boost Your Website's SEO with AI-Powered Image Naming

-

Stop manually renaming images for SEO. Our AI tool automatically generates SEO-friendly filenames based on your keywords and image content.

- Try It Free -
-
- SEO Image Renamer Dashboard +
+
+
+ + AI-Powered +
+

Save time! Bulk rename your images individually for better SEO performance

+

Transform your image SEO workflow with AI that analyzes content and generates perfect filenames automatically. No more manual renaming - just upload, enhance, and download.

+ +
+
+ + AI Vision Analysis +
+
+ + Smart Keyword Enhancement +
+
+ + Instant ZIP Download +
+
+ +
+
+ 10k+ + Images Processed +
+
+ 95% + Time Saved +
+
+
+ +
+
+
+
+ +
+

Drop your images here

+

or click to browse files

+ + +
+ Supports: JPG, PNG, WEBP, GIF +
+
+
+
-
+