product Hunt
This commit is contained in:
@@ -1,101 +1,101 @@
|
||||
services:
|
||||
landing:
|
||||
build:
|
||||
context: ./greenlns-landing
|
||||
dockerfile: Dockerfile
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 3000
|
||||
NEXT_PUBLIC_SITE_URL: ${SITE_URL:-https://greenlenspro.com}
|
||||
networks:
|
||||
- greenlens_net
|
||||
healthcheck:
|
||||
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000').then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
api:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: server/Dockerfile
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 3000
|
||||
DATABASE_URL: postgresql://${POSTGRES_USER:-greenlns}:${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}@postgres:5432/${POSTGRES_DB:-greenlns}
|
||||
POSTGRES_HOST: postgres
|
||||
POSTGRES_PORT: 5432
|
||||
POSTGRES_DB: ${POSTGRES_DB:-greenlns}
|
||||
POSTGRES_USER: ${POSTGRES_USER:-greenlns}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
|
||||
MINIO_ENDPOINT: minio
|
||||
MINIO_PORT: 9000
|
||||
MINIO_USE_SSL: "false"
|
||||
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:-greenlns-minio}
|
||||
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:?MINIO_SECRET_KEY is required}
|
||||
MINIO_BUCKET: ${MINIO_BUCKET:-plant-images}
|
||||
MINIO_PUBLIC_URL: ${MINIO_PUBLIC_URL:-https://greenlenspro.com/storage}
|
||||
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
|
||||
OPENAI_SCAN_MODEL: ${OPENAI_SCAN_MODEL:-gpt-5-mini}
|
||||
OPENAI_HEALTH_MODEL: ${OPENAI_HEALTH_MODEL:-gpt-5-mini}
|
||||
REVENUECAT_WEBHOOK_SECRET: ${REVENUECAT_WEBHOOK_SECRET:-}
|
||||
REVENUECAT_PRO_ENTITLEMENT_ID: ${REVENUECAT_PRO_ENTITLEMENT_ID:-pro}
|
||||
JWT_SECRET: ${JWT_SECRET:?JWT_SECRET is required}
|
||||
PLANT_IMPORT_ADMIN_KEY: ${PLANT_IMPORT_ADMIN_KEY:-}
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
minio:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- greenlens_net
|
||||
healthcheck:
|
||||
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/health').then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: ${POSTGRES_DB:-greenlns}
|
||||
POSTGRES_USER: ${POSTGRES_USER:-greenlns}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
|
||||
ports:
|
||||
- "5434:5432"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
networks:
|
||||
- greenlens_net
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
minio:
|
||||
image: minio/minio:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MINIO_ROOT_USER: ${MINIO_ACCESS_KEY:-greenlns-minio}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY:?MINIO_SECRET_KEY is required}
|
||||
command: server /data --console-address ":9001"
|
||||
volumes:
|
||||
- ./minio_data:/data # <-- NEU: Lokaler Ordner statt benanntes Volume!
|
||||
networks:
|
||||
- greenlens_net
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
|
||||
networks:
|
||||
greenlens_net:
|
||||
services:
|
||||
landing:
|
||||
build:
|
||||
context: ./greenlns-landing
|
||||
dockerfile: Dockerfile
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 3000
|
||||
NEXT_PUBLIC_SITE_URL: ${SITE_URL:-https://greenlenspro.com}
|
||||
networks:
|
||||
- greenlens_net
|
||||
healthcheck:
|
||||
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000').then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
api:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: server/Dockerfile
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 3000
|
||||
DATABASE_URL: postgresql://${POSTGRES_USER:-greenlns}:${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}@postgres:5432/${POSTGRES_DB:-greenlns}
|
||||
POSTGRES_HOST: postgres
|
||||
POSTGRES_PORT: 5432
|
||||
POSTGRES_DB: ${POSTGRES_DB:-greenlns}
|
||||
POSTGRES_USER: ${POSTGRES_USER:-greenlns}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
|
||||
MINIO_ENDPOINT: minio
|
||||
MINIO_PORT: 9000
|
||||
MINIO_USE_SSL: "false"
|
||||
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:-greenlns-minio}
|
||||
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:?MINIO_SECRET_KEY is required}
|
||||
MINIO_BUCKET: ${MINIO_BUCKET:-plant-images}
|
||||
MINIO_PUBLIC_URL: ${MINIO_PUBLIC_URL:-https://greenlenspro.com/storage}
|
||||
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
|
||||
OPENAI_SCAN_MODEL: ${OPENAI_SCAN_MODEL:-gpt-5-mini}
|
||||
OPENAI_HEALTH_MODEL: ${OPENAI_HEALTH_MODEL:-gpt-5-mini}
|
||||
REVENUECAT_WEBHOOK_SECRET: ${REVENUECAT_WEBHOOK_SECRET:-}
|
||||
REVENUECAT_PRO_ENTITLEMENT_ID: ${REVENUECAT_PRO_ENTITLEMENT_ID:-pro}
|
||||
JWT_SECRET: ${JWT_SECRET:?JWT_SECRET is required}
|
||||
PLANT_IMPORT_ADMIN_KEY: ${PLANT_IMPORT_ADMIN_KEY:-}
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
minio:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- greenlens_net
|
||||
healthcheck:
|
||||
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/health').then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: ${POSTGRES_DB:-greenlns}
|
||||
POSTGRES_USER: ${POSTGRES_USER:-greenlns}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
|
||||
ports:
|
||||
- "5434:5432"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
networks:
|
||||
- greenlens_net
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
minio:
|
||||
image: minio/minio:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MINIO_ROOT_USER: ${MINIO_ACCESS_KEY:-greenlns-minio}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY:?MINIO_SECRET_KEY is required}
|
||||
command: server /data --console-address ":9001"
|
||||
volumes:
|
||||
- ./minio_data:/data # <-- NEU: Lokaler Ordner statt benanntes Volume!
|
||||
networks:
|
||||
- greenlens_net
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
|
||||
networks:
|
||||
greenlens_net:
|
||||
external: true
|
||||
@@ -1,4 +1,4 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16 4C11 10 6 14 4 18C2 22 4 28 10 28C14 28 16 26 16 26C16 26 18 28 22 28C28 28 30 22 28 18C26 14 21 10 16 4Z" fill="#2A5C3F"/>
|
||||
<path d="M16 4C14 8 13 12 14 16C15 20 18 22 16 26" stroke="#F4F1E8" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16 4C11 10 6 14 4 18C2 22 4 28 10 28C14 28 16 26 16 26C16 26 18 28 22 28C28 28 30 22 28 18C26 14 21 10 16 4Z" fill="#2A5C3F"/>
|
||||
<path d="M16 4C14 8 13 12 14 16C15 20 18 22 16 26" stroke="#F4F1E8" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 356 B After Width: | Height: | Size: 360 B |
@@ -68,23 +68,38 @@ export default function Hero() {
|
||||
{t.hero.desc}
|
||||
</p>
|
||||
|
||||
<div className="hero-actions reveal delay-3">
|
||||
<a href="#cta" className="btn-primary" id="hero-cta-primary">
|
||||
<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||
<path d="M11 20A7 7 0 0 1 9.8 6.1C15.5 5 17 4.48 19 2c1 2 2 4.18 2 8 0 5.5-4.78 10-10 10z" />
|
||||
<path d="M2 21c0-3 1.85-5.36 5.08-6C9.5 14.52 12 13 13 12" />
|
||||
<div className="hero-actions reveal delay-3">
|
||||
<a href="#cta" className="btn-primary" id="hero-cta-primary">
|
||||
<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||
<path d="M11 20A7 7 0 0 1 9.8 6.1C15.5 5 17 4.48 19 2c1 2 2 4.18 2 8 0 5.5-4.78 10-10 10z" />
|
||||
<path d="M2 21c0-3 1.85-5.36 5.08-6C9.5 14.52 12 13 13 12" />
|
||||
</svg>
|
||||
{t.hero.primary}
|
||||
</a>
|
||||
<a href="#features" className="btn-outline" id="hero-cta-secondary">
|
||||
{t.hero.secondary}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Segmentation widget */}
|
||||
<div className="hero-seg reveal delay-4" role="group" aria-label={t.hero.segTitle}>
|
||||
<p className="hero-seg-title">{t.hero.segTitle}</p>
|
||||
<div className="hero-seg-options">
|
||||
<a href="#features" className="btn-outline" id="hero-cta-secondary">
|
||||
{t.hero.secondary}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<a
|
||||
className="product-hunt-badge reveal delay-4"
|
||||
href="https://www.producthunt.com/products/greenlens?embed=true&utm_source=badge-featured&utm_medium=badge&utm_campaign=badge-greenlens"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="GreenLens on Product Hunt"
|
||||
>
|
||||
<img
|
||||
alt="GreenLens - Scan plants, understand care, and grow smarter | Product Hunt"
|
||||
width="250"
|
||||
height="54"
|
||||
src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1160891&theme=light&t=1780336122877"
|
||||
/>
|
||||
</a>
|
||||
|
||||
{/* Segmentation widget */}
|
||||
<div className="hero-seg reveal delay-4" role="group" aria-label={t.hero.segTitle}>
|
||||
<p className="hero-seg-title">{t.hero.segTitle}</p>
|
||||
<div className="hero-seg-options">
|
||||
<button
|
||||
className={`hero-seg-btn${segChoice === 0 ? ' hero-seg-btn--active' : ''}`}
|
||||
onClick={() => handleSeg(0)}
|
||||
@@ -120,11 +135,20 @@ export default function Hero() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style jsx>{`
|
||||
.hero-seg {
|
||||
margin-top: 2rem;
|
||||
background: rgba(244,241,232,0.06);
|
||||
border: 1px solid rgba(244,241,232,0.12);
|
||||
<style jsx>{`
|
||||
.product-hunt-badge {
|
||||
display: block;
|
||||
width: fit-content;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
.product-hunt-badge img {
|
||||
width: 250px;
|
||||
height: 54px;
|
||||
}
|
||||
.hero-seg {
|
||||
margin-top: 2rem;
|
||||
background: rgba(244,241,232,0.06);
|
||||
border: 1px solid rgba(244,241,232,0.12);
|
||||
border-radius: 16px;
|
||||
padding: 1.2rem 1.5rem;
|
||||
max-width: 460px;
|
||||
@@ -178,12 +202,18 @@ export default function Hero() {
|
||||
display: inline-block;
|
||||
transition: border-color 0.2s, background 0.2s;
|
||||
}
|
||||
.hero-seg-btn--active .hero-seg-radio {
|
||||
border-color: var(--green-light);
|
||||
background: var(--green-light);
|
||||
box-shadow: 0 0 0 3px rgba(86,160,116,0.2);
|
||||
}
|
||||
`}</style>
|
||||
.hero-seg-btn--active .hero-seg-radio {
|
||||
border-color: var(--green-light);
|
||||
background: var(--green-light);
|
||||
box-shadow: 0 0 0 3px rgba(86,160,116,0.2);
|
||||
}
|
||||
@media (max-width: 1024px) {
|
||||
.product-hunt-badge {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -528,5 +528,5 @@ Keep the visual language consistent across all slides in the slideshow.
|
||||
- `Slide 4:` Create a premium botanical editorial slide in vertical 9:16. Use the exact GreenLens Botanical Archive design system: digital herbarium aesthetic, high-end editorial archive mood, soft sand background #fbfaf0, layered stone-paper surfaces #f5f4ea and #e4e3d9, deep forest green accents #204e2b and #386641, tertiary earth details only when useful, tactile paper texture, subtle lithography feel, natural daylight, tonal depth instead of borders, no hard divider lines, no clutter, no generic app UI, no harsh shadows, premium magazine composition with centered text and generous whitespace. Scene: brown leaf-tip macro with refined editorial detail. Add large clean overlay text: "3. Brown tips" Add smaller subtext: "Not always dryness." Text style: bold, modern, minimal, high contrast, polished editorial layout with strong editorial hierarchy inspired by Plus Jakarta Sans headlines and Manrope body copy. Text placement: centered, never top-heavy, with balanced spacing, generous safe margins, and a consistent premium botanical magazine feel. Final requirement: the text must stay exactly in the middle.
|
||||
- `Slide 5:` Create a premium botanical editorial slide in vertical 9:16. Use the exact GreenLens Botanical Archive design system: digital herbarium aesthetic, high-end editorial archive mood, soft sand background #fbfaf0, layered stone-paper surfaces #f5f4ea and #e4e3d9, deep forest green accents #204e2b and #386641, tertiary earth details only when useful, tactile paper texture, subtle lithography feel, natural daylight, tonal depth instead of borders, no hard divider lines, no clutter, no generic app UI, no harsh shadows, premium magazine composition with centered text and generous whitespace. Scene: wilted leaf or soft leaf specimen card in clean layout. Add large clean overlay text: "4. Wilting or softness" Add smaller subtext: "Symptoms need context." Text style: bold, modern, minimal, high contrast, polished editorial layout with strong editorial hierarchy inspired by Plus Jakarta Sans headlines and Manrope body copy. Text placement: centered, never top-heavy, with balanced spacing, generous safe margins, and a consistent premium botanical magazine feel. Final requirement: the text must stay exactly in the middle.
|
||||
- `Slide 6:` Create a premium botanical editorial slide in vertical 9:16. Use the exact GreenLens Botanical Archive design system: digital herbarium aesthetic, high-end editorial archive mood, soft sand background #fbfaf0, layered stone-paper surfaces #f5f4ea and #e4e3d9, deep forest green accents #204e2b and #386641, tertiary earth details only when useful, tactile paper texture, subtle lithography feel, natural daylight, tonal depth instead of borders, no hard divider lines, no clutter, no generic app UI, no harsh shadows, premium magazine composition with centered text and generous whitespace. Scene: summary slide with five refined specimen cards in premium hierarchy. Add large clean overlay text: "5. Sudden decline" Add smaller subtext: "It usually starts earlier than you think." Text style: bold, modern, minimal, high contrast, polished editorial layout with strong editorial hierarchy inspired by Plus Jakarta Sans headlines and Manrope body copy. Text placement: centered, never top-heavy, with balanced spacing, generous safe margins, and a consistent premium botanical magazine feel. Final requirement: the text must stay exactly in the middle.
|
||||
- `Slide 7:` Create a premium botanical editorial slide in vertical 9:16. Use the exact GreenLens Botanical Archive design system: digital herbarium aesthetic, high-end editorial archive mood, soft sand background #fbfaf0, layered stone-paper surfaces #f5f4ea and #e4e3d9, deep forest green accents #204e2b and #386641, tertiary earth details only when useful, tactile paper texture, subtle lithography feel, natural daylight, tonal depth instead of borders, no hard divider lines, no clutter, no generic app UI, no harsh shadows, premium magazine composition with centered text and generous whitespace. Scene: final month-close CTA in polished GreenLens style. Add large clean overlay text: "Follow and scan with GreenLens next time." Add smaller subtext: "Month two starts with better diagnosis." Text style: bold, modern, minimal, high contrast, polished editorial layout with strong editorial hierarchy inspired by Plus Jakarta Sans headlines and Manrope body copy. Text placement: centered, never top-heavy, with balanced spacing, generous safe margins, and a consistent premium botanical magazine feel. Final requirement: the text must stay exactly in the middle.
|
||||
|
||||
- `Slide 7:` Create a premium botanical editorial slide in vertical 9:16. Use the exact GreenLens Botanical Archive design system: digital herbarium aesthetic, high-end editorial archive mood, soft sand background #fbfaf0, layered stone-paper surfaces #f5f4ea and #e4e3d9, deep forest green accents #204e2b and #386641, tertiary earth details only when useful, tactile paper texture, subtle lithography feel, natural daylight, tonal depth instead of borders, no hard divider lines, no clutter, no generic app UI, no harsh shadows, premium magazine composition with centered text and generous whitespace. Scene: final month-close CTA in polished GreenLens style. Add large clean overlay text: "Follow and scan with GreenLens next time." Add smaller subtext: "Month two starts with better diagnosis." Text style: bold, modern, minimal, high contrast, polished editorial layout with strong editorial hierarchy inspired by Plus Jakarta Sans headlines and Manrope body copy. Text placement: centered, never top-heavy, with balanced spacing, generous safe margins, and a consistent premium botanical magazine feel. Final requirement: the text must stay exactly in the middle.
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
# Google Search Console SEO Report
|
||||
|
||||
Property: https://greenlenspro.com/
|
||||
Current period: 2026-04-27 to 2026-05-24
|
||||
Previous period: 2026-03-30 to 2026-04-26
|
||||
|
||||
## High-Impression Low-CTR Opportunities
|
||||
|
||||
_No rows matched this rule._
|
||||
|
||||
|
||||
## Striking-Distance Queries
|
||||
|
||||
| Item | Clicks | Impressions | CTR | Position | Click Delta | Impression Delta |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
|
||||
| blumen scanner | 0 | 77 | 0.00% | 7.5 | 0 | 76 |
|
||||
|
||||
|
||||
## Declining Queries
|
||||
|
||||
_No rows matched this rule._
|
||||
|
||||
Reference in New Issue
Block a user