# Shopify AI App Builder

An AI-powered platform for building complete, production-ready Shopify apps. Describe your app idea in plain English and let AI build it for you.

> **✅ Deploying to Portainer?** The container now automatically fixes the "unexpected character \u200e" error! Environment variables are sanitized on startup. See the [Quick Fix Guide](PORTAINER-QUICKFIX.md) for more details.

## Features

- 🤖 **AI-Powered Development** - Describe your Shopify app and watch it being built in real-time
- 📦 **Complete Apps** - Get production-ready apps with proper structure, API integrations, and more
- ⚡ **OpenCode Integration** - Built on SST OpenCode for reliable code generation
- 📤 **Easy Export** - Download as ZIP or push directly to GitHub
- 🔧 **Full Terminal Access** - Make manual adjustments through the web terminal
- ☁️ **Docker Ready** - Deploy anywhere with the included Docker configuration

## Quick Start

### Prerequisites
- Docker and Docker Compose installed
- (Optional) GitHub Personal Access Token for version control
- (Optional) Dodo Payments API key for payment integration
- (Optional) Google OAuth credentials for authentication

### 1. Clone and Configure

```bash
# Clone the repository
git clone https://github.com/southseact-3d/shopify-ai.git
cd shopify-ai

# Copy environment template
cp .env.example .env

# Edit .env with your configuration (optional)
```

### 2. Build and Run

```bash
# Build the Docker image
docker compose build

# Start the service
docker compose up -d
```

### 3. Access the Application

- **Homepage**: http://localhost:4000
- **App Builder**: http://localhost:4000/builder
- **Terminal**: http://localhost:4001

## Architecture

```
┌─────────────────────────────────────────────────────┐
│                    Docker Container                  │
│                                                      │
│  ┌──────────────┐        ┌──────────────────────┐  │
│  │   Port 4000  │        │      Port 4001       │  │
│  │  Web App UI  │        │   ttyd Terminal      │  │
│  │  (Node.js)   │        │   (PowerShell)       │  │
│  └──────────────┘        └──────────────────────┘  │
│           │                        │                │
│           └────────┬───────────────┘                │
│                    │                                │
│           ┌────────▼────────┐                       │
│           │    OpenCode     │                       │
│           │   AI Assistant  │                       │
│           └─────────────────┘                       │
│                    │                                │
│           ┌────────▼────────┐                       │
│           │   /home/web/data│                       │
│           │   (Workspace)   │                       │
│           └─────────────────┘                       │
└─────────────────────────────────────────────────────┘
```

## Configuration

### Environment Variables

Payment setup: see the Dodo Payments guide in [dodo.md](dodo.md).

| Variable | Description | Default |
|----------|-------------|---------|
| `CHAT_PORT` | Web UI port | `4000` |
| `ACCESS_PASSWORD` | Terminal password protection (leave empty for none) | - |
| `GITHUB_PAT` | GitHub Personal Access Token | - |
| `GITHUB_USERNAME` | GitHub username | - |
| `GITHUB_CLIENT_ID` | GitHub OAuth app client ID | - |
| `GITHUB_CLIENT_SECRET` | GitHub OAuth app client secret | - |
| `OPENROUTER_API_KEY` | OpenRouter API key used for planning mode | - |
| `OPENROUTER_API_URL` | OpenRouter API endpoint for planning | `https://openrouter.ai/api/v1/chat/completions` |
| `OPENROUTER_MODEL_PRIMARY` | Primary OpenRouter model for planning conversations | `anthropic/claude-3.5-sonnet` (fallback order) |
| `OPENROUTER_MODEL_BACKUP_1` | Backup OpenRouter model #1 | `openai/gpt-4o-mini` (fallback order) |
| `OPENROUTER_MODEL_BACKUP_2` | Backup OpenRouter model #2 | `mistralai/mistral-large-latest` (fallback order) |
| `OPENROUTER_MODEL_BACKUP_3` | Backup OpenRouter model #3 | `google/gemini-flash-1.5` (fallback order) |
| `OPENROUTER_DEFAULT_MODEL` | Final fallback model if none of the above resolve | `openai/gpt-4o-mini` |
| `OPENROUTER_PLAN_PROMPT_PATH` | Optional path to override the OpenRouter planning prompt file | `chat/public/openrouter-plan-prompt.txt` |
| `OPENROUTER_FALLBACK_MODELS` | Comma-separated additional fallback models for planning | - |
| `OLLAMA_API_URL` | Ollama self-hosted API URL for planning (e.g., `http://localhost:11434`) | - |
| `OLLAMA_API_KEY` | Ollama API key for self-hosted instances (optional) | - |
| `DODO_PAYMENTS_API_KEY` | Dodo Payments API key | - |
| `DODO_PAYMENTS_ENV` | Dodo Payments environment (`test` or `live`) | `test` |
| `DODO_TOPUP_PRODUCT_FREE` | Dodo product ID for the Free/Starter top-up | - |
| `DODO_TOPUP_PRODUCT_PLUS` | Dodo product ID for the Plus top-up | - |
| `DODO_TOPUP_PRODUCT_PRO` | Dodo product ID for the Pro top-up | - |
| `DODO_TOPUP_TOKENS_FREE` | Token credits for the Free/Starter top-up | `500000` |
| `DODO_TOPUP_TOKENS_PLUS` | Token credits for the Plus top-up | `1000000` |
| `DODO_TOPUP_TOKENS_PRO` | Token credits for the Pro top-up | `1000000` |
| `DODO_MIN_AMOUNT` | Minimum charge (in minor units) to enforce with discounts | `50` |
| `GOOGLE_CLIENT_ID` | Google OAuth client ID | - |
| `GOOGLE_CLIENT_SECRET` | Google OAuth secret | - |
| `PUBLIC_BASE_URL` | Optional base URL override for OAuth redirects | - |
| `SESSION_SECRET` | Session encryption key | - |
| `ADMIN_USER` | Admin username for the `/admin` dashboard | `admin` |
| `ADMIN_PASSWORD` | Admin password for the `/admin` dashboard | - |
| `ADMIN_SESSION_TTL_MS` | Admin session timeout in milliseconds | `86400000` (24h) |
| `COOKIE_SECURE` | Set to `1` to enable secure cookies (requires HTTPS) | `0` |
| `SMTP_HOST` | SMTP host for transactional emails | - |
| `SMTP_PORT` | SMTP port | `587` |
| `SMTP_SECURE` | Set to `1` to force TLS/SSL (usually port 465) | `0` |
| `SMTP_USER` | SMTP username | - |
| `SMTP_PASS` | SMTP password | - |
| `SMTP_FROM` | From email address for verification/reset emails | - |
| `EMAIL_VERIFICATION_TTL_MS` | How long verification links remain valid | `86400000` (24h) |
| `PASSWORD_RESET_TTL_MS` | How long password reset links remain valid | `3600000` (1h) |

> ⚠️ Emails (verification and password reset) are sent as **branded HTML** messages matching the app design. For images/assets to render correctly in emails, set `PUBLIC_BASE_URL` to your site URL (e.g. `https://app.example.com`). If `PUBLIC_BASE_URL` is not set, assets fallback to relative paths.

The planning prompt sent to OpenRouter can be edited at `chat/public/openrouter-plan-prompt.txt` (or by pointing `OPENROUTER_PLAN_PROMPT_PATH` to a different file).

### Password Protection

To enable password protection for the terminal:

```bash
# In .env file
ACCESS_PASSWORD=your_secure_password
```

This will require authentication for the terminal (port 4001) with:
- Username: `user`
- Password: `<your_secure_password>`

## OAuth Sign-In Setup (Google + GitHub)

1. **Set redirect URLs**
   - Google: `https://your-domain.com/auth/google/callback`
   - GitHub: `https://your-domain.com/auth/github/callback`
   - For local testing use `http://localhost:4000/auth/<provider>/callback`
2. **Create credentials**
   - **Google**: In Cloud Console → APIs & Services → Credentials → OAuth Client ID (Web). Add the redirect URL above.
   - **GitHub**: Go to https://github.com/settings/developers → OAuth Apps → New OAuth App. Set the callback URL above.
3. **Configure environment**
   - Add `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET` to `.env`, `docker-compose.yml`, or your stack.
   - If the app is behind a reverse proxy, set `PUBLIC_BASE_URL` (e.g., `https://app.example.com`) so callback URLs are generated correctly.
4. **Restart the service** to apply the new credentials.

## Building Shopify Apps

### Using Templates

The builder includes quick-start templates for common Shopify apps:

1. **Product Recommendation Engine** - Suggest products based on customer behavior
2. **Bulk Image Optimizer** - Optimize product images in bulk
3. **Custom Shipping Rules** - Create advanced shipping logic
4. **Discount Manager** - Manage complex discount campaigns

### Custom Apps

Simply describe what you want in the chat input:

```
Build a Shopify app that shows product recommendations based on 
customer purchase history. Include an admin dashboard with analytics 
and a theme app extension to display recommendations on the storefront.
```

The AI will:
1. Create a complete app structure with necessary configuration
2. Set up API integrations and webhooks
3. Build the admin UI for the merchant
4. Create storefront components or theme app extensions
5. Generate documentation and setup instructions

### Exporting Your App

1. Click "Download as ZIP" in the sidebar
2. Or push directly to GitHub using the version control panel
3. The app is ready to deploy to Vercel, Railway, or any Node.js host

## Deployment

### Docker Compose (Recommended)

```bash
docker compose up -d
```

### Portainer Stack

Use the included `stack-portainer.yml` for Portainer deployment.

**📖 See [PORTAINER.md](PORTAINER.md) for detailed deployment guide and troubleshooting.**

Quick start:
1. Go to Portainer → Stacks → Add Stack
2. Upload or paste `stack-portainer.yml`
3. Set environment variables
   - **Important**: Manually type variable names and values - don't copy-paste to avoid invisible Unicode characters
   - See [PORTAINER.md](PORTAINER.md) for detailed instructions
4. Deploy

### Production Recommendations

- Use a reverse proxy (NGINX, Traefik, Caddy) for HTTPS
- Set strong `ACCESS_PASSWORD` for terminal authentication
- Configure proper firewall rules
- Use Docker secrets for sensitive values

## Development

### Project Structure

```
shopify-ai/
├── chat/                    # Web application
│   ├── server.js           # Express server
│   └── public/             # Frontend files
│       ├── homepage.html   # Landing page
│       ├── builder.html    # App builder UI
│       ├── styles.css      # Shared styles
│       └── app.js          # Frontend JavaScript
├── scripts/                 # Utility scripts
│   ├── entrypoint.sh       # Container startup
│   └── healthcheck.sh      # Health monitoring
├── profile/                 # PowerShell profile
├── Dockerfile              # Container definition
├── docker-compose.yml      # Local development
└── stack-portainer.yml     # Production deployment
```

### Running Locally (Development)

```bash
cd chat
npm install
npm start
```

## Troubleshooting

### Portainer Deployment Error: "unexpected character \u200e"

If you see this error when deploying to Portainer:
```
Failed to deploy a stack: unable to get the environment from the env file: 
failed to read /data/compose/42/stack.env: line 8: unexpected character "\u200e" 
in variable name "ADMIN_USER\u200e=user"
```

This is caused by invisible Unicode characters (like U+200E Left-to-Right Mark) in your environment variables. This typically happens when copying and pasting from certain text editors or web pages.

**Solution:**

1. **Option A - Use the cleanup script:**
   ```bash
   # Clean your .env file
   ./scripts/clean-env.sh .env
   ```

2. **Option B - Manually recreate the variables:**
   - In Portainer's environment variables editor, manually type (don't copy-paste) the variable names
   - Or export the environment variables to a file, clean it, and re-import

3. **Option C - Download a clean template:**
   ```bash
   # Use the clean .env.example as a starting point
   cp .env.example .env
   ```

### OpenCode Not Working

```bash
# Inside the terminal, verify OpenCode is installed
opencode --version

# Check the OpenCode installation
ls -la /root/.opencode/
```

### Port Already in Use

```bash
# Check what's using the port
lsof -i :4000
lsof -i :4001

# Or change ports in docker-compose.yml
```

### Container Health Issues

```bash
# Check container logs
docker compose logs -f

# Check health status
docker compose ps
```

## Security Notes

- This service exposes a web terminal - always use `ACCESS_PASSWORD` in production
- Never commit `.env` files or secrets to version control
- Use HTTPS in production with a reverse proxy
- Rotate credentials regularly

## License

ISC

## Support

For issues and feature requests, please open a GitHub issue.
