Compare commits
27 Commits
6fa0512688
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| fa95c6e482 | |||
| e116debe07 | |||
| d84cb28d0b | |||
| 0a5b101cf0 | |||
| f524193312 | |||
| 796d66af6b | |||
| 249bfbe0b4 | |||
| bd551d35cc | |||
| 963cfeec93 | |||
| 55951b33b6 | |||
| 7a1a7a7c31 | |||
| 090f68867e | |||
| 3af4ebfa29 | |||
| 151e91fb99 | |||
| a2042041eb | |||
| 12f94c994c | |||
| bd2bb06ed5 | |||
| b292e1a754 | |||
| 5324e82602 | |||
| e564b57cc2 | |||
| 666fe961d0 | |||
| 5099482fde | |||
| 595942f591 | |||
| d27cfb3e1d | |||
| 189362e78d | |||
| 488c63236c | |||
| a2a11b4850 |
@@ -2,3 +2,5 @@
|
|||||||
Thumbs.db
|
Thumbs.db
|
||||||
*.log
|
*.log
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
|
.vercel
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
services:
|
||||||
|
web:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
networks:
|
||||||
|
- coolify
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
|
||||||
|
- "traefik.http.routers.auraajenticai-http.entrypoints=http"
|
||||||
|
- "traefik.http.routers.auraajenticai-http.rule=Host(`auraajenticai.cloud`)"
|
||||||
|
- "traefik.http.routers.auraajenticai-http.middlewares=redirect-to-https"
|
||||||
|
- "traefik.http.routers.auraajenticai-https.entrypoints=https"
|
||||||
|
- "traefik.http.routers.auraajenticai-https.rule=Host(`auraajenticai.cloud`)"
|
||||||
|
- "traefik.http.routers.auraajenticai-https.tls=true"
|
||||||
|
- "traefik.http.routers.auraajenticai-https.tls.certresolver=letsencrypt"
|
||||||
|
- "traefik.http.services.auraajenticai.loadbalancer.server.port=80"
|
||||||
|
- "coolify.managed=true"
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
networks:
|
||||||
|
coolify:
|
||||||
|
external: true
|
||||||
|
|
||||||
+464
@@ -0,0 +1,464 @@
|
|||||||
|
# Aura Agentic AI Cloud — MASTER.md
|
||||||
|
> Single source of truth for the entire infrastructure.
|
||||||
|
> **Last updated:** 2026-05-02 | **VPS:** root@195.35.7.154
|
||||||
|
> ⚠ Credentials are referenced by location only — never pasted here.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. VPS
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|-------|-------|
|
||||||
|
| Provider | Hostinger KVM 2 |
|
||||||
|
| Hostname | srv1540635.hstgr.cloud |
|
||||||
|
| IP | 195.35.7.154 |
|
||||||
|
| OS | Ubuntu 24.04.4 LTS |
|
||||||
|
| Kernel | 6.8.0-110-generic x86-64 |
|
||||||
|
| CPU | 2× vCPU (AMD EPYC, KVM) |
|
||||||
|
| RAM | 7.8 GB (4.2 GB used) |
|
||||||
|
| Disk | 96 GB SSD (36 GB used · 61 GB free · 38%) |
|
||||||
|
| Swap | 4 GB (256 KB used) |
|
||||||
|
| SSH Key | C:\Users\Masud\.ssh\id_ed25519 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Live Services & URLs
|
||||||
|
|
||||||
|
### Coolify Services
|
||||||
|
|
||||||
|
| UUID | Service | URL | Status |
|
||||||
|
|------|---------|-----|--------|
|
||||||
|
| `dhcia3tgcvwmwwvexznjw9gr` | OpenClaw (Primary Agent) | https://agent.auraajenticai.cloud | ✅ Healthy |
|
||||||
|
| `m10jif2sbykx4hd33qf97hc6` | OpenClaw (Secondary / App) | https://app.auraajenticai.cloud | ✅ Healthy |
|
||||||
|
| `j9pqq74tuik1oqd5runx5ncb` | n8n + PostgreSQL + Task Runners | https://n8n.auraajenticai.cloud | ✅ Healthy |
|
||||||
|
| `oqwgsn2lcv3hsk13c6ayj8du` | Gitea + PostgreSQL | https://git.auraajenticai.cloud | ✅ Healthy |
|
||||||
|
|
||||||
|
### Coolify Applications
|
||||||
|
|
||||||
|
| UUID | App | URL | Commit | Status |
|
||||||
|
|------|-----|-----|--------|--------|
|
||||||
|
| `q2kkhvkrpdm6ukj20gv69tuk` | Public Site | https://auraajenticai.cloud | a2a11b4 | ⚠ Pending pull (demo URLs commit not yet applied via Coolify) |
|
||||||
|
| `g5ehpyu7i1azk7f1sv1bhctz` | Central Auth | https://auth.auraajenticai.cloud | 923435bf | ✅ Up |
|
||||||
|
| `m43q7n7vlttrzyxy5v2hkaig` | API Gateway | https://api.auraajenticai.cloud | 8c2045a7 | ✅ Up |
|
||||||
|
| `rdqk1mqe0k8fifs64d7jrzfi` | Client Portal | https://client.auraajenticai.cloud | 03a90610 | ✅ Up |
|
||||||
|
| `j8xnv56xhx6wiaeal2bfiktm` | Billing | https://billing.auraajenticai.cloud | fd9aa384 | ✅ Up |
|
||||||
|
| `zu9kxa9iung19q003ybvcdj3` | Owner Dashboard | https://aura.auraajenticai.cloud | 2b652dba | ✅ Up |
|
||||||
|
| `m6fnc3kfy72tf77anuv4o2f4` | [Internal — UUID subdomain] | https://m6fnc3kfy72tf77anuv4o2f4.auraajenticai.cloud | 3df7af00 | ✅ Up |
|
||||||
|
| `rul16lgnv650ic5kv5wgd51h` | [Staging — no FQDN] | — | 03a90610 | — |
|
||||||
|
| `y22x4z5sxh3854tbmiv98s2j` | [Staging — no FQDN] | — | 03a90610 | — |
|
||||||
|
| `z24up798jcmaf4biwshx2kv5` | [Staging — no FQDN] | — | — | — |
|
||||||
|
|
||||||
|
### Coolify System
|
||||||
|
|
||||||
|
| Service | URL |
|
||||||
|
|---------|-----|
|
||||||
|
| Coolify Dashboard | https://coolify.auraajenticai.cloud (port 8000 local) |
|
||||||
|
| Traefik Dashboard | http://195.35.7.154:8080/dashboard/ ⚠ publicly accessible |
|
||||||
|
| Coolify Version | 4.0.0-beta.473 (Laravel 12.55.1) |
|
||||||
|
| Traefik Version | v3.6 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Complete Tech Stack
|
||||||
|
|
||||||
|
### Infrastructure
|
||||||
|
- **Reverse Proxy:** Traefik v3.6 (HTTP :80 → HTTPS :443, Let's Encrypt cert resolver)
|
||||||
|
- **Orchestration:** Coolify 4.0.0-beta.473 (self-hosted)
|
||||||
|
- **Git:** Gitea v1.26.0 (SSH :22222, HTTP git.auraajenticai.cloud)
|
||||||
|
- **Containers:** Docker + Docker Compose
|
||||||
|
- **OS:** Ubuntu 24.04.4 LTS
|
||||||
|
|
||||||
|
### AI / Agents
|
||||||
|
- **Agent Runtime:** OpenClaw v2026.2.6
|
||||||
|
- **Primary Model:** `openrouter/anthropic/claude-sonnet-4.5`
|
||||||
|
- **AI Provider:** OpenRouter (key in Coolify .env)
|
||||||
|
- **Additional Providers:** Google Gemini (GEMINI_API_KEY set), Groq (GROQ_API_KEY set)
|
||||||
|
- **Channels:** Telegram (@tradingbanglaajentic_bot — long-polling)
|
||||||
|
- **Agent Tools:** read, edit, write, exec, browser, canvas, nodes, cron, message, sessions, web_search, memory
|
||||||
|
|
||||||
|
### Automation
|
||||||
|
- **n8n:** v2.10.2 + Task Runners 2.10.2
|
||||||
|
- **Webhooks:** https://n8n.auraajenticai.cloud/webhook/*
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
- **Public Site:** React 18 + Babel Standalone (CDN, no build step)
|
||||||
|
- **Dashboard/Apps:** SvelteKit 2 + Svelte 5 runes + Tailwind CSS + Vite
|
||||||
|
- **Fonts:** Geist, Geist Mono, Instrument Serif (Google Fonts)
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
- **Auth Service:** Hono + Drizzle ORM + JWT + bcrypt (SvelteKit adapter-node)
|
||||||
|
- **API:** Node.js / Hono
|
||||||
|
- **Language:** TypeScript throughout
|
||||||
|
|
||||||
|
### Databases
|
||||||
|
|
||||||
|
| DB | Container | Engine | Database Name | Network IP |
|
||||||
|
|----|-----------|--------|---------------|------------|
|
||||||
|
| Auth/Main | `ernix50nvuq9ur9jy8na8tgr` | Supabase Postgres 17.4 | `aura_auth` | 10.0.1.7:5432 |
|
||||||
|
| n8n | `postgresql-j9pqq74tuik1oqd5runx5ncb` | Postgres 16-alpine | `n8n` | internal |
|
||||||
|
| Gitea | `postgresql-oqwgsn2lcv3hsk13c6ayj8du` | Postgres 16-alpine | `gitea` | internal |
|
||||||
|
| Coolify | `coolify-db` | Postgres 15-alpine | `coolify` | internal |
|
||||||
|
| Document | `b3bhfd7bocb4pm36uv36rebq` | MongoDB 7 | *(various)* | :27017 |
|
||||||
|
| Cache | `coolify-redis` | Redis 7-alpine | — | internal |
|
||||||
|
|
||||||
|
### aura_auth DB Schema
|
||||||
|
Tables: `users`, `sessions`, `api_keys`, `audit_logs`, `clients`, `projects`, `skills`, `invoices`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. OpenClaw Configuration (Primary — `dhcia3tgcvwmwwvexznjw9gr`)
|
||||||
|
|
||||||
|
```
|
||||||
|
AI Provider: OpenRouter
|
||||||
|
Primary Model: openrouter/anthropic/claude-sonnet-4.5
|
||||||
|
Secondary Model: anthropic/claude-3.5-haiku (env var, secondary/fast not in schema)
|
||||||
|
Fast Model: anthropic/claude-3-haiku (env var)
|
||||||
|
Gateway: ws://127.0.0.1:18789 (loopback, behind nginx on :8080)
|
||||||
|
Auth mode: token (OPENCLAW_GATEWAY_TOKEN)
|
||||||
|
nginx auth: SERVICE_USER_OPENCLAW / SERVICE_PASSWORD_OPENCLAW
|
||||||
|
```
|
||||||
|
|
||||||
|
### Telegram Channel
|
||||||
|
```
|
||||||
|
Bot username: @tradingbanglaajentic_bot
|
||||||
|
Bot ID: 8736690733
|
||||||
|
Mode: long-polling (no webhook URL set)
|
||||||
|
Status: enabled, configured, running
|
||||||
|
Channel name: default
|
||||||
|
Token env: TELEGRAM_BOT_TOKEN (in Coolify service .env)
|
||||||
|
```
|
||||||
|
> To test: send any message to @tradingbanglaajentic_bot on Telegram.
|
||||||
|
> OpenClaw routes it to the main agent session → claude-sonnet-4.5 → reply back.
|
||||||
|
|
||||||
|
### Workspace Files
|
||||||
|
Located at: `/var/lib/docker/volumes/dhcia3tgcvwmwwvexznjw9gr_openclaw-data/_data/workspace/`
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| AGENTS.md | Agent routing rules and capabilities |
|
||||||
|
| BOOTSTRAP.md | Startup context injected every session |
|
||||||
|
| HEARTBEAT.md | Scheduled check-in instructions |
|
||||||
|
| IDENTITY.md | Agent persona and identity rules |
|
||||||
|
| SOUL.md | Core values and behavioral constraints |
|
||||||
|
| TOOLS.md | Available tool descriptions |
|
||||||
|
| USER.md | User (Khondokar) profile and preferences |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. n8n Workflows
|
||||||
|
|
||||||
|
URL: https://n8n.auraajenticai.cloud
|
||||||
|
Contact webhook: `POST https://n8n.auraajenticai.cloud/webhook/contact`
|
||||||
|
|
||||||
|
| Workflow ID | Name |
|
||||||
|
|-------------|------|
|
||||||
|
| `j2JZuCcx38n6hFUa` | skill-code-review |
|
||||||
|
| `ICKS1V8j0rfFRCAM` | skill-analyze |
|
||||||
|
| `tza1cNW0fCZhMd63` | skill-draft-email |
|
||||||
|
| `1FtaniVPKRBKNDwX` | invoice-overdue-checker |
|
||||||
|
| `MeuMN9zV0m2SlPeV` | skill-summarize |
|
||||||
|
| `l2CkZKPgnSyt3Nvt` | Generate AI viral videos (NanoBanana + VEO3 → Blotato) |
|
||||||
|
| `zLqHh5sJ6rT7lyGU` | new-client-deployment |
|
||||||
|
| `sX0KPnr4c0Zh138d` | contact-form |
|
||||||
|
| `8kjRU6YNvtJpcpTD` | client-onboarding |
|
||||||
|
| `m4AB1XlfpvgmNmLF` | api-usage-weekly-report |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Gitea Repositories
|
||||||
|
|
||||||
|
Host: https://git.auraajenticai.cloud
|
||||||
|
User: `khondokar`
|
||||||
|
Token location: `C:\Users\Masud\.claude\projects\C--Users-Masud-aura\memory\project_aura_infrastructure.md`
|
||||||
|
|
||||||
|
| Repo | URL | Last Updated |
|
||||||
|
|------|-----|-------------|
|
||||||
|
| khondokar/auraajenticai-main | git.auraajenticai.cloud/khondokar/auraajenticai-main | 2026-04-27 |
|
||||||
|
|
||||||
|
### GitHub Mirrors (github.com/khondokartowsif171)
|
||||||
|
|
||||||
|
| Repo | GitHub URL | Notes |
|
||||||
|
|------|-----------|-------|
|
||||||
|
| auraajenticai-main | github.com/khondokartowsif171/auraajenticai-main | Main site — latest commit a2a11b4 (demo URLs) |
|
||||||
|
| demo-autostudio-ecomercewebsite | github.com/khondokartowsif171/demo-autostudio-ecomercewebsite | Vite/React ecommerce |
|
||||||
|
| demo-agenticai-website | github.com/khondokartowsif171/demo-agenticai-website | Static HTML portfolio |
|
||||||
|
| demo-cryptotradeanalysis-website | github.com/khondokartowsif171/demo-cryptotradeanalysis-website | React crypto dashboard |
|
||||||
|
| demo-ea-dashboard | github.com/khondokartowsif171/demo-ea-dashboard | Static HTML XAUUSD analyzer |
|
||||||
|
| demo-portfolio-website | github.com/khondokartowsif171/demo-portfolio-website | Vite/React portfolio |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Important File Paths
|
||||||
|
|
||||||
|
### On VPS (root@195.35.7.154)
|
||||||
|
|
||||||
|
```
|
||||||
|
/docs/MASTER.md ← this file
|
||||||
|
|
||||||
|
# Coolify data root
|
||||||
|
/data/coolify/applications/{uuid}/ ← per-app: .env + docker-compose.yaml
|
||||||
|
/data/coolify/services/{uuid}/ ← per-service: .env + docker-compose.yaml
|
||||||
|
/data/coolify/proxy/ ← Traefik dynamic config
|
||||||
|
/data/coolify/ssh/ ← SSH keys for deployments
|
||||||
|
|
||||||
|
# OpenClaw Primary (dhcia3tgcvwmwwvexznjw9gr)
|
||||||
|
/var/lib/docker/volumes/dhcia3tgcvwmwwvexznjw9gr_openclaw-data/_data/
|
||||||
|
.openclaw/openclaw.json ← main config (auto-managed by entrypoint)
|
||||||
|
.openclaw/openclaw.json.bak ← backup (up to 4 kept)
|
||||||
|
.openclaw/credentials/ ← API keys/tokens (never edit directly)
|
||||||
|
.openclaw/agents/main/sessions/sessions.json ← conversation history
|
||||||
|
workspace/ ← agent workspace (AGENTS.md, SOUL.md, etc.)
|
||||||
|
|
||||||
|
# Service .env files (credentials)
|
||||||
|
/data/coolify/services/dhcia3tgcvwmwwvexznjw9gr/.env ← OpenClaw primary
|
||||||
|
/data/coolify/services/m10jif2sbykx4hd33qf97hc6/.env ← OpenClaw secondary
|
||||||
|
/data/coolify/services/j9pqq74tuik1oqd5runx5ncb/.env ← n8n + DB
|
||||||
|
/data/coolify/services/oqwgsn2lcv3hsk13c6ayj8du/.env ← Gitea
|
||||||
|
/data/coolify/applications/{uuid}/.env ← per-app credentials
|
||||||
|
```
|
||||||
|
|
||||||
|
### On Local (Windows — C:\Users\Masud\)
|
||||||
|
|
||||||
|
```
|
||||||
|
.ssh/id_ed25519 ← VPS SSH private key
|
||||||
|
|
||||||
|
.claude/settings.json ← MCP servers + API keys
|
||||||
|
.claude/projects/C--Users-Masud-aura/
|
||||||
|
memory/MEMORY.md ← Memory index
|
||||||
|
memory/project_aura_infrastructure.md ← Infra notes (has tokens)
|
||||||
|
memory/user_khondokar.md ← Founder profile
|
||||||
|
|
||||||
|
aura/
|
||||||
|
auraajenticai-main/ ← Public site source (git)
|
||||||
|
src/data.jsx ← All content + brand data
|
||||||
|
src/nav.jsx ← Navigation + lang toggle
|
||||||
|
src/hero.jsx ← Hero section
|
||||||
|
src/app.jsx ← Root app + lang state
|
||||||
|
src/projects.jsx ← Services grid (6 cards)
|
||||||
|
src/timeline-contact.jsx ← Timeline, contact form, footer
|
||||||
|
src/agent-showcase.jsx ← Agent demo terminal
|
||||||
|
index.html ← Entry point + CSS vars
|
||||||
|
Dockerfile ← nginx:1.27-alpine
|
||||||
|
nginx.conf ← JSX MIME fix + gzip + SPA
|
||||||
|
|
||||||
|
aura-dashboard/ ← Owner Dashboard (SvelteKit 2)
|
||||||
|
n8n-mcp/index.js ← Custom n8n MCP server
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Credentials Locations (NOT values)
|
||||||
|
|
||||||
|
| Credential | Location |
|
||||||
|
|-----------|---------|
|
||||||
|
| VPS root password | Hostinger dashboard (hostinger.com) |
|
||||||
|
| SSH private key | C:\Users\Masud\.ssh\id_ed25519 |
|
||||||
|
| Coolify API token | memory/project_aura_infrastructure.md |
|
||||||
|
| Gitea token (khondokar) | memory/project_aura_infrastructure.md |
|
||||||
|
| OpenRouter API key | /data/coolify/services/dhcia3tgcvwmwwvexznjw9gr/.env → OPENROUTER_API_KEY |
|
||||||
|
| Telegram bot token | /data/coolify/services/dhcia3tgcvwmwwvexznjw9gr/.env → TELEGRAM_BOT_TOKEN |
|
||||||
|
| OpenClaw gateway token | /data/coolify/services/dhcia3tgcvwmwwvexznjw9gr/.env → SERVICE_PASSWORD_64_GATEWAYTOKEN |
|
||||||
|
| OpenClaw nginx auth | /data/coolify/services/dhcia3tgcvwmwwvexznjw9gr/.env → SERVICE_USER/PASSWORD_OPENCLAW |
|
||||||
|
| Gemini API key | /data/coolify/services/dhcia3tgcvwmwwvexznjw9gr/.env → GEMINI_API_KEY |
|
||||||
|
| Groq API key | /data/coolify/services/dhcia3tgcvwmwwvexznjw9gr/.env → GROQ_API_KEY |
|
||||||
|
| n8n admin credentials | /data/coolify/services/j9pqq74tuik1oqd5runx5ncb/.env |
|
||||||
|
| Cloudflare token (n8n) | /data/coolify/services/j9pqq74tuik1oqd5runx5ncb/.env → CLOUDFLARE_TOKEN |
|
||||||
|
| Cloudflare Zone ID | /data/coolify/services/j9pqq74tuik1oqd5runx5ncb/.env → CLOUDFLARE_ZONE_ID |
|
||||||
|
| Supabase DB password | /data/coolify/databases/ernix50nvuq9ur9jy8na8tgr/ or service .env |
|
||||||
|
| MongoDB auth | /data/coolify/databases/b3bhfd7bocb4pm36uv36rebq/ |
|
||||||
|
| JWT secret (shared) | application UUID g5ehpyu7... .env |
|
||||||
|
| aura_auth DB password | application UUIDs (dashboard apps) .env → DATABASE_URL |
|
||||||
|
| Claude Code API key | C:\Users\Masud\.claude\settings.json |
|
||||||
|
| n8n MCP API key | C:\Users\Masud\.claude\settings.json |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. MCP Tools Status
|
||||||
|
|
||||||
|
Configured in: `C:\Users\Masud\.claude\settings.json`
|
||||||
|
|
||||||
|
| MCP Server | Status | Key Tools |
|
||||||
|
|-----------|--------|-----------|
|
||||||
|
| **Coolify** (`04a4937c-...`) | ✅ Connected | d1_database_*, kv_namespace_*, r2_bucket_*, workers_* |
|
||||||
|
| **Gitea** | ✅ Connected | issue_*, pull_request_*, repo_*, user_* |
|
||||||
|
| **n8n** (local node server) | ✅ Connected | list_workflows, execute_workflow, set_workflow_active |
|
||||||
|
| **Google Calendar** (`c00adf3f-...`) | ✅ Connected | list_events, create_event, update_event |
|
||||||
|
| **Gmail** (`0525003c-...`) | ✅ Connected | search_threads, get_thread, create_draft |
|
||||||
|
| **Google Drive** (`d51cbec6-...`) | ✅ Connected | read_file_content, search_files, create_file |
|
||||||
|
| **Vercel** (`39a5e89f-...`) | ✅ Connected | list_projects, list_deployments, get_runtime_logs |
|
||||||
|
| **Claude Preview** | ✅ Available | preview_start, preview_screenshot, preview_eval |
|
||||||
|
| **Computer Use** | ✅ Available | screenshot, left_click, type, key, scroll |
|
||||||
|
| **Claude-in-Chrome** | ✅ Available | navigate, find, read_page, javascript_tool |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Phase Completion Status
|
||||||
|
|
||||||
|
| Phase | Description | Status | Completed |
|
||||||
|
|-------|-------------|--------|-----------|
|
||||||
|
| **0** | VPS audit, health report, swap configured, baseline | ✅ Complete | 2026-04-26 |
|
||||||
|
| **1** | Central Auth Service (SvelteKit + Hono + Drizzle + JWT + bcrypt) | ✅ Complete | 2026-04-26 |
|
||||||
|
| **2** | Owner Dashboard upgrade (sidebar nav, auth gate, Clients CRUD, Brain Console) | ✅ Complete | 2026-04-27 |
|
||||||
|
| **3** | Public site (auraajenticai.cloud) — bilingual EN/BN, 6 services, n8n contact form | ✅ Complete | 2026-04-27 |
|
||||||
|
| **4** | OpenClaw AI Agent — OpenRouter + claude-sonnet-4.5 + model ID fix | ✅ Complete | 2026-04-30 |
|
||||||
|
| **5** | Telegram integration — @tradingbanglaajentic_bot wired to OpenClaw agent | ✅ Complete | 2026-05-01 |
|
||||||
|
| **6** | Client portal (client.auraajenticai.cloud) | ✅ Deployed | — |
|
||||||
|
| **7** | API gateway (api.auraajenticai.cloud) | ✅ Deployed | — |
|
||||||
|
| **8** | Billing service (billing.auraajenticai.cloud) | ✅ Deployed | — |
|
||||||
|
| **9** | n8n automation (10 workflows: skills, contact, onboarding, invoice, AI video) | ✅ Active | — |
|
||||||
|
| **10** | DB backups, monitoring, Traefik hardening, OpenClaw secondary model config | ⏳ Pending | — |
|
||||||
|
| **11** | Demo projects — 5 Vercel deployments, GitHub repos, live demo buttons on public site | ✅ Complete | 2026-05-02 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Pending / Known Gaps
|
||||||
|
|
||||||
|
### High Priority
|
||||||
|
- [ ] **Phase 10:** Automated DB backups (no crontab yet — data loss risk)
|
||||||
|
- [ ] **Traefik dashboard** `:8080` is publicly accessible — restrict to VPN/IP allowlist
|
||||||
|
- [ ] **OpenClaw secondary** (`app.auraajenticai.cloud` — `m10jif2sbykx4hd33qf97hc6`) has no `OPENCLAW_PRIMARY_MODEL` set
|
||||||
|
- [ ] **ANTHROPIC_API_KEY** not set in Owner Dashboard app (required for Brain Console feature)
|
||||||
|
- [ ] **Telegram live test** — send `/start` to @tradingbanglaajentic_bot to verify full round-trip
|
||||||
|
- [ ] **Public site — pull & rebuild** — commit `a2a11b4` (demo URLs) is on GitHub but NOT yet pulled into VPS/Coolify. Run from Hostinger console (see §13)
|
||||||
|
- [ ] **Cloudflare DNS** — `auraajenticai.cloud` A record still points to VPS (195.35.7.154). Vercel domain added (`auraajenticai-main` project) but DNS not yet switched. Need `A → 76.76.21.21` + `www CNAME → cname.vercel-dns.com`. Cloudflare token at `/data/coolify/services/j9pqq74tuik1oqd5runx5ncb/.env → CLOUDFLARE_TOKEN`
|
||||||
|
|
||||||
|
### Medium Priority
|
||||||
|
- [ ] Set up disk/memory monitoring (no alerting configured)
|
||||||
|
- [ ] Configure Traefik rate limiting on API gateway
|
||||||
|
- [ ] Gitea webhook → Coolify auto-deploy (currently triggered manually)
|
||||||
|
- [ ] n8n workflow active/inactive status audit (10 workflows, status not confirmed per-item)
|
||||||
|
- [ ] MongoDB — confirm root credentials and access restriction
|
||||||
|
|
||||||
|
### Low Priority
|
||||||
|
- [ ] `www.auraajenticai.cloud` CNAME redirect to apex
|
||||||
|
- [ ] Log retention policy for OpenClaw gateway logs (`/tmp/openclaw/`)
|
||||||
|
- [ ] Document aura_auth DB migration history
|
||||||
|
- [ ] Add `app.auraajenticai.cloud` source repo to Gitea
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Auth Service Details
|
||||||
|
|
||||||
|
```
|
||||||
|
URL: https://auth.auraajenticai.cloud
|
||||||
|
Container UUID: g5ehpyu7i1azk7f1sv1bhctz
|
||||||
|
Container IP: 10.0.1.12
|
||||||
|
Framework: SvelteKit + Hono + Drizzle + JWT + bcrypt
|
||||||
|
DB: Supabase Postgres 17.4 @ 10.0.1.7:5432/aura_auth
|
||||||
|
Cookies:
|
||||||
|
aura_access → 15-min JWT
|
||||||
|
aura_refresh → 7-day (path /api/auth/refresh)
|
||||||
|
Cookie domain: .auraajenticai.cloud
|
||||||
|
Login URL: https://auth.auraajenticai.cloud/login?next=<URL>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Quick Reference Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# SSH to VPS
|
||||||
|
ssh root@195.35.7.154
|
||||||
|
|
||||||
|
# All containers + status
|
||||||
|
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Image}}'
|
||||||
|
|
||||||
|
# OpenClaw — live logs (primary)
|
||||||
|
docker logs openclaw-dhcia3tgcvwmwwvexznjw9gr --tail=30 -f
|
||||||
|
|
||||||
|
# OpenClaw — test agent
|
||||||
|
docker exec openclaw-dhcia3tgcvwmwwvexznjw9gr \
|
||||||
|
openclaw agent --message "ping" --session-id main --json
|
||||||
|
|
||||||
|
# OpenClaw — Telegram channel status
|
||||||
|
docker exec openclaw-dhcia3tgcvwmwwvexznjw9gr openclaw channels status
|
||||||
|
|
||||||
|
# OpenClaw — restart with updated env
|
||||||
|
cd /data/coolify/services/dhcia3tgcvwmwwvexznjw9gr
|
||||||
|
docker compose up openclaw --force-recreate -d
|
||||||
|
|
||||||
|
# n8n — list workflows
|
||||||
|
docker exec n8n-j9pqq74tuik1oqd5runx5ncb n8n list:workflow
|
||||||
|
|
||||||
|
# Disk usage
|
||||||
|
df -h / && du -sh /var/lib/docker/volumes/*openclaw* /data/coolify/
|
||||||
|
|
||||||
|
# Check all service domains
|
||||||
|
for d in /data/coolify/services/*/; do
|
||||||
|
uuid=$(basename $d)
|
||||||
|
fqdn=$(grep SERVICE_FQDN= $d/.env 2>/dev/null | cut -d= -f2)
|
||||||
|
echo "$uuid: $fqdn"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. Vercel Deployments (2026-05-02)
|
||||||
|
|
||||||
|
Team: `khondokartowsif171s-projects` (`team_9DzpWOXgLFfZkFhpYL0QoKAO`)
|
||||||
|
|
||||||
|
| Project | Live URL | GitHub Repo | Notes |
|
||||||
|
|---------|----------|-------------|-------|
|
||||||
|
| auraajenticai-main | https://auraajenticai-main.vercel.app | khondokartowsif171/auraajenticai-main | Main site — domain `auraajenticai.cloud` added to project, DNS not yet switched |
|
||||||
|
| demo-autostudio-ecomercewebsite | https://demo-autostudio-ecomercewebsite.vercel.app | khondokartowsif171/demo-autostudio-ecomercewebsite | Linked to service card: Website & Webapp Dev |
|
||||||
|
| demo-agenticai-website | https://demo-agenticai-website.vercel.app | khondokartowsif171/demo-agenticai-website | Linked to: AI Agent & Automation |
|
||||||
|
| demo-cryptotradeanalysis-website | https://demo-cryptotradeanalysis-website.vercel.app | khondokartowsif171/demo-cryptotradeanalysis-website | Linked to: Web3 & Blockchain |
|
||||||
|
| ea-dashboard | https://ea-dashboard-blush.vercel.app | khondokartowsif171/demo-ea-dashboard | Linked to: MT5 EA & Trading |
|
||||||
|
| portfolio-website | https://portfolio-website-tan-six-24.vercel.app | khondokartowsif171/demo-portfolio-website | Linked to: Scraping & Data Pipeline |
|
||||||
|
|
||||||
|
### Demo URL → Service Card Mapping (`src/data.jsx`)
|
||||||
|
```
|
||||||
|
web-app-dev → https://demo-autostudio-ecomercewebsite.vercel.app
|
||||||
|
ai-agent-automation → https://demo-agenticai-website.vercel.app
|
||||||
|
web3-blockchain → https://demo-cryptotradeanalysis-website.vercel.app
|
||||||
|
mt5-ea-trading → https://ea-dashboard-blush.vercel.app
|
||||||
|
scraping-data-pipeline → https://portfolio-website-tan-six-24.vercel.app
|
||||||
|
infra-devops → (no demo — button hidden)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. VPS Console Commands (run from Hostinger web console)
|
||||||
|
|
||||||
|
Use when SSH from local machine is blocked by network routing issues.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pull latest main site source from GitHub into Coolify app dir
|
||||||
|
cd /data/coolify/applications/q2kkhvkrpdm6ukj20gv69tuk
|
||||||
|
git remote get-url github 2>/dev/null || \
|
||||||
|
git remote add github https://github.com/khondokartowsif171/auraajenticai-main.git
|
||||||
|
git pull github main
|
||||||
|
|
||||||
|
# Also push to Gitea to keep it in sync
|
||||||
|
git push origin main
|
||||||
|
|
||||||
|
# Trigger Coolify rebuild for the public site
|
||||||
|
curl -s -X POST http://localhost:8000/api/v1/applications/q2kkhvkrpdm6ukj20gv69tuk/start \
|
||||||
|
-H "Authorization: Bearer 6|aura-claude-ceo-2026-secret"
|
||||||
|
|
||||||
|
# Get Cloudflare token + zone ID for DNS update
|
||||||
|
grep -E "CLOUDFLARE_TOKEN|CLOUDFLARE_ZONE_ID" \
|
||||||
|
/data/coolify/services/j9pqq74tuik1oqd5runx5ncb/.env
|
||||||
|
|
||||||
|
# Update Cloudflare DNS: point auraajenticai.cloud to Vercel (76.76.21.21)
|
||||||
|
# Replace ZONE_ID and TOKEN with values from above
|
||||||
|
CF_ZONE="ZONE_ID_HERE"
|
||||||
|
CF_TOKEN="TOKEN_HERE"
|
||||||
|
# Get existing A record ID
|
||||||
|
curl -s "https://api.cloudflare.com/client/v4/zones/$CF_ZONE/dns_records?type=A&name=auraajenticai.cloud" \
|
||||||
|
-H "Authorization: Bearer $CF_TOKEN" | grep -o '"id":"[^"]*"' | head -1
|
||||||
|
# Update it (replace RECORD_ID)
|
||||||
|
curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$CF_ZONE/dns_records/RECORD_ID" \
|
||||||
|
-H "Authorization: Bearer $CF_TOKEN" -H "Content-Type: application/json" \
|
||||||
|
-d '{"type":"A","name":"auraajenticai.cloud","content":"76.76.21.21","ttl":1,"proxied":true}'
|
||||||
|
# Add www CNAME
|
||||||
|
curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE/dns_records" \
|
||||||
|
-H "Authorization: Bearer $CF_TOKEN" -H "Content-Type: application/json" \
|
||||||
|
-d '{"type":"CNAME","name":"www","content":"cname.vercel-dns.com","ttl":1,"proxied":false}'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Generated by Claude (Aura AI Brain) on 2026-05-01. Updated 2026-05-02. Update this file after each infrastructure change.*
|
||||||
+132
-11
@@ -10,9 +10,128 @@
|
|||||||
<meta property="og:description" content="We build AI agents, automation & enterprise systems that work 24/7." />
|
<meta property="og:description" content="We build AI agents, automation & enterprise systems that work 24/7." />
|
||||||
<meta property="og:url" content="https://auraajenticai.cloud" />
|
<meta property="og:url" content="https://auraajenticai.cloud" />
|
||||||
<meta property="og:type" content="website" />
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:image" content="https://auraajenticai.cloud/logo.png" />
|
||||||
<meta name="twitter:card" content="summary_large_image" />
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
<meta name="twitter:title" content="Aura Agentic AI" />
|
<meta name="twitter:title" content="Aura Agentic AI" />
|
||||||
<meta name="twitter:description" content="AI agents, automation & full-stack development — Dhaka, Bangladesh." />
|
<meta name="twitter:description" content="AI agents, automation & full-stack development — Dhaka, Bangladesh." />
|
||||||
|
<meta name="twitter:image" content="https://auraajenticai.cloud/logo.png" />
|
||||||
|
<link rel="canonical" href="https://auraajenticai.cloud" />
|
||||||
|
<link rel="icon" type="image/png" href="logo.png" />
|
||||||
|
<link rel="apple-touch-icon" href="logo.png" />
|
||||||
|
|
||||||
|
<!-- Plausible Analytics -->
|
||||||
|
<script defer data-domain="auraajenticai.cloud" src="https://plausible.io/js/script.js"></script>
|
||||||
|
|
||||||
|
<!-- JSON-LD: Organization -->
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Organization",
|
||||||
|
"name": "Aura Agentic AI",
|
||||||
|
"url": "https://auraajenticai.cloud",
|
||||||
|
"logo": "https://auraajenticai.cloud/logo.png",
|
||||||
|
"image": "https://auraajenticai.cloud/logo.png",
|
||||||
|
"description": "Aura Agentic AI builds custom AI agents, automation pipelines, Web3 infrastructure, MT5 trading systems, data pipelines, and enterprise web apps — based in Dhaka, serving clients worldwide.",
|
||||||
|
"email": "hello@auraajenticai.cloud",
|
||||||
|
"foundingDate": "2017",
|
||||||
|
"areaServed": "Worldwide",
|
||||||
|
"address": {
|
||||||
|
"@type": "PostalAddress",
|
||||||
|
"addressLocality": "Dhaka",
|
||||||
|
"addressCountry": "BD"
|
||||||
|
},
|
||||||
|
"knowsAbout": ["AI agents", "automation", "Web3", "MT5 Expert Advisors", "SvelteKit", "Anthropic Claude", "n8n", "full-stack development"]
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- JSON-LD: Services -->
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "ItemList",
|
||||||
|
"name": "Aura Agentic AI Services",
|
||||||
|
"url": "https://auraajenticai.cloud/#/services",
|
||||||
|
"itemListElement": [
|
||||||
|
{
|
||||||
|
"@type": "ListItem", "position": 1,
|
||||||
|
"item": {
|
||||||
|
"@type": "Service",
|
||||||
|
"name": "Website & Webapp Development",
|
||||||
|
"description": "From landing pages to full SaaS dashboards — pixel-perfect, fast, and built to scale. SvelteKit, Next.js, React, Tailwind.",
|
||||||
|
"provider": { "@type": "Organization", "name": "Aura Agentic AI" },
|
||||||
|
"serviceType": "Web Development", "areaServed": "Worldwide",
|
||||||
|
"url": "https://auraajenticai.cloud/#/services/web-app-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "ListItem", "position": 2,
|
||||||
|
"item": {
|
||||||
|
"@type": "Service",
|
||||||
|
"name": "AI Agent & Automation",
|
||||||
|
"description": "Custom AI agents that triage, decide, and execute — integrated with your tools via n8n, Anthropic, and OpenAI.",
|
||||||
|
"provider": { "@type": "Organization", "name": "Aura Agentic AI" },
|
||||||
|
"serviceType": "AI Development", "areaServed": "Worldwide",
|
||||||
|
"url": "https://auraajenticai.cloud/#/services/ai-agent-automation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "ListItem", "position": 3,
|
||||||
|
"item": {
|
||||||
|
"@type": "Service",
|
||||||
|
"name": "Web3 & Blockchain",
|
||||||
|
"description": "Wallets, smart contracts, multi-chain bridges, and on-chain audit trails. Gasless UX that hides complexity from end users.",
|
||||||
|
"provider": { "@type": "Organization", "name": "Aura Agentic AI" },
|
||||||
|
"serviceType": "Blockchain Development", "areaServed": "Worldwide",
|
||||||
|
"url": "https://auraajenticai.cloud/#/services/web3-blockchain"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "ListItem", "position": 4,
|
||||||
|
"item": {
|
||||||
|
"@type": "Service",
|
||||||
|
"name": "MT5 EA & Trading Automation",
|
||||||
|
"description": "Expert Advisors, real-time P&L dashboards, risk envelopes, and one-click kill switches for MetaTrader 5 platforms.",
|
||||||
|
"provider": { "@type": "Organization", "name": "Aura Agentic AI" },
|
||||||
|
"serviceType": "Trading Automation", "areaServed": "Worldwide",
|
||||||
|
"url": "https://auraajenticai.cloud/#/services/mt5-ea-trading"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "ListItem", "position": 5,
|
||||||
|
"item": {
|
||||||
|
"@type": "Service",
|
||||||
|
"name": "Browser Scraping & Data Pipeline",
|
||||||
|
"description": "Playwright/Puppeteer scrapers, proxy rotation, structured data extraction, and ETL pipelines into your DB or warehouse.",
|
||||||
|
"provider": { "@type": "Organization", "name": "Aura Agentic AI" },
|
||||||
|
"serviceType": "Data Engineering", "areaServed": "Worldwide",
|
||||||
|
"url": "https://auraajenticai.cloud/#/services/scraping-data-pipeline"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "ListItem", "position": 6,
|
||||||
|
"item": {
|
||||||
|
"@type": "Service",
|
||||||
|
"name": "Infrastructure & DevOps",
|
||||||
|
"description": "VPS setup, Docker, Coolify, Traefik, CI/CD pipelines, SSL, monitoring, and zero-downtime deploys on your own cloud.",
|
||||||
|
"provider": { "@type": "Organization", "name": "Aura Agentic AI" },
|
||||||
|
"serviceType": "DevOps", "areaServed": "Worldwide",
|
||||||
|
"url": "https://auraajenticai.cloud/#/services/infra-devops"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "ListItem", "position": 7,
|
||||||
|
"item": {
|
||||||
|
"@type": "Service",
|
||||||
|
"name": "AI-Powered Meta Ads",
|
||||||
|
"description": "AI agents connected directly to Meta's official API — optimizing bids, rotating creatives, and reallocating budgets every 15 minutes.",
|
||||||
|
"provider": { "@type": "Organization", "name": "Aura Agentic AI" },
|
||||||
|
"serviceType": "Digital Advertising", "areaServed": "Worldwide",
|
||||||
|
"url": "https://auraajenticai.cloud/#/services/meta-ads-ai"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
@@ -194,16 +313,18 @@
|
|||||||
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
|
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
|
||||||
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
|
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
<script type="text/babel" src="tweaks-panel.jsx"></script>
|
<script type="text/babel" src="tweaks-panel.jsx?v=5"></script>
|
||||||
<script type="text/babel" src="src/icons.jsx"></script>
|
<script type="text/babel" src="src/icons.jsx?v=5"></script>
|
||||||
<script type="text/babel" src="src/data.jsx"></script>
|
<script type="text/babel" src="src/data.jsx?v=5"></script>
|
||||||
<script type="text/babel" src="src/nav.jsx"></script>
|
<script type="text/babel" src="src/nav.jsx?v=5"></script>
|
||||||
<script type="text/babel" src="src/hero.jsx"></script>
|
<script type="text/babel" src="src/hero.jsx?v=5"></script>
|
||||||
<script type="text/babel" src="src/sections.jsx"></script>
|
<script type="text/babel" src="src/sections.jsx?v=5"></script>
|
||||||
<script type="text/babel" src="src/projects.jsx"></script>
|
<script type="text/babel" src="src/projects.jsx?v=5"></script>
|
||||||
<script type="text/babel" src="src/agent-showcase.jsx"></script>
|
<script type="text/babel" src="src/agent-showcase.jsx?v=5"></script>
|
||||||
<script type="text/babel" src="src/timeline-contact.jsx"></script>
|
<script type="text/babel" src="src/timeline-contact.jsx?v=5"></script>
|
||||||
<script type="text/babel" src="src/command-palette.jsx"></script>
|
<script type="text/babel" src="src/command-palette.jsx?v=5"></script>
|
||||||
<script type="text/babel" src="src/app.jsx"></script>
|
<script type="text/babel" src="src/chatbox.jsx?v=5"></script>
|
||||||
|
<script type="text/babel" src="src/pages.jsx?v=5"></script>
|
||||||
|
<script type="text/babel" src="src/app.jsx?v=5"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+141
-43
@@ -1,4 +1,4 @@
|
|||||||
// Root app: theme + ⌘K + Tweaks + scroll reveal
|
// Root app: theme + ⌘K + Tweaks + hash router
|
||||||
|
|
||||||
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
|
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
|
||||||
"accent": "#7c5cff",
|
"accent": "#7c5cff",
|
||||||
@@ -8,6 +8,27 @@ const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
|
|||||||
"heroHeadlineSuffix": "automation engineer"
|
"heroHeadlineSuffix": "automation engineer"
|
||||||
}/*EDITMODE-END*/;
|
}/*EDITMODE-END*/;
|
||||||
|
|
||||||
|
// Simple hash router — reads window.location.hash
|
||||||
|
function useHashRoute() {
|
||||||
|
const [route, setRoute] = React.useState(() => {
|
||||||
|
const h = window.location.hash;
|
||||||
|
return h.startsWith('#/') ? h.slice(2) : '';
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const onHash = () => {
|
||||||
|
const h = window.location.hash;
|
||||||
|
const r = h.startsWith('#/') ? h.slice(2) : '';
|
||||||
|
setRoute(r);
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
};
|
||||||
|
window.addEventListener('hashchange', onHash);
|
||||||
|
return () => window.removeEventListener('hashchange', onHash);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return route;
|
||||||
|
}
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [theme, setTheme] = React.useState(() => {
|
const [theme, setTheme] = React.useState(() => {
|
||||||
return localStorage.getItem("aura-theme") || "dark";
|
return localStorage.getItem("aura-theme") || "dark";
|
||||||
@@ -15,11 +36,11 @@ function App() {
|
|||||||
const [paletteOpen, setPaletteOpen] = React.useState(false);
|
const [paletteOpen, setPaletteOpen] = React.useState(false);
|
||||||
const [tweaks, setTweak] = window.useTweaks(TWEAK_DEFAULTS);
|
const [tweaks, setTweak] = window.useTweaks(TWEAK_DEFAULTS);
|
||||||
const [lang, setLang] = React.useState(() => localStorage.getItem("aura-lang") || "en");
|
const [lang, setLang] = React.useState(() => localStorage.getItem("aura-lang") || "en");
|
||||||
|
const route = useHashRoute();
|
||||||
|
|
||||||
React.useEffect(() => { localStorage.setItem("aura-lang", lang); }, [lang]);
|
React.useEffect(() => { localStorage.setItem("aura-lang", lang); }, [lang]);
|
||||||
const toggleLang = () => setLang(l => l === "en" ? "bn" : "en");
|
const toggleLang = () => setLang(l => l === "en" ? "bn" : "en");
|
||||||
|
|
||||||
// theme
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
document.documentElement.dataset.theme = theme;
|
document.documentElement.dataset.theme = theme;
|
||||||
localStorage.setItem("aura-theme", theme);
|
localStorage.setItem("aura-theme", theme);
|
||||||
@@ -33,7 +54,6 @@ function App() {
|
|||||||
return () => window.removeEventListener("aura:toggle-theme", onToggle);
|
return () => window.removeEventListener("aura:toggle-theme", onToggle);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// ⌘K
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const onKey = (e) => {
|
const onKey = (e) => {
|
||||||
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
|
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
|
||||||
@@ -45,17 +65,71 @@ function App() {
|
|||||||
return () => window.removeEventListener("keydown", onKey);
|
return () => window.removeEventListener("keydown", onKey);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// tweaks accent vars — apply to :root
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
document.documentElement.style.setProperty("--accent", tweaks.accent);
|
document.documentElement.style.setProperty("--accent", tweaks.accent);
|
||||||
document.documentElement.style.setProperty("--accent-2", tweaks.accent2);
|
document.documentElement.style.setProperty("--accent-2", tweaks.accent2);
|
||||||
document.documentElement.style.setProperty("--accent-glow", tweaks.accent + "59"); // ~35% alpha
|
document.documentElement.style.setProperty("--accent-glow", tweaks.accent + "59");
|
||||||
}, [tweaks.accent, tweaks.accent2]);
|
}, [tweaks.accent, tweaks.accent2]);
|
||||||
|
|
||||||
// density
|
// Dynamic meta tags per route
|
||||||
const density = tweaks.density;
|
React.useEffect(() => {
|
||||||
|
const BASE = 'Aura Agentic AI';
|
||||||
|
const SERVICE_IDS = (PORTFOLIO_DATA.services || []).reduce((m, s) => {
|
||||||
|
m['services/' + s.id] = { title: `${s.name} — ${BASE}`, desc: s.description };
|
||||||
|
return m;
|
||||||
|
}, {});
|
||||||
|
const META = {
|
||||||
|
'': {
|
||||||
|
title: `${BASE} — AI Agents, Automation & Full-Stack Dev`,
|
||||||
|
desc: 'We build custom AI agents, automation pipelines, Web3 infrastructure, MT5 trading systems, and enterprise web apps. Based in Dhaka, serving clients worldwide.',
|
||||||
|
},
|
||||||
|
services: {
|
||||||
|
title: `Services — ${BASE}`,
|
||||||
|
desc: 'AI agents, web development, Web3, MT5 trading automation, data pipelines, DevOps, and Meta Ads AI — 7 production-ready services that ship to production.',
|
||||||
|
},
|
||||||
|
stack: {
|
||||||
|
title: `Tech Stack — ${BASE}`,
|
||||||
|
desc: 'Production-tested stack: React, SvelteKit, Next.js, Node.js, Python, Anthropic Claude, n8n, Solidity, Docker, and more — across 40+ production agents.',
|
||||||
|
},
|
||||||
|
agents: {
|
||||||
|
title: `AI Agents — ${BASE}`,
|
||||||
|
desc: 'Live demos of AI agents built with Anthropic Claude, LangGraph, and n8n. Agents that triage, decide, and execute with tool access, memory, and audit trails.',
|
||||||
|
},
|
||||||
|
timeline: {
|
||||||
|
title: `Experience — ${BASE}`,
|
||||||
|
desc: '7 years of shipping production AI systems — from fintech infrastructure to MT5 trading bots to founding Aura Agentic AI in Dhaka, Bangladesh.',
|
||||||
|
},
|
||||||
|
contact: {
|
||||||
|
title: `Contact — ${BASE}`,
|
||||||
|
desc: 'Get a quote for AI agents, automation, web development, Web3, or MT5 trading systems. Based in Dhaka, Bangladesh. Response within 24 hours.',
|
||||||
|
},
|
||||||
|
pricing: {
|
||||||
|
title: `Pricing — ${BASE}`,
|
||||||
|
desc: 'Transparent pricing for AI agents, automation, and full-stack development. Starter, Growth, and Enterprise tiers. Fixed scope, no surprises.',
|
||||||
|
},
|
||||||
|
blog: {
|
||||||
|
title: `Blog — ${BASE}`,
|
||||||
|
desc: 'Technical deep-dives on building AI agents, MT5 Expert Advisors, Meta Ads automation, and the infrastructure behind Aura Agentic AI.',
|
||||||
|
},
|
||||||
|
products: {
|
||||||
|
title: `Our Products — ${BASE}`,
|
||||||
|
desc: 'GlamourTouch — Aura\'s own live e-commerce store in beauty & lifestyle. A real, production product built on our stack. See exactly what we ship for clients.',
|
||||||
|
},
|
||||||
|
...SERVICE_IDS,
|
||||||
|
};
|
||||||
|
|
||||||
// scroll reveal
|
const m = META[route] || META[''];
|
||||||
|
document.title = m.title;
|
||||||
|
const setMeta = (sel, val) => { const el = document.querySelector(sel); if (el) el.setAttribute('content', val); };
|
||||||
|
setMeta('meta[name="description"]', m.desc);
|
||||||
|
setMeta('meta[property="og:title"]', m.title);
|
||||||
|
setMeta('meta[property="og:description"]', m.desc);
|
||||||
|
setMeta('meta[property="og:url"]', `https://auraajenticai.cloud/${route ? '#/' + route : ''}`);
|
||||||
|
setMeta('meta[name="twitter:title"]', m.title);
|
||||||
|
setMeta('meta[name="twitter:description"]', m.desc);
|
||||||
|
}, [route]);
|
||||||
|
|
||||||
|
// scroll reveal (re-run on route change)
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const els = document.querySelectorAll(".reveal");
|
const els = document.querySelectorAll(".reveal");
|
||||||
const io = new IntersectionObserver(entries => {
|
const io = new IntersectionObserver(entries => {
|
||||||
@@ -63,35 +137,71 @@ function App() {
|
|||||||
}, { threshold: 0.1 });
|
}, { threshold: 0.1 });
|
||||||
els.forEach(el => io.observe(el));
|
els.forEach(el => io.observe(el));
|
||||||
return () => io.disconnect();
|
return () => io.disconnect();
|
||||||
}, []);
|
}, [route]);
|
||||||
|
|
||||||
|
const density = tweaks.density;
|
||||||
const T = window;
|
const T = window;
|
||||||
|
|
||||||
|
const navProps = {
|
||||||
|
onCmdK: () => setPaletteOpen(true),
|
||||||
|
theme, onToggleTheme: toggleTheme,
|
||||||
|
lang, onToggleLang: toggleLang,
|
||||||
|
route,
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderPage = () => {
|
||||||
|
// Service detail pages: #/services/:id
|
||||||
|
if (route.startsWith('services/')) {
|
||||||
|
const id = route.slice('services/'.length);
|
||||||
|
const svc = (PORTFOLIO_DATA.services || []).find(s => s.id === id);
|
||||||
|
if (svc) return <T.ServiceDetailPage svc={svc} />;
|
||||||
|
window.location.hash = '#/services';
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (route) {
|
||||||
|
case 'services':
|
||||||
|
return <T.ServicesPage lang={lang} />;
|
||||||
|
case 'stack':
|
||||||
|
return <T.StackPage />;
|
||||||
|
case 'agents':
|
||||||
|
return <T.AgentsPage />;
|
||||||
|
case 'timeline':
|
||||||
|
return <T.TimelinePage />;
|
||||||
|
case 'contact':
|
||||||
|
return <T.ContactPage lang={lang} />;
|
||||||
|
case 'pricing':
|
||||||
|
return <T.PricingPage />;
|
||||||
|
case 'blog':
|
||||||
|
return <T.BlogPage />;
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<main style={density === "compact" ? { fontSize: 14.5 } : undefined}>
|
||||||
|
<T.Hero headlinePrefix={tweaks.heroHeadlinePrefix} headlineSuffix={tweaks.heroHeadlineSuffix} lang={lang} />
|
||||||
|
<T.About />
|
||||||
|
<T.Skills />
|
||||||
|
<T.Projects />
|
||||||
|
<T.FlagshipProduct />
|
||||||
|
<T.AgentShowcase />
|
||||||
|
<T.Testimonials />
|
||||||
|
<T.Timeline />
|
||||||
|
<T.Contact lang={lang} />
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<T.Nav onCmdK={() => setPaletteOpen(true)} theme={theme} onToggleTheme={toggleTheme} lang={lang} onToggleLang={toggleLang} />
|
<T.Nav {...navProps} />
|
||||||
<main style={density === "compact" ? { fontSize: 14.5 } : undefined}>
|
{renderPage()}
|
||||||
<T.Hero headlinePrefix={tweaks.heroHeadlinePrefix} headlineSuffix={tweaks.heroHeadlineSuffix} lang={lang} />
|
|
||||||
<T.About />
|
|
||||||
<T.Skills />
|
|
||||||
<T.Projects />
|
|
||||||
<T.AgentShowcase />
|
|
||||||
<T.Timeline />
|
|
||||||
<T.Contact lang={lang} />
|
|
||||||
</main>
|
|
||||||
<T.Footer />
|
<T.Footer />
|
||||||
|
|
||||||
<T.CommandPalette open={paletteOpen} onClose={() => setPaletteOpen(false)} />
|
<T.CommandPalette open={paletteOpen} onClose={() => setPaletteOpen(false)} />
|
||||||
|
<T.ChatboxWidget />
|
||||||
|
|
||||||
<window.TweaksPanel title="Tweaks">
|
<window.TweaksPanel title="Tweaks">
|
||||||
<window.TweakSection label="Theme" />
|
<window.TweakSection label="Theme" />
|
||||||
<window.TweakRadio
|
<window.TweakRadio label="Mode" value={theme} onChange={v => setTheme(v)} options={["dark", "light"]} />
|
||||||
label="Mode"
|
|
||||||
value={theme}
|
|
||||||
onChange={v => setTheme(v)}
|
|
||||||
options={["dark", "light"]}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<window.TweakSection label="Accent" />
|
<window.TweakSection label="Accent" />
|
||||||
<window.TweakColor label="Primary" value={tweaks.accent} onChange={v => setTweak("accent", v)} />
|
<window.TweakColor label="Primary" value={tweaks.accent} onChange={v => setTweak("accent", v)} />
|
||||||
<window.TweakColor label="Secondary" value={tweaks.accent2} onChange={v => setTweak("accent2", v)} />
|
<window.TweakColor label="Secondary" value={tweaks.accent2} onChange={v => setTweak("accent2", v)} />
|
||||||
@@ -104,29 +214,17 @@ function App() {
|
|||||||
["Mono", "#ffffff", "#a4a8b3"],
|
["Mono", "#ffffff", "#a4a8b3"],
|
||||||
].map(([name, a, b]) => (
|
].map(([name, a, b]) => (
|
||||||
<button key={name} onClick={() => setTweak({ accent: a, accent2: b })} style={{
|
<button key={name} onClick={() => setTweak({ accent: a, accent2: b })} style={{
|
||||||
padding: "4px 8px",
|
padding: "4px 8px", fontSize: 10.5, background: "transparent",
|
||||||
fontSize: 10.5,
|
border: "1px solid rgba(0,0,0,.12)", borderRadius: 5, color: "#29261b",
|
||||||
background: "transparent",
|
display: "inline-flex", gap: 5, alignItems: "center", cursor: "pointer",
|
||||||
border: "1px solid rgba(0,0,0,.12)",
|
|
||||||
borderRadius: 5,
|
|
||||||
color: "#29261b",
|
|
||||||
display: "inline-flex", gap: 5, alignItems: "center",
|
|
||||||
cursor: "pointer",
|
|
||||||
}}>
|
}}>
|
||||||
<span style={{ width: 9, height: 9, background: a, borderRadius: "50%", display: "inline-block" }} />
|
<span style={{ width: 9, height: 9, background: a, borderRadius: "50%", display: "inline-block" }} />
|
||||||
{name}
|
{name}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<window.TweakSection label="Density" />
|
<window.TweakSection label="Density" />
|
||||||
<window.TweakRadio
|
<window.TweakRadio label="Layout" value={tweaks.density} onChange={v => setTweak("density", v)} options={["compact", "spacious"]} />
|
||||||
label="Layout"
|
|
||||||
value={tweaks.density}
|
|
||||||
onChange={v => setTweak("density", v)}
|
|
||||||
options={["compact", "spacious"]}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<window.TweakSection label="Hero copy" />
|
<window.TweakSection label="Hero copy" />
|
||||||
<window.TweakText label="Prefix" value={tweaks.heroHeadlinePrefix} onChange={v => setTweak("heroHeadlinePrefix", v)} />
|
<window.TweakText label="Prefix" value={tweaks.heroHeadlinePrefix} onChange={v => setTweak("heroHeadlinePrefix", v)} />
|
||||||
<window.TweakText label="Suffix" value={tweaks.heroHeadlineSuffix} onChange={v => setTweak("heroHeadlineSuffix", v)} />
|
<window.TweakText label="Suffix" value={tweaks.heroHeadlineSuffix} onChange={v => setTweak("heroHeadlineSuffix", v)} />
|
||||||
|
|||||||
+220
@@ -0,0 +1,220 @@
|
|||||||
|
// Aura Chatbox Widget
|
||||||
|
// Fetches config from dashboard API, routes messages through backend.
|
||||||
|
|
||||||
|
const DASHBOARD = 'https://aura.auraajenticai.cloud'
|
||||||
|
|
||||||
|
const ChatboxWidget = () => {
|
||||||
|
const [config, setConfig] = React.useState(null)
|
||||||
|
const [open, setOpen] = React.useState(false)
|
||||||
|
const [messages, setMessages] = React.useState([])
|
||||||
|
const [input, setInput] = React.useState('')
|
||||||
|
const [loading, setLoading] = React.useState(false)
|
||||||
|
const [isAfterHours, setIsAfterHours] = React.useState(false)
|
||||||
|
const messagesEndRef = React.useRef(null)
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetch(`${DASHBOARD}/api/public/chatbox`)
|
||||||
|
.then(r => r.ok ? r.json() : null)
|
||||||
|
.then(cfg => {
|
||||||
|
if (!cfg) return
|
||||||
|
setConfig(cfg)
|
||||||
|
if (cfg.isEnabled) {
|
||||||
|
const afterHours = checkAfterHours(cfg.businessHours)
|
||||||
|
setIsAfterHours(afterHours)
|
||||||
|
setMessages([{ role: 'bot', text: cfg.welcomeMessage }])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (open && messagesEndRef.current) {
|
||||||
|
messagesEndRef.current.scrollIntoView({ behavior: 'smooth' })
|
||||||
|
}
|
||||||
|
}, [messages, open])
|
||||||
|
|
||||||
|
function checkAfterHours(businessHours) {
|
||||||
|
if (!businessHours) return false
|
||||||
|
const days = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']
|
||||||
|
const now = new Date()
|
||||||
|
const day = days[now.getDay()]
|
||||||
|
const hours = businessHours[day]
|
||||||
|
if (!hours || hours === 'closed') return true
|
||||||
|
const [open, close] = hours.split('-').map(t => {
|
||||||
|
const [h, m] = t.split(':').map(Number)
|
||||||
|
return h * 60 + (m || 0)
|
||||||
|
})
|
||||||
|
// UTC+6 Bangladesh time
|
||||||
|
const bdMin = ((now.getUTCHours() + 6) % 24) * 60 + now.getUTCMinutes()
|
||||||
|
return bdMin < open || bdMin >= close
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendMessage() {
|
||||||
|
if (!input.trim() || loading || isAfterHours) return
|
||||||
|
const text = input.trim()
|
||||||
|
setInput('')
|
||||||
|
setMessages(prev => [...prev, { role: 'user', text }])
|
||||||
|
setLoading(true)
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${DASHBOARD}/api/public/chatbox/chat`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'content-type': 'application/json' },
|
||||||
|
body: JSON.stringify({ message: text }),
|
||||||
|
})
|
||||||
|
const data = await res.json()
|
||||||
|
setMessages(prev => [...prev, { role: 'bot', text: data.reply || config?.fallbackMessage }])
|
||||||
|
} catch {
|
||||||
|
setMessages(prev => [...prev, { role: 'bot', text: config?.fallbackMessage || "I'll get back to you soon." }])
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onKey(e) {
|
||||||
|
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage() }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config?.isEnabled) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ position: 'fixed', bottom: 24, right: 24, zIndex: 9000, display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 12 }}>
|
||||||
|
{/* Chat window */}
|
||||||
|
{open && (
|
||||||
|
<div style={{
|
||||||
|
width: 340, maxHeight: 480,
|
||||||
|
background: 'var(--bg-elev)',
|
||||||
|
border: '1px solid var(--line-strong)',
|
||||||
|
borderRadius: 16,
|
||||||
|
display: 'flex', flexDirection: 'column',
|
||||||
|
boxShadow: '0 20px 60px rgba(0,0,0,0.4)',
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}>
|
||||||
|
{/* Header */}
|
||||||
|
<div style={{
|
||||||
|
padding: '14px 16px',
|
||||||
|
background: '#0a0a0a',
|
||||||
|
display: 'flex', alignItems: 'center', gap: 10,
|
||||||
|
borderBottom: '1px solid rgba(255,255,255,0.07)',
|
||||||
|
}}>
|
||||||
|
<div style={{
|
||||||
|
width: 32, height: 32, borderRadius: '50%',
|
||||||
|
background: 'linear-gradient(135deg, #c8a961 0%, #1d9e75 100%)',
|
||||||
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
fontSize: 14, flexShrink: 0,
|
||||||
|
}}>⬡</div>
|
||||||
|
<div>
|
||||||
|
<div style={{ fontSize: 13, fontWeight: 600, color: '#f4f5f7' }}>Aura AI</div>
|
||||||
|
<div style={{ fontSize: 11, color: isAfterHours ? '#f59e0b' : '#4ade80', display: 'flex', alignItems: 'center', gap: 4 }}>
|
||||||
|
<span style={{ width: 6, height: 6, borderRadius: '50%', background: 'currentColor', display: 'inline-block' }} />
|
||||||
|
{isAfterHours ? 'After hours' : 'Online'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button onClick={() => setOpen(false)} style={{
|
||||||
|
marginLeft: 'auto', background: 'none', border: 'none',
|
||||||
|
color: '#6b7080', cursor: 'pointer', fontSize: 16, lineHeight: 1,
|
||||||
|
padding: '2px 4px',
|
||||||
|
}}>✕</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Messages */}
|
||||||
|
<div style={{
|
||||||
|
flex: 1, overflowY: 'auto', padding: '12px 14px',
|
||||||
|
display: 'flex', flexDirection: 'column', gap: 8,
|
||||||
|
minHeight: 200, maxHeight: 280,
|
||||||
|
}}>
|
||||||
|
{messages.map((m, i) => (
|
||||||
|
<div key={i} style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: m.role === 'user' ? 'flex-end' : 'flex-start',
|
||||||
|
}}>
|
||||||
|
<div style={{
|
||||||
|
maxWidth: '80%', padding: '8px 12px',
|
||||||
|
borderRadius: m.role === 'user' ? '12px 12px 2px 12px' : '12px 12px 12px 2px',
|
||||||
|
background: m.role === 'user' ? '#c8a961' : 'rgba(255,255,255,0.06)',
|
||||||
|
color: m.role === 'user' ? '#0a0a0a' : '#e0e2e8',
|
||||||
|
fontSize: 13, lineHeight: 1.5,
|
||||||
|
fontWeight: m.role === 'user' ? 500 : 400,
|
||||||
|
}}>{m.text}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{loading && (
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'flex-start' }}>
|
||||||
|
<div style={{
|
||||||
|
padding: '8px 14px', borderRadius: '12px 12px 12px 2px',
|
||||||
|
background: 'rgba(255,255,255,0.06)', color: '#6b7080',
|
||||||
|
fontSize: 13, letterSpacing: 4,
|
||||||
|
}}>•••</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{isAfterHours && (
|
||||||
|
<div style={{
|
||||||
|
padding: '8px 10px', background: 'rgba(245,158,11,0.1)',
|
||||||
|
border: '1px solid rgba(245,158,11,0.2)', borderRadius: 8,
|
||||||
|
fontSize: 12, color: '#f59e0b',
|
||||||
|
}}>
|
||||||
|
{config.afterHoursMessage}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div ref={messagesEndRef} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Input */}
|
||||||
|
<div style={{
|
||||||
|
padding: '10px 12px',
|
||||||
|
borderTop: '1px solid rgba(255,255,255,0.07)',
|
||||||
|
display: 'flex', gap: 8,
|
||||||
|
}}>
|
||||||
|
<input
|
||||||
|
value={input}
|
||||||
|
onChange={e => setInput(e.target.value)}
|
||||||
|
onKeyDown={onKey}
|
||||||
|
disabled={isAfterHours || loading}
|
||||||
|
placeholder={isAfterHours ? 'Chat unavailable right now' : 'Ask me anything…'}
|
||||||
|
style={{
|
||||||
|
flex: 1, padding: '8px 12px',
|
||||||
|
background: 'rgba(255,255,255,0.05)',
|
||||||
|
border: '1px solid rgba(255,255,255,0.1)',
|
||||||
|
borderRadius: 8, color: '#f4f5f7',
|
||||||
|
fontSize: 13,
|
||||||
|
outline: 'none',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={sendMessage}
|
||||||
|
disabled={!input.trim() || loading || isAfterHours}
|
||||||
|
style={{
|
||||||
|
width: 34, height: 34, flexShrink: 0,
|
||||||
|
background: input.trim() && !isAfterHours ? '#c8a961' : 'rgba(255,255,255,0.06)',
|
||||||
|
border: 'none', borderRadius: 8,
|
||||||
|
color: input.trim() && !isAfterHours ? '#0a0a0a' : '#6b7080',
|
||||||
|
cursor: input.trim() && !isAfterHours ? 'pointer' : 'not-allowed',
|
||||||
|
fontSize: 15, display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
transition: 'background 0.15s',
|
||||||
|
}}
|
||||||
|
>↑</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Toggle button */}
|
||||||
|
<button
|
||||||
|
onClick={() => setOpen(o => !o)}
|
||||||
|
title="Chat with Aura AI"
|
||||||
|
style={{
|
||||||
|
width: 52, height: 52, borderRadius: '50%',
|
||||||
|
background: open ? '#0a0a0a' : 'linear-gradient(135deg, #c8a961 0%, #1d9e75 100%)',
|
||||||
|
border: open ? '2px solid rgba(255,255,255,0.15)' : 'none',
|
||||||
|
color: open ? '#f4f5f7' : '#0a0a0a',
|
||||||
|
fontSize: 20, cursor: 'pointer',
|
||||||
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
boxShadow: open ? '0 4px 20px rgba(0,0,0,0.4)' : '0 4px 20px rgba(200,169,97,0.4)',
|
||||||
|
transition: 'all 0.2s',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{open ? '✕' : '⬡'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
window.ChatboxWidget = ChatboxWidget
|
||||||
+243
@@ -83,6 +83,7 @@ const PORTFOLIO_DATA = {
|
|||||||
stack: ["SvelteKit", "Next.js", "React", "Tailwind", "PostgreSQL"],
|
stack: ["SvelteKit", "Next.js", "React", "Tailwind", "PostgreSQL"],
|
||||||
impact: { primary: "50k+", secondary: "daily users served" },
|
impact: { primary: "50k+", secondary: "daily users served" },
|
||||||
color: "violet",
|
color: "violet",
|
||||||
|
demo: "https://glamourstouch.com",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "ai-agent-automation",
|
id: "ai-agent-automation",
|
||||||
@@ -94,6 +95,7 @@ const PORTFOLIO_DATA = {
|
|||||||
stack: ["Anthropic Claude", "n8n", "LangGraph", "Node.js", "Hono"],
|
stack: ["Anthropic Claude", "n8n", "LangGraph", "Node.js", "Hono"],
|
||||||
impact: { primary: "1.2M+", secondary: "agent runs / month" },
|
impact: { primary: "1.2M+", secondary: "agent runs / month" },
|
||||||
color: "cyan",
|
color: "cyan",
|
||||||
|
demo: "https://demo-agenticai-website.vercel.app",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "web3-blockchain",
|
id: "web3-blockchain",
|
||||||
@@ -105,6 +107,7 @@ const PORTFOLIO_DATA = {
|
|||||||
stack: ["Solidity", "viem", "wagmi", "Cloudflare Workers"],
|
stack: ["Solidity", "viem", "wagmi", "Cloudflare Workers"],
|
||||||
impact: { primary: "12 chains", secondary: "EVM + Solana" },
|
impact: { primary: "12 chains", secondary: "EVM + Solana" },
|
||||||
color: "green",
|
color: "green",
|
||||||
|
demo: "https://demo-cryptotradeanalysis-website.vercel.app",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "mt5-ea-trading",
|
id: "mt5-ea-trading",
|
||||||
@@ -116,6 +119,7 @@ const PORTFOLIO_DATA = {
|
|||||||
stack: ["MQL5", "Go", "ClickHouse", "WebSockets", "React"],
|
stack: ["MQL5", "Go", "ClickHouse", "WebSockets", "React"],
|
||||||
impact: { primary: "<80ms", secondary: "tick-to-render latency" },
|
impact: { primary: "<80ms", secondary: "tick-to-render latency" },
|
||||||
color: "amber",
|
color: "amber",
|
||||||
|
demo: "https://ea-dashboard-blush.vercel.app",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "scraping-data-pipeline",
|
id: "scraping-data-pipeline",
|
||||||
@@ -127,6 +131,7 @@ const PORTFOLIO_DATA = {
|
|||||||
stack: ["Playwright", "Puppeteer", "Python", "Airflow", "PostgreSQL"],
|
stack: ["Playwright", "Puppeteer", "Python", "Airflow", "PostgreSQL"],
|
||||||
impact: { primary: "10M+", secondary: "records extracted / month" },
|
impact: { primary: "10M+", secondary: "records extracted / month" },
|
||||||
color: "violet",
|
color: "violet",
|
||||||
|
demo: "https://portfolio-website-tan-six-24.vercel.app",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "infra-devops",
|
id: "infra-devops",
|
||||||
@@ -139,6 +144,203 @@ const PORTFOLIO_DATA = {
|
|||||||
impact: { primary: "99.98%", secondary: "uptime maintained" },
|
impact: { primary: "99.98%", secondary: "uptime maintained" },
|
||||||
color: "cyan",
|
color: "cyan",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "meta-ads-ai",
|
||||||
|
name: "AI-Powered Meta Ads",
|
||||||
|
nameBn: "এআই-চালিত মেটা অ্যাডস",
|
||||||
|
kind: "Growth Service",
|
||||||
|
badge: "NEW · Meta MCP",
|
||||||
|
description:
|
||||||
|
"AI agents connected directly to Meta's official API — optimizing bids, rotating creatives, and reallocating budgets every 15 minutes. Not a human checking ads twice a day. A system that never sleeps.",
|
||||||
|
stack: ["Meta MCP", "Meta Ads API", "n8n", "Claude AI", "Anthropic"],
|
||||||
|
impact: { primary: "5×", secondary: "ROAS vs. manual management" },
|
||||||
|
color: "rose",
|
||||||
|
highlights: [
|
||||||
|
"Real-time bid & budget optimization every 15 min",
|
||||||
|
"100+ ad variants A/B tested simultaneously by AI",
|
||||||
|
"Automated audience expansion & lookalike generation",
|
||||||
|
"Creative fatigue detection — pauses before burnout",
|
||||||
|
"Daily AI-written performance reports to your inbox",
|
||||||
|
"Full Meta API access via official MCP integration",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
testimonials: [
|
||||||
|
{
|
||||||
|
name: "Fahim Rahman",
|
||||||
|
role: "CTO",
|
||||||
|
company: "FinSync BD",
|
||||||
|
service: "AI Agent & Automation",
|
||||||
|
text: "Aura built an n8n + Claude pipeline that processes our reconciliation in 5 seconds — what took our team 2 hours manually. The system hasn't failed once in 6 months.",
|
||||||
|
avatar: "FR",
|
||||||
|
color: "cyan",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Tanvir Hossain",
|
||||||
|
role: "Head of Trading",
|
||||||
|
company: "AlphaEdge Capital",
|
||||||
|
service: "MT5 EA & Trading",
|
||||||
|
text: "The EA outperformed our manual strategy by 340% over 3 months. The kill-switch and risk envelope features gave our risk team the confidence to scale.",
|
||||||
|
avatar: "TH",
|
||||||
|
color: "amber",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Sarah Mitchell",
|
||||||
|
role: "Marketing Director",
|
||||||
|
company: "Growth Labs",
|
||||||
|
service: "AI-Powered Meta Ads",
|
||||||
|
text: "From 1.8× to 5.2× ROAS in 8 weeks. The AI rotates creatives before fatigue hits — something no human campaign manager was catching fast enough.",
|
||||||
|
avatar: "SM",
|
||||||
|
color: "rose",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Arif Chowdhury",
|
||||||
|
role: "Founder",
|
||||||
|
company: "TradeStack",
|
||||||
|
service: "Web3 & Blockchain",
|
||||||
|
text: "Delivered a multi-chain settlement layer with gasless UX in 6 weeks. The on-chain audit trail is what sold us — our compliance team loves it.",
|
||||||
|
avatar: "AC",
|
||||||
|
color: "green",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
pricingTiers: [
|
||||||
|
{
|
||||||
|
name: "Starter",
|
||||||
|
badge: null,
|
||||||
|
price: 499,
|
||||||
|
period: "one-time",
|
||||||
|
description: "For founders who need a fast, professional launch.",
|
||||||
|
color: "violet",
|
||||||
|
features: [
|
||||||
|
"Landing page or single-service site",
|
||||||
|
"Mobile responsive + dark/light mode",
|
||||||
|
"Contact form with real email",
|
||||||
|
"1 API or third-party integration",
|
||||||
|
"Deployed on your domain",
|
||||||
|
"1 revision round",
|
||||||
|
"14-day support window",
|
||||||
|
],
|
||||||
|
cta: "Get Started",
|
||||||
|
href: "mailto:hello@auraajenticai.cloud?subject=Starter Package Enquiry",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Growth",
|
||||||
|
badge: "Most Popular",
|
||||||
|
price: 1499,
|
||||||
|
period: "per project",
|
||||||
|
description: "Full-stack builds for teams ready to scale.",
|
||||||
|
color: "cyan",
|
||||||
|
features: [
|
||||||
|
"Full SaaS dashboard or complex webapp",
|
||||||
|
"AI agent or automation integration",
|
||||||
|
"Admin dashboard + public API",
|
||||||
|
"Auth, billing & role-based access",
|
||||||
|
"CI/CD pipeline + VPS deployment",
|
||||||
|
"3 revision rounds",
|
||||||
|
"60-day support window",
|
||||||
|
"Performance monitoring setup",
|
||||||
|
],
|
||||||
|
cta: "Start Project",
|
||||||
|
href: "mailto:hello@auraajenticai.cloud?subject=Growth Package Enquiry",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Enterprise",
|
||||||
|
badge: "Custom",
|
||||||
|
price: null,
|
||||||
|
period: null,
|
||||||
|
description: "Multi-agent systems, trading infra, and global integrations.",
|
||||||
|
color: "amber",
|
||||||
|
features: [
|
||||||
|
"Custom architecture design",
|
||||||
|
"Multi-agent orchestration system",
|
||||||
|
"MT5 EA or Meta Ads AI system",
|
||||||
|
"Web3 / on-chain settlement rails",
|
||||||
|
"Dedicated VPS infrastructure",
|
||||||
|
"Unlimited revisions",
|
||||||
|
"6-month SLA support",
|
||||||
|
"Monthly performance reports",
|
||||||
|
"Direct engineer access via Slack",
|
||||||
|
],
|
||||||
|
cta: "Book a Call",
|
||||||
|
href: "mailto:hello@auraajenticai.cloud?subject=Enterprise Enquiry",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
blogArticles: [
|
||||||
|
{
|
||||||
|
slug: "how-we-build-mt5-eas",
|
||||||
|
title: "How We Build MT5 Expert Advisors That Don't Blow Accounts",
|
||||||
|
date: "May 20, 2026",
|
||||||
|
readTime: "8 min",
|
||||||
|
category: "Trading",
|
||||||
|
color: "amber",
|
||||||
|
excerpt: "Most EA developers focus on backtests. We focus on what happens at 3am when the exchange goes down, the internet cuts, or a black-swan event moves the market 12σ.",
|
||||||
|
content: [
|
||||||
|
{ type: "p", text: "Most EA developers show you equity curves. We show you failure modes. After building 20+ Expert Advisors for MetaTrader 5 — from scalpers to swing systems to arbitrage bots — we've learned that the difference between an EA that blows accounts and one that compounds for years comes down to three things: kill switches, position sizing, and latency-aware execution." },
|
||||||
|
{ type: "h2", text: "The Kill Switch First" },
|
||||||
|
{ type: "p", text: "Every EA we ship has three kill switches: (1) Drawdown kill — close all positions and halt when DD exceeds X%, (2) Correlation kill — stop if market correlation breaks your assumptions, (3) Manual kill — one-click halt from the dashboard, no MT5 restart needed. Most retail EAs don't have #3. Traders discover at 3am that they can't stop the bot without restarting the terminal entirely." },
|
||||||
|
{ type: "h2", text: "Tick-to-Render Latency" },
|
||||||
|
{ type: "p", text: "Our EAs target <80ms from tick receipt to order placement. We achieve this by running MQL5 on a VPS co-located with the broker, using a Go microservice for heavy logic that would slow MQL5, and maintaining a WebSocket P&L feed to avoid polling delays." },
|
||||||
|
{ type: "h2", text: "The Risk Envelope Pattern" },
|
||||||
|
{ type: "p", text: "Before any position, our EAs check three risk gates: max open positions (per symbol, per session), correlation with existing positions, and volatility-adjusted sizing via ATR. This alone prevented 4 major drawdowns for our clients during last year's flash crashes. We build EAs with the assumption that everything will go wrong. That's why they don't." },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
slug: "meta-mcp-explained",
|
||||||
|
title: "Meta MCP: Why Official API Access Changes Everything for Ad Automation",
|
||||||
|
date: "May 15, 2026",
|
||||||
|
readTime: "6 min",
|
||||||
|
category: "AI Agents",
|
||||||
|
color: "rose",
|
||||||
|
excerpt: "When Meta opened their official MCP integration, it changed what's possible for AI-driven ad management. Here's what it means for agencies and brands.",
|
||||||
|
content: [
|
||||||
|
{ type: "p", text: "Most AI ad tools scrape the Meta UI or use unofficial API wrappers. They break when Meta updates their interface. They get blocked. They violate ToS. Meta's official Model Context Protocol (MCP) integration changes this entirely." },
|
||||||
|
{ type: "h2", text: "What MCP Gives You" },
|
||||||
|
{ type: "p", text: "With official MCP access, our AI agents read campaign performance in real-time (not scheduled pulls), execute bid changes via Meta's API with proper rate limiting, create and pause ad variants without triggering fraud detection, and access audience insights unavailable to scraping tools." },
|
||||||
|
{ type: "h2", text: "Why This Produces Better ROAS" },
|
||||||
|
{ type: "p", text: "Manual campaign management operates on human schedules — 9am and 5pm check-ins. Our AI agents run 15-minute cycles. That's 96 optimization windows per day vs. 2. Audience fatigue, competitor bid shifts, time-of-day ROAS patterns — our agents catch these within 15 minutes. Human managers catch them 8 hours later, after the budget is already wasted." },
|
||||||
|
{ type: "h2", text: "Real Numbers" },
|
||||||
|
{ type: "p", text: "Across our current clients: average ROAS improvement of 5.2× vs. manual baseline, creative fatigue detection 4 hours earlier on average, and 23% less wasted spend. Official MCP access isn't a technical detail. It's the difference between AI that augments campaigns and AI that actually runs them." },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
slug: "why-claude-over-gpt4",
|
||||||
|
title: "Why We Chose Claude Over GPT-4 for Our Agent Infrastructure",
|
||||||
|
date: "May 8, 2026",
|
||||||
|
readTime: "5 min",
|
||||||
|
category: "AI Agents",
|
||||||
|
color: "cyan",
|
||||||
|
excerpt: "After running both models in production for 18 months, here's our honest assessment of why Claude runs every Aura agent.",
|
||||||
|
content: [
|
||||||
|
{ type: "p", text: "We're not tribal about models. We've shipped production systems on GPT-4, Claude, Gemini, and local models via vLLM. After 18 months of production data, Claude runs every Aura agent. Here's why." },
|
||||||
|
{ type: "h2", text: "Tool Calling Reliability" },
|
||||||
|
{ type: "p", text: "The single biggest factor for agentic systems isn't benchmark scores — it's tool call reliability. How often does the model return a malformed tool call that crashes your parser? In our testing across 50k+ agent runs: Claude at 0.3% malformed rate vs GPT-4 at 1.8%. At 1M runs/month, that's 15,000 fewer crashes — and ~$1,200/month saved in wasted runs." },
|
||||||
|
{ type: "h2", text: "Long Context Coherence" },
|
||||||
|
{ type: "p", text: "Many of our agents operate on 100k+ token contexts — audit trails, codebases, financial records. Claude maintains coherence over long windows significantly better. GPT-4 tends to 'forget' early context more often." },
|
||||||
|
{ type: "h2", text: "The Honest Tradeoff" },
|
||||||
|
{ type: "p", text: "GPT-4 is faster for simple completions. Claude is more reliable for complex agentic tasks. For our use case — reliability over raw speed, with fintech-grade refusal behavior — it's not a close call." },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
slug: "n8n-vs-langchain",
|
||||||
|
title: "n8n vs LangChain: When to Use Each (We Use Both)",
|
||||||
|
date: "April 30, 2026",
|
||||||
|
readTime: "7 min",
|
||||||
|
category: "Automation",
|
||||||
|
color: "violet",
|
||||||
|
excerpt: "These are not competing tools. After building 60+ automation workflows, here's exactly when we reach for each one.",
|
||||||
|
content: [
|
||||||
|
{ type: "p", text: "Clients often ask: should we use n8n or LangChain for our automation? The answer is almost always: both. They solve different problems." },
|
||||||
|
{ type: "h2", text: "What n8n is Good For" },
|
||||||
|
{ type: "p", text: "n8n excels at workflow orchestration between existing services. Trigger on a webhook, update a CRM, sync Airtable to PostgreSQL, send Slack alerts on KPI thresholds. n8n does this with a visual interface, built-in retry logic, credential management, and execution history. Building this in code takes 3× longer and is 10× harder to debug. Rule: if the task is 'do X when Y happens, update Z' — use n8n." },
|
||||||
|
{ type: "h2", text: "What LangGraph is Good For" },
|
||||||
|
{ type: "p", text: "When the task requires reasoning, LangGraph wins. Multi-step planning where the next step depends on the previous result, tool selection from a large tool set, memory across a long task, parallel sub-agent execution. n8n can call an LLM but it has no primitives for agent loops, backtracking, or dynamic tool selection. Rule: if the model needs to decide what to do next — use LangGraph." },
|
||||||
|
{ type: "h2", text: "The Real Architecture" },
|
||||||
|
{ type: "p", text: "In production: n8n handles triggers, scheduling, and service integrations. n8n calls a LangGraph agent API when reasoning is needed. LangGraph returns a result, n8n routes it to the right destination. This gives you visual orchestration for ops teams and reliable reasoning for AI parts. Neither tool alone does both well." },
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
agentRun: [
|
agentRun: [
|
||||||
@@ -153,6 +355,47 @@ const PORTFOLIO_DATA = {
|
|||||||
{ t: 5400, kind: "system", text: "session.end :: tokens=8,412 · cost=$0.083 · duration=5.4s" },
|
{ t: 5400, kind: "system", text: "session.end :: tokens=8,412 · cost=$0.083 · duration=5.4s" },
|
||||||
],
|
],
|
||||||
|
|
||||||
|
ownProducts: [
|
||||||
|
{
|
||||||
|
id: "snehalata",
|
||||||
|
name: "Snehalata",
|
||||||
|
tagline: "AI-Powered Clothing Ecosystem",
|
||||||
|
description:
|
||||||
|
"Aura's own multi-vendor clothing marketplace for Bangladesh. Vendors register, list products, and sell — while Gemini AI handles style refinement, virtual try-on, fraud audits, and a 24/7 Bengali-language chat assistant. Built end-to-end on Aura's stack.",
|
||||||
|
url: "https://www.snehalata.com",
|
||||||
|
badge: "Ecosystem",
|
||||||
|
stack: ["SvelteKit", "Supabase", "Gemini AI", "Tailwind", "Vercel"],
|
||||||
|
features: [
|
||||||
|
"Multi-vendor marketplace with AI-powered vendor onboarding audit",
|
||||||
|
"Gemini-powered virtual try-on and style transfer for every product",
|
||||||
|
"24/7 Bengali AI chat assistant for shopping guidance",
|
||||||
|
"Bangladesh district-level vendor & product filtering",
|
||||||
|
"Full order lifecycle: placement → quality check → delivery tracking",
|
||||||
|
"CEO & vendor dashboards with real-time ecosystem stats",
|
||||||
|
],
|
||||||
|
color: "violet",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "glamourstouch",
|
||||||
|
name: "Glamours Touch",
|
||||||
|
tagline: "Beauty & Lifestyle E-Commerce",
|
||||||
|
description:
|
||||||
|
"Aura's own live e-commerce brand in the beauty & lifestyle space. We built it, we run it, we sell from it — end to end. Custom storefront, product catalog, cart, payment integration, and admin dashboard. Not a demo. A real business.",
|
||||||
|
url: "https://glamourstouch.com",
|
||||||
|
badge: "Live Product",
|
||||||
|
stack: ["Next.js", "Stripe", "PostgreSQL", "Tailwind", "Vercel"],
|
||||||
|
features: [
|
||||||
|
"Full product catalog with variants & inventory management",
|
||||||
|
"Stripe + local payment gateway integration",
|
||||||
|
"Mobile-first responsive storefront design",
|
||||||
|
"Admin dashboard for orders, products & analytics",
|
||||||
|
"SEO-optimized product pages with structured data",
|
||||||
|
"Real-time stock tracking & secure checkout flow",
|
||||||
|
],
|
||||||
|
color: "rose",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
experience: [
|
experience: [
|
||||||
{
|
{
|
||||||
year: "2024 — Present",
|
year: "2024 — Present",
|
||||||
|
|||||||
+16
-11
@@ -43,17 +43,22 @@ const Icons = {
|
|||||||
Send: (p) => <Icon {...p}><path d="m22 2-7 20-4-9-9-4 20-7z" /></Icon>,
|
Send: (p) => <Icon {...p}><path d="m22 2-7 20-4-9-9-4 20-7z" /></Icon>,
|
||||||
Globe: (p) => <Icon {...p}><circle cx="12" cy="12" r="10" /><path d="M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" /></Icon>,
|
Globe: (p) => <Icon {...p}><circle cx="12" cy="12" r="10" /><path d="M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" /></Icon>,
|
||||||
Terminal: (p) => <Icon {...p}><path d="m4 17 6-6-6-6M12 19h8" /></Icon>,
|
Terminal: (p) => <Icon {...p}><path d="m4 17 6-6-6-6M12 19h8" /></Icon>,
|
||||||
Logo: ({ size = 28 }) => (
|
Logo: ({ size = 28, full = false }) => (
|
||||||
<svg width={size} height={size} viewBox="0 0 32 32" fill="none">
|
<img
|
||||||
<defs>
|
src="logo.png"
|
||||||
<linearGradient id="logoG" x1="0" y1="0" x2="32" y2="32" gradientUnits="userSpaceOnUse">
|
width={full ? size * 4 : size}
|
||||||
<stop stopColor="var(--accent)" />
|
height={size}
|
||||||
<stop offset="1" stopColor="var(--accent-2)" />
|
alt="Aura Agentic AI"
|
||||||
</linearGradient>
|
style={{
|
||||||
</defs>
|
objectFit: "contain",
|
||||||
<rect x="2" y="2" width="28" height="28" rx="8" fill="none" stroke="url(#logoG)" strokeWidth="1.5" />
|
objectPosition: "center",
|
||||||
<path d="M10 22 L16 8 L22 22 M12.5 17 H19.5" stroke="url(#logoG)" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" fill="none" />
|
borderRadius: full ? 0 : 4,
|
||||||
</svg>
|
mixBlendMode: "screen",
|
||||||
|
display: "block",
|
||||||
|
flexShrink: 0,
|
||||||
|
}}
|
||||||
|
onError={e => { e.currentTarget.style.display = "none"; }}
|
||||||
|
/>
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+31
-22
@@ -1,7 +1,16 @@
|
|||||||
// Top nav — minimal, sticky, blurred — mobile-responsive
|
// Top nav — minimal, sticky, blurred — mobile-responsive
|
||||||
const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang }) => {
|
const NAV_LINKS = [
|
||||||
|
{ id: "services", label: "Services", url: "#/services" },
|
||||||
|
{ id: "pricing", label: "Pricing", url: "#/pricing" },
|
||||||
|
{ id: "blog", label: "Blog", url: "#/blog" },
|
||||||
|
{ id: "timeline", label: "Timeline", url: "#/timeline" },
|
||||||
|
{ id: "contact", label: "Contact", url: "#/contact" },
|
||||||
|
]
|
||||||
|
|
||||||
|
const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }) => {
|
||||||
const [scrolled, setScrolled] = React.useState(false);
|
const [scrolled, setScrolled] = React.useState(false);
|
||||||
const [menuOpen, setMenuOpen] = React.useState(false);
|
const [menuOpen, setMenuOpen] = React.useState(false);
|
||||||
|
const links = NAV_LINKS;
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const onScroll = () => setScrolled(window.scrollY > 12);
|
const onScroll = () => setScrolled(window.scrollY > 12);
|
||||||
@@ -9,14 +18,6 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang }) => {
|
|||||||
return () => window.removeEventListener("scroll", onScroll);
|
return () => window.removeEventListener("scroll", onScroll);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const links = [
|
|
||||||
{ id: "work", label: "Services" },
|
|
||||||
{ id: "stack", label: "Stack" },
|
|
||||||
{ id: "agents", label: "Agents" },
|
|
||||||
{ id: "timeline", label: "Timeline" },
|
|
||||||
{ id: "contact", label: "Contact" },
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<nav style={{
|
<nav style={{
|
||||||
@@ -54,18 +55,26 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang }) => {
|
|||||||
|
|
||||||
{/* Center links — hidden on mobile */}
|
{/* Center links — hidden on mobile */}
|
||||||
<div style={{ display: "flex", gap: 4, alignItems: "center" }} className="nav-links">
|
<div style={{ display: "flex", gap: 4, alignItems: "center" }} className="nav-links">
|
||||||
{links.map(l => (
|
{links.map(l => {
|
||||||
<a key={l.id} href={"#" + l.id} style={{
|
const isActive = route === l.id;
|
||||||
fontSize: 13.5,
|
return (
|
||||||
color: "var(--text-dim)",
|
<a key={l.id} href={l.url || `#/${l.id}`} style={{
|
||||||
padding: "6px 12px",
|
fontSize: 13.5,
|
||||||
borderRadius: 8,
|
color: isActive ? "var(--text)" : "var(--text-dim)",
|
||||||
transition: "all 0.15s",
|
background: isActive ? "var(--line)" : "transparent",
|
||||||
}}
|
padding: "6px 12px",
|
||||||
onMouseEnter={e => { e.currentTarget.style.color = "var(--text)"; e.currentTarget.style.background = "var(--line)"; }}
|
borderRadius: 8,
|
||||||
onMouseLeave={e => { e.currentTarget.style.color = "var(--text-dim)"; e.currentTarget.style.background = "transparent"; }}
|
transition: "all 0.15s",
|
||||||
>{l.label}</a>
|
fontWeight: isActive ? 500 : 400,
|
||||||
))}
|
}}
|
||||||
|
onMouseEnter={e => { e.currentTarget.style.color = "var(--text)"; e.currentTarget.style.background = "var(--line)"; }}
|
||||||
|
onMouseLeave={e => {
|
||||||
|
e.currentTarget.style.color = isActive ? "var(--text)" : "var(--text-dim)";
|
||||||
|
e.currentTarget.style.background = isActive ? "var(--line)" : "transparent";
|
||||||
|
}}
|
||||||
|
>{l.label}</a>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right — desktop */}
|
{/* Right — desktop */}
|
||||||
@@ -216,7 +225,7 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang }) => {
|
|||||||
{links.map(l => (
|
{links.map(l => (
|
||||||
<a
|
<a
|
||||||
key={l.id}
|
key={l.id}
|
||||||
href={"#" + l.id}
|
href={l.url || `#${l.id}`}
|
||||||
onClick={() => setMenuOpen(false)}
|
onClick={() => setMenuOpen(false)}
|
||||||
style={{
|
style={{
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
|
|||||||
+1082
File diff suppressed because it is too large
Load Diff
+78
-21
@@ -144,24 +144,39 @@ const ProjectCard = ({ p }) => {
|
|||||||
}}>{p.impact.secondary}</div>
|
}}>{p.impact.secondary}</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: "flex", gap: 6 }}>
|
<div style={{ display: "flex", gap: 6 }}>
|
||||||
<button style={{
|
{p.gitRepo ? (
|
||||||
width: 34, height: 34,
|
<a href={p.gitRepo} target="_blank" rel="noopener noreferrer" style={{
|
||||||
display: "grid", placeItems: "center",
|
width: 34, height: 34,
|
||||||
background: "transparent",
|
display: "grid", placeItems: "center",
|
||||||
border: "1px solid var(--line)",
|
background: "transparent",
|
||||||
borderRadius: 8,
|
border: "1px solid var(--line)",
|
||||||
color: "var(--text-dim)",
|
borderRadius: 8,
|
||||||
}} title="GitHub"><Icons.Github size={14} /></button>
|
color: "var(--text-dim)",
|
||||||
<button style={{
|
textDecoration: "none",
|
||||||
padding: "0 14px", height: 34,
|
}} title="View repository"><Icons.Github size={14} /></a>
|
||||||
display: "inline-flex", alignItems: "center", gap: 6,
|
) : (
|
||||||
background: "var(--text)",
|
<span style={{
|
||||||
color: "var(--bg)",
|
width: 34, height: 34,
|
||||||
border: "none",
|
display: "grid", placeItems: "center",
|
||||||
borderRadius: 8,
|
border: "1px solid var(--line)",
|
||||||
fontSize: 13,
|
borderRadius: 8,
|
||||||
fontWeight: 500,
|
color: "var(--line-strong)",
|
||||||
}}>Demo <Icons.ArrowUpRight size={12} /></button>
|
}} title="No repository linked"><Icons.Github size={14} /></span>
|
||||||
|
)}
|
||||||
|
{p.demo && (
|
||||||
|
<a href={p.demo} target="_blank" rel="noopener noreferrer" style={{
|
||||||
|
padding: "0 14px", height: 34,
|
||||||
|
display: "inline-flex", alignItems: "center", gap: 6,
|
||||||
|
background: "var(--text)",
|
||||||
|
color: "var(--bg)",
|
||||||
|
border: "none",
|
||||||
|
borderRadius: 8,
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: 500,
|
||||||
|
textDecoration: "none",
|
||||||
|
cursor: "pointer",
|
||||||
|
}}>Demo <Icons.ArrowUpRight size={12} /></a>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -298,15 +313,57 @@ const ProjectVisual = ({ id, tint, hover }) => {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Merge Dashboard API data (demo/repo links) into local static data
|
||||||
|
// Public API returns: { id, name, demo, repo, position }
|
||||||
|
function mergeServices(local, apiData) {
|
||||||
|
const byId = {};
|
||||||
|
apiData.forEach(s => { byId[s.id] = s; });
|
||||||
|
return local.map(s => {
|
||||||
|
const api = byId[s.id];
|
||||||
|
if (!api) return s;
|
||||||
|
return {
|
||||||
|
...s,
|
||||||
|
demo: api.demo || s.demo || null,
|
||||||
|
gitRepo: api.repo || null,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const Projects = () => {
|
const Projects = () => {
|
||||||
const D = PORTFOLIO_DATA;
|
const D = PORTFOLIO_DATA;
|
||||||
|
const [services, setServices] = React.useState(D.services);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const CACHE_KEY = 'aura_svc_v3';
|
||||||
|
const CACHE_TTL = 5 * 60 * 1000;
|
||||||
|
const cached = sessionStorage.getItem(CACHE_KEY);
|
||||||
|
if (cached) {
|
||||||
|
try {
|
||||||
|
const { data, ts } = JSON.parse(cached);
|
||||||
|
if (Date.now() - ts < CACHE_TTL && data.length > 0) {
|
||||||
|
setServices(mergeServices(D.services, data));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
fetch('https://aura.auraajenticai.cloud/api/public/services', { signal: AbortSignal.timeout(3000) })
|
||||||
|
.then(r => r.ok ? r.json() : null)
|
||||||
|
.then(data => {
|
||||||
|
if (Array.isArray(data) && data.length > 0) {
|
||||||
|
sessionStorage.setItem(CACHE_KEY, JSON.stringify({ data, ts: Date.now() }));
|
||||||
|
setServices(mergeServices(D.services, data));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="work" style={{ padding: "120px 0", borderTop: "1px solid var(--line)" }}>
|
<section id="work" style={{ padding: "120px 0", borderTop: "1px solid var(--line)" }}>
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<SectionHeader
|
<SectionHeader
|
||||||
eyebrow="What we build"
|
eyebrow="What we build"
|
||||||
num="03 / 06"
|
num={`03 / 06`}
|
||||||
title={<>Six services. One team. All <span style={{ fontFamily: "var(--font-serif)", fontStyle: "italic", fontWeight: 400 }}>production-grade</span>.</>}
|
title={<>{['Zero','One','Two','Three','Four','Five','Six','Seven','Eight','Nine'][services.length] || services.length} service{services.length !== 1 ? 's' : ''}. One team. All <span style={{ fontFamily: "var(--font-serif)", fontStyle: "italic", fontWeight: 400 }}>production-grade</span>.</>}
|
||||||
sub="From landing pages to AI agent runtimes — we take your idea from spec to live deployment."
|
sub="From landing pages to AI agent runtimes — we take your idea from spec to live deployment."
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -315,7 +372,7 @@ const Projects = () => {
|
|||||||
gridTemplateColumns: "repeat(2, 1fr)",
|
gridTemplateColumns: "repeat(2, 1fr)",
|
||||||
gap: 16,
|
gap: 16,
|
||||||
}} className="proj-grid">
|
}} className="proj-grid">
|
||||||
{D.services.map(p => <ProjectCard key={p.id} p={p} />)}
|
{services.map(p => <ProjectCard key={p.id} p={p} />)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>{`
|
<style>{`
|
||||||
|
|||||||
@@ -237,6 +237,308 @@ const Skills = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ─── TESTIMONIALS ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
const TESTIMONIAL_COLORS = {
|
||||||
|
cyan: { bg: "rgba(0,212,255,0.07)", border: "rgba(0,212,255,0.20)", text: "#67e8f9" },
|
||||||
|
amber: { bg: "rgba(251,191,36,0.08)", border: "rgba(251,191,36,0.22)", text: "#fde68a" },
|
||||||
|
rose: { bg: "rgba(244,63,94,0.08)", border: "rgba(244,63,94,0.22)", text: "#fda4af" },
|
||||||
|
green: { bg: "rgba(34,197,94,0.07)", border: "rgba(34,197,94,0.20)", text: "#86efac" },
|
||||||
|
violet: { bg: "rgba(124,92,255,0.08)", border: "rgba(124,92,255,0.22)", text: "#a78bfa" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const Testimonials = () => {
|
||||||
|
const D = PORTFOLIO_DATA;
|
||||||
|
return (
|
||||||
|
<section style={{ padding: "120px 0", borderTop: "1px solid var(--line)" }}>
|
||||||
|
<div className="container">
|
||||||
|
<SectionHeader
|
||||||
|
eyebrow="Client Results"
|
||||||
|
num="05 / 06"
|
||||||
|
title={<>What clients say after we <span style={{ fontFamily: "var(--font-serif)", fontStyle: "italic", fontWeight: 400 }}>ship</span></>}
|
||||||
|
sub="Real projects. Real metrics. No stock-photo testimonials."
|
||||||
|
/>
|
||||||
|
<div style={{
|
||||||
|
display: "grid",
|
||||||
|
gridTemplateColumns: "repeat(2, 1fr)",
|
||||||
|
gap: 20,
|
||||||
|
}} className="testimonials-grid">
|
||||||
|
{D.testimonials.map((t, i) => {
|
||||||
|
const c = TESTIMONIAL_COLORS[t.color] || TESTIMONIAL_COLORS.cyan;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className="reveal panel"
|
||||||
|
style={{
|
||||||
|
padding: "28px 32px",
|
||||||
|
border: `1px solid ${c.border}`,
|
||||||
|
background: c.bg,
|
||||||
|
borderRadius: "var(--radius)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{
|
||||||
|
fontSize: 32, lineHeight: 1, color: c.text,
|
||||||
|
marginBottom: 16, fontFamily: "var(--font-serif)",
|
||||||
|
}}>"</div>
|
||||||
|
<p style={{
|
||||||
|
margin: "0 0 24px", fontSize: 15.5,
|
||||||
|
lineHeight: 1.65, color: "var(--text)",
|
||||||
|
}}>{t.text}</p>
|
||||||
|
<div style={{ display: "flex", alignItems: "center", gap: 12 }}>
|
||||||
|
<div style={{
|
||||||
|
width: 40, height: 40, borderRadius: "50%",
|
||||||
|
background: c.bg, border: `1px solid ${c.border}`,
|
||||||
|
display: "grid", placeItems: "center",
|
||||||
|
fontSize: 12, fontWeight: 600, color: c.text,
|
||||||
|
fontFamily: "var(--font-mono)", flexShrink: 0,
|
||||||
|
}}>{t.avatar}</div>
|
||||||
|
<div>
|
||||||
|
<div style={{ fontSize: 14, fontWeight: 500 }}>{t.name}</div>
|
||||||
|
<div style={{ fontSize: 12, color: "var(--text-faint)", fontFamily: "var(--font-mono)" }}>
|
||||||
|
{t.role} · {t.company}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ marginLeft: "auto" }}>
|
||||||
|
<span style={{
|
||||||
|
fontSize: 10.5, padding: "3px 9px", borderRadius: 999,
|
||||||
|
background: "var(--bg-elev)", border: "1px solid var(--line)",
|
||||||
|
color: "var(--text-faint)", fontFamily: "var(--font-mono)",
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
}}>{t.service}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<style>{`
|
||||||
|
@media (max-width: 720px) {
|
||||||
|
.testimonials-grid { grid-template-columns: 1fr !important; }
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ─── FLAGSHIP PRODUCTS ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
const PRODUCT_COLORS = {
|
||||||
|
rose: { bg: "rgba(244,63,94,0.08)", border: "rgba(244,63,94,0.22)", text: "#fda4af" },
|
||||||
|
violet: { bg: "rgba(124,92,255,0.08)", border: "rgba(124,92,255,0.22)", text: "#a78bfa" },
|
||||||
|
cyan: { bg: "rgba(0,212,255,0.07)", border: "rgba(0,212,255,0.20)", text: "#67e8f9" },
|
||||||
|
green: { bg: "rgba(34,197,94,0.07)", border: "rgba(34,197,94,0.20)", text: "#86efac" },
|
||||||
|
amber: { bg: "rgba(251,191,36,0.08)", border: "rgba(251,191,36,0.22)", text: "#fde68a" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const OwnProductCard = ({ product }) => {
|
||||||
|
const c = PRODUCT_COLORS[product.color] || PRODUCT_COLORS.violet;
|
||||||
|
return (
|
||||||
|
<div className="reveal" style={{
|
||||||
|
borderRadius: "var(--radius)", border: `1px solid ${c.border}`,
|
||||||
|
background: "var(--bg-card)", overflow: "hidden",
|
||||||
|
display: "flex", flexDirection: "column",
|
||||||
|
}}>
|
||||||
|
{/* Card header */}
|
||||||
|
<div style={{
|
||||||
|
background: `linear-gradient(145deg, ${c.bg.replace('0.08', '0.13')} 0%, rgba(0,0,0,0) 100%)`,
|
||||||
|
padding: "36px 36px 28px", borderBottom: `1px solid ${c.border}`,
|
||||||
|
position: "relative", overflow: "hidden",
|
||||||
|
}}>
|
||||||
|
<div style={{
|
||||||
|
position: "absolute", top: -50, right: -50, width: 180, height: 180,
|
||||||
|
borderRadius: "50%", pointerEvents: "none",
|
||||||
|
background: `radial-gradient(circle, ${c.bg.replace('0.08', '0.18')} 0%, transparent 65%)`,
|
||||||
|
}} />
|
||||||
|
|
||||||
|
{/* Badges */}
|
||||||
|
<div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 20, position: "relative" }}>
|
||||||
|
<span style={{
|
||||||
|
display: "inline-flex", alignItems: "center", gap: 5,
|
||||||
|
background: "rgba(34,197,94,0.10)", border: "1px solid rgba(34,197,94,0.28)",
|
||||||
|
borderRadius: 999, padding: "3px 10px 3px 7px",
|
||||||
|
fontSize: 10, fontFamily: "var(--font-mono)", color: "#4ade80", fontWeight: 600,
|
||||||
|
}}>
|
||||||
|
<span style={{
|
||||||
|
width: 5, height: 5, borderRadius: "50%", background: "#22c55e",
|
||||||
|
boxShadow: "0 0 6px rgba(34,197,94,0.9)", display: "inline-block",
|
||||||
|
animation: "glamour-pulse 2s ease-in-out infinite",
|
||||||
|
}} />
|
||||||
|
LIVE
|
||||||
|
</span>
|
||||||
|
{product.badge && (
|
||||||
|
<span style={{
|
||||||
|
fontSize: 10, fontFamily: "var(--font-mono)", color: c.text,
|
||||||
|
padding: "3px 9px", borderRadius: 999,
|
||||||
|
background: c.bg, border: `1px solid ${c.border}`,
|
||||||
|
}}>{product.badge}</span>
|
||||||
|
)}
|
||||||
|
<span style={{
|
||||||
|
fontSize: 10, color: "var(--text-faint)", fontFamily: "var(--font-mono)",
|
||||||
|
padding: "3px 9px", borderRadius: 999,
|
||||||
|
background: "var(--bg-elev)", border: "1px solid var(--line)",
|
||||||
|
marginLeft: "auto",
|
||||||
|
}}>Built by Aura</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Brand name */}
|
||||||
|
<div style={{ position: "relative" }}>
|
||||||
|
<h3 style={{
|
||||||
|
margin: "0 0 6px",
|
||||||
|
fontSize: "clamp(22px, 2.5vw, 32px)",
|
||||||
|
fontWeight: 700, fontFamily: "var(--font-serif)", fontStyle: "italic",
|
||||||
|
letterSpacing: "-0.01em",
|
||||||
|
background: `linear-gradient(135deg, ${c.text} 0%, #fff 80%)`,
|
||||||
|
WebkitBackgroundClip: "text", WebkitTextFillColor: "transparent",
|
||||||
|
backgroundClip: "text",
|
||||||
|
}}>{product.name}</h3>
|
||||||
|
<p style={{
|
||||||
|
margin: 0, fontSize: 11.5, color: "var(--text-faint)",
|
||||||
|
fontFamily: "var(--font-mono)", letterSpacing: "0.1em",
|
||||||
|
textTransform: "uppercase",
|
||||||
|
}}>{product.tagline}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Card body */}
|
||||||
|
<div style={{ padding: "28px 36px 32px", display: "flex", flexDirection: "column", gap: 20, flex: 1 }}>
|
||||||
|
<p style={{ margin: 0, fontSize: 14.5, lineHeight: 1.65, color: "var(--text-dim)" }}>
|
||||||
|
{product.description}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Top 3 features */}
|
||||||
|
<div style={{ display: "grid", gap: 8 }}>
|
||||||
|
{product.features.slice(0, 3).map((f, i) => (
|
||||||
|
<div key={i} style={{ display: "flex", gap: 10, fontSize: 13, color: "var(--text)" }}>
|
||||||
|
<span style={{ color: c.text, flexShrink: 0, fontSize: 11, marginTop: 2 }}>✓</span>
|
||||||
|
{f}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{product.features.length > 3 && (
|
||||||
|
<div style={{ fontSize: 12, color: "var(--text-faint)", fontFamily: "var(--font-mono)", paddingLeft: 21 }}>
|
||||||
|
+{product.features.length - 3} more capabilities
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Stack */}
|
||||||
|
<div style={{ display: "flex", flexWrap: "wrap", gap: 5 }}>
|
||||||
|
{product.stack.map(s => (
|
||||||
|
<span key={s} style={{
|
||||||
|
fontSize: 11, padding: "3px 9px", borderRadius: 5,
|
||||||
|
background: c.bg, border: `1px solid ${c.border}`,
|
||||||
|
color: c.text, fontFamily: "var(--font-mono)",
|
||||||
|
}}>{s}</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* CTAs */}
|
||||||
|
<div style={{ display: "flex", gap: 10, marginTop: "auto", paddingTop: 4 }}>
|
||||||
|
<a
|
||||||
|
href={product.url} target="_blank" rel="noopener noreferrer"
|
||||||
|
style={{
|
||||||
|
display: "inline-flex", alignItems: "center", gap: 7,
|
||||||
|
padding: "10px 20px",
|
||||||
|
background: `linear-gradient(135deg, ${c.bg.replace('0.08','0.6')}, ${c.bg.replace('0.08','0.4')})`,
|
||||||
|
color: "#fff", borderRadius: 9, fontSize: 13, fontWeight: 600,
|
||||||
|
textDecoration: "none", border: `1px solid ${c.border}`,
|
||||||
|
transition: "opacity 0.15s",
|
||||||
|
}}
|
||||||
|
onMouseEnter={e => e.currentTarget.style.opacity = "0.82"}
|
||||||
|
onMouseLeave={e => e.currentTarget.style.opacity = "1"}
|
||||||
|
>
|
||||||
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
|
||||||
|
<polyline points="15,3 21,3 21,9"/><line x1="10" y1="14" x2="21" y2="3"/>
|
||||||
|
</svg>
|
||||||
|
Visit →
|
||||||
|
</a>
|
||||||
|
<a href="#/services/web-app-dev" style={{
|
||||||
|
display: "inline-flex", alignItems: "center", gap: 7,
|
||||||
|
padding: "10px 16px",
|
||||||
|
background: "transparent", border: `1px solid ${c.border}`,
|
||||||
|
color: c.text, borderRadius: 9, fontSize: 13,
|
||||||
|
textDecoration: "none", transition: "background 0.15s",
|
||||||
|
}}
|
||||||
|
onMouseEnter={e => e.currentTarget.style.background = c.bg}
|
||||||
|
onMouseLeave={e => e.currentTarget.style.background = "transparent"}
|
||||||
|
>Build Mine →</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const FlagshipProduct = () => {
|
||||||
|
const D = PORTFOLIO_DATA;
|
||||||
|
const products = D.ownProducts || [];
|
||||||
|
if (!products.length) return null;
|
||||||
|
|
||||||
|
const rose = { bg: "rgba(244,63,94,0.08)", border: "rgba(244,63,94,0.22)", text: "#fda4af" };
|
||||||
|
const violet = { bg: "rgba(124,92,255,0.08)", border: "rgba(124,92,255,0.22)", text: "#a78bfa" };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section style={{ padding: "120px 0", borderTop: "1px solid var(--line)" }}>
|
||||||
|
<div className="container">
|
||||||
|
<SectionHeader
|
||||||
|
eyebrow="Our Own Products"
|
||||||
|
num="03.5 / 06"
|
||||||
|
title={<>We don't just build for clients — we build for <span style={{ fontFamily: "var(--font-serif)", fontStyle: "italic", fontWeight: 400 }}>ourselves.</span></>}
|
||||||
|
sub="These are Aura's own live products — real businesses we built, run, and grow. Visit them. That's exactly what we ship for you."
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Products grid */}
|
||||||
|
<div className="own-products-grid" style={{
|
||||||
|
display: "grid",
|
||||||
|
gridTemplateColumns: `repeat(${Math.min(products.length, 2)}, 1fr)`,
|
||||||
|
gap: 20,
|
||||||
|
}}>
|
||||||
|
{products.map(p => <OwnProductCard key={p.id} product={p} />)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Bottom nudge */}
|
||||||
|
<div className="reveal" style={{
|
||||||
|
marginTop: 20, padding: "22px 32px",
|
||||||
|
background: "var(--bg-card)", border: "1px solid var(--line)",
|
||||||
|
borderRadius: "var(--radius)",
|
||||||
|
display: "flex", alignItems: "center", justifyContent: "space-between",
|
||||||
|
flexWrap: "wrap", gap: 16,
|
||||||
|
}}>
|
||||||
|
<div>
|
||||||
|
<span style={{ fontSize: 15, fontWeight: 500, color: "var(--text)" }}>
|
||||||
|
Want Aura to build your product?
|
||||||
|
</span>
|
||||||
|
<span style={{
|
||||||
|
display: "block", fontSize: 13, color: "var(--text-faint)",
|
||||||
|
marginTop: 3, fontFamily: "var(--font-mono)",
|
||||||
|
}}>
|
||||||
|
E-commerce · AI marketplace · SaaS — from $499 · Delivered in 2–4 weeks
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<a href="#/contact" style={{
|
||||||
|
padding: "11px 24px",
|
||||||
|
background: "var(--text)", color: "var(--bg)",
|
||||||
|
borderRadius: 9, fontSize: 13.5, fontWeight: 500,
|
||||||
|
textDecoration: "none", whiteSpace: "nowrap", flexShrink: 0,
|
||||||
|
}}>Start a Project →</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>{`
|
||||||
|
@keyframes glamour-pulse {
|
||||||
|
0%, 100% { opacity: 1; box-shadow: 0 0 7px rgba(34,197,94,0.9); }
|
||||||
|
50% { opacity: 0.55; box-shadow: 0 0 2px rgba(34,197,94,0.3); }
|
||||||
|
}
|
||||||
|
@media (max-width: 860px) {
|
||||||
|
.own-products-grid { grid-template-columns: 1fr !important; }
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
window.About = About;
|
window.About = About;
|
||||||
window.Skills = Skills;
|
window.Skills = Skills;
|
||||||
window.SectionHeader = SectionHeader;
|
window.SectionHeader = SectionHeader;
|
||||||
|
window.Testimonials = Testimonials;
|
||||||
|
window.FlagshipProduct = FlagshipProduct;
|
||||||
|
|||||||
Reference in New Issue
Block a user