- Add nodemailer dependency for SMTP email delivery - Create src/services/email.js with reusable email service - Update POST /api/contact to send email notification after saving - Email is sent fire-and-forget (doesn't block API response) - Emails include both plain-text and HTML versions - Reply-To header set to submitter's email for easy replies - Add SMTP environment variables to .env.example - Document email setup in README with provider-specific guides
293 lines
8.2 KiB
Markdown
293 lines
8.2 KiB
Markdown
# Moxie Backend
|
|
|
|
Express.js backend API for user management of an AI site, built with ESM syntax, Auth0 authentication, and SQLite database.
|
|
|
|
## Features
|
|
|
|
- **Auth0 Authentication**: Secure authentication via Auth0
|
|
- **User Management**: Auto-creation of users on first login, profile management
|
|
- **Credit System**: Track and manage user credits
|
|
- **API Keys**: Generate and manage API keys for programmatic access
|
|
- **Payment Webhooks**: Ready for Stripe and PayPal integration
|
|
- **Admin Endpoints**: User management for administrators
|
|
- **SQLite Database**: Lightweight, file-based storage
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
# Install dependencies
|
|
npm install
|
|
|
|
# Copy environment file and configure
|
|
cp .env.example .env
|
|
# Edit .env with your Auth0 credentials
|
|
|
|
# Start the server
|
|
npm start
|
|
|
|
# Start in development mode (with auto-reload)
|
|
npm run dev
|
|
```
|
|
|
|
The server runs on port 9991 by default.
|
|
|
|
## Auth0 Setup
|
|
|
|
### 1. Create Auth0 Application
|
|
|
|
1. Go to Auth0 Dashboard > Applications > Applications
|
|
2. Create a new "Single Page Application" or "Regular Web Application"
|
|
3. Configure the following URLs:
|
|
- **Allowed Callback URLs**: `https://moxiegen.client.guacamolebox.net/api/callback`
|
|
- **Allowed Logout URLs**: `https://moxiegen.client.guacamolebox.net`
|
|
- **Allowed Web Origins**: `https://moxiegen.client.guacamolebox.net`
|
|
- **Application Login URI**: `https://moxiegen.client.guacamolebox.net/login`
|
|
|
|
### 2. Create Auth0 API (Optional)
|
|
|
|
For machine-to-machine authentication:
|
|
1. Go to Auth0 Dashboard > Applications > APIs
|
|
2. Create a new API
|
|
3. Set the identifier as your audience
|
|
4. Enable RBAC if needed
|
|
|
|
### 3. Configure Environment Variables
|
|
|
|
```env
|
|
AUTH0_DOMAIN=your-tenant.auth0.com
|
|
AUTH0_CLIENT_ID=your-client-id
|
|
AUTH0_CLIENT_SECRET=your-client-secret
|
|
AUTH0_AUDIENCE=https://your-tenant.auth0.com/api/v2/
|
|
APP_URL=https://moxiegen.client.guacamolebox.net
|
|
```
|
|
|
|
## API Endpoints
|
|
|
|
### Auth Endpoints
|
|
|
|
| Method | Endpoint | Description |
|
|
|--------|----------|-------------|
|
|
| GET | `/api/login` | Redirect to Auth0 login |
|
|
| GET | `/api/callback` | Handle Auth0 callback |
|
|
| GET | `/api/logout` | Logout and redirect |
|
|
|
|
### User Endpoints (Requires Auth0 Token)
|
|
|
|
All authenticated endpoints require `Authorization: Bearer <access_token>` header.
|
|
|
|
| Method | Endpoint | Description |
|
|
|--------|----------|-------------|
|
|
| GET | `/api/users/me` | Get current user profile |
|
|
| PUT | `/api/users/me` | Update profile |
|
|
| DELETE | `/api/users/me` | Deactivate account |
|
|
| GET | `/api/users/credits` | Get credits and history |
|
|
| GET | `/api/users/api-keys` | List API keys |
|
|
| POST | `/api/users/api-keys` | Create new API key |
|
|
| DELETE | `/api/users/api-keys/:keyId` | Revoke API key |
|
|
|
|
### Admin Endpoints (Requires `role: 'admin'`)
|
|
|
|
| Method | Endpoint | Description |
|
|
|--------|----------|-------------|
|
|
| GET | `/api/users` | List all users |
|
|
| GET | `/api/users/:userId` | Get user by ID |
|
|
| PUT | `/api/users/:userId` | Update user |
|
|
| DELETE | `/api/users/:userId` | Delete user |
|
|
| POST | `/api/users/:userId/credits` | Adjust user credits |
|
|
| PUT | `/api/users/:userId/role` | Change user role |
|
|
|
|
### Webhook Endpoints
|
|
|
|
| Method | Endpoint | Description |
|
|
|--------|----------|-------------|
|
|
| POST | `/api/webhooks/stripe` | Stripe webhook handler |
|
|
| POST | `/api/webhooks/paypal` | PayPal webhook handler |
|
|
|
|
## Frontend Integration
|
|
|
|
### Login Flow
|
|
|
|
```javascript
|
|
// Redirect to Auth0 login
|
|
window.location.href = '/api/login';
|
|
|
|
// Handle callback (tokens in URL params)
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const accessToken = urlParams.get('access_token');
|
|
const idToken = urlParams.get('id_token');
|
|
|
|
// Store token and use for API calls
|
|
localStorage.setItem('access_token', accessToken);
|
|
|
|
// Make authenticated requests
|
|
fetch('/api/users/me', {
|
|
headers: {
|
|
'Authorization': `Bearer ${accessToken}`
|
|
}
|
|
});
|
|
```
|
|
|
|
### Using Auth0 SPA SDK
|
|
|
|
```javascript
|
|
import { Auth0Client } from '@auth0/auth0-spa-js';
|
|
|
|
const auth0 = new Auth0Client({
|
|
domain: 'your-tenant.auth0.com',
|
|
client_id: 'your-client-id',
|
|
redirect_uri: window.location.origin
|
|
});
|
|
|
|
// Login
|
|
await auth0.loginWithRedirect();
|
|
|
|
// Get token
|
|
const token = await auth0.getTokenSilently();
|
|
|
|
// Use token
|
|
fetch('/api/users/me', {
|
|
headers: {
|
|
'Authorization': `Bearer ${token}`
|
|
}
|
|
});
|
|
```
|
|
|
|
## Database Schema
|
|
|
|
### Users Table
|
|
- `id` - Primary key (UUID)
|
|
- `auth0_id` - Auth0 user ID (sub claim)
|
|
- `email` - Email address
|
|
- `name` - Display name
|
|
- `picture` - Profile picture URL
|
|
- `role` - User role ('user' or 'admin')
|
|
- `credits` - Available credits
|
|
- `subscription_status` - Subscription state
|
|
- `subscription_tier` - Subscription level
|
|
- `stripe_customer_id` - Stripe customer reference
|
|
- `paypal_customer_id` - PayPal customer reference
|
|
- `is_active` - Account status flag
|
|
|
|
### API Keys Table
|
|
- `id` - Key ID
|
|
- `user_id` - Foreign key to users
|
|
- `key_hash` - Hashed API key
|
|
- `name` - Key name/description
|
|
- `is_active` - Key status
|
|
|
|
### Credit Transactions Table
|
|
- `id` - Transaction ID
|
|
- `user_id` - Foreign key to users
|
|
- `amount` - Credit amount (+/-)
|
|
- `type` - 'credit' or 'debit'
|
|
- `description` - Transaction description
|
|
|
|
### Payments Table
|
|
- `id` - Payment ID
|
|
- `user_id` - Foreign key to users
|
|
- `amount` - Payment amount
|
|
- `provider` - 'stripe' or 'paypal'
|
|
- `status` - Payment status
|
|
|
|
## Caddy Configuration
|
|
|
|
```caddyfile
|
|
moxiegen.client.guacamolebox.net {
|
|
# Static site
|
|
root * /path/to/static/site
|
|
file_server
|
|
|
|
# API proxy
|
|
handle /api/* {
|
|
reverse_proxy localhost:9991
|
|
}
|
|
}
|
|
```
|
|
|
|
## Making a User Admin
|
|
|
|
To promote a user to admin role:
|
|
|
|
1. Find the user ID from the database
|
|
2. Use the admin API (requires an existing admin)
|
|
3. Or directly update the database:
|
|
|
|
```sql
|
|
UPDATE users SET role = 'admin' WHERE email = 'admin@example.com';
|
|
```
|
|
|
|
## Payment Integration
|
|
|
|
### Stripe Setup
|
|
|
|
1. Create a Stripe account and get API keys
|
|
2. Add keys to environment variables
|
|
3. Create a webhook endpoint in Stripe dashboard pointing to `https://moxiegen.client.guacamolebox.net/api/webhooks/stripe`
|
|
4. Copy the webhook signing secret to `STRIPE_WEBHOOK_SECRET`
|
|
|
|
### PayPal Setup
|
|
|
|
1. Create a PayPal Developer account
|
|
2. Create a REST API application
|
|
3. Add credentials to environment variables
|
|
4. Configure webhook in PayPal dashboard pointing to `https://moxiegen.client.guacamolebox.net/api/webhooks/paypal`
|
|
|
|
## Email Configuration (Contact Form)
|
|
|
|
Contact form submissions are automatically emailed to a configured address using Nodemailer with SMTP.
|
|
|
|
### Setup
|
|
|
|
Add the following to your `.env` file:
|
|
|
|
```env
|
|
# SMTP Email Configuration
|
|
SMTP_HOST=smtp.gmail.com # Your SMTP server
|
|
SMTP_PORT=587 # Usually 587 (TLS) or 465 (SSL)
|
|
SMTP_USER=your-email@gmail.com # Email address to send from
|
|
SMTP_PASS=your-app-password # Password or app-specific password
|
|
SMTP_SECURE=false # true for port 465, false for 587
|
|
SMTP_FROM=Moxie <your-email@gmail.com> # From display name & address
|
|
|
|
# Where contact form emails are sent (defaults to SMTP_USER)
|
|
CONTACT_EMAIL=notifications@yourdomain.com
|
|
```
|
|
|
|
### Gmail Users
|
|
|
|
If you use Gmail, you must generate an **App Password** because regular passwords no longer work with SMTP:
|
|
|
|
1. Go to [Google Account Security](https://myaccount.google.com/security)
|
|
2. Enable **2-Step Verification** (if not already enabled)
|
|
3. Go to **App passwords** (search in security settings)
|
|
4. Create a new app password for "Mail"
|
|
5. Use that 16-character password as `SMTP_PASS`
|
|
|
|
### Other Providers
|
|
|
|
| Provider | SMTP_HOST | SMTP_PORT | Notes |
|
|
|----------|-----------|-----------|-------|
|
|
| Gmail | smtp.gmail.com | 587 | Requires App Password |
|
|
| Outlook / Office 365 | smtp.office365.com | 587 | |
|
|
| SendGrid | smtp.sendgrid.net | 587 | Use API key as password |
|
|
| Mailgun | smtp.mailgun.org | 587 | |
|
|
| AWS SES | email-smtp.[region].amazonaws.com | 587 | Use SMTP credentials |
|
|
|
|
### How It Works
|
|
|
|
When someone submits the contact form (`POST /api/contact`):
|
|
|
|
1. The submission is **saved to the database** (always succeeds)
|
|
2. An email notification is **sent in the background** (fire-and-forget)
|
|
3. If email sending fails, the form still saves — a warning is logged but the user is not affected
|
|
|
|
The email includes:
|
|
- Plain-text and HTML versions
|
|
- The submitter's name, email, and company (if provided)
|
|
- The full message
|
|
- A reply-to header so you can reply directly to the submitter
|
|
|
|
## License
|
|
|
|
ISC
|