A secure, user-friendly web application that allows non-technical users to manage chatbot response text through a Google Sheet interface. This application decouples bot copy from conversational logic, enabling dynamic updates and simplifying future migrations to other bot platforms.
- Secure Authentication: JWT-based authentication system for content managers
- Content Dashboard: Paginated table view with real-time search and filtering
- CRUD Operations: Create, read, update, and delete bot responses with inline editing
- Google Sheets Integration: Single source of truth using Google Sheets API
- Dual API Architecture:
- Management API for content managers (authenticated)
- Delivery API for bot integration (API key based)
- Change Auditing: Automatic timestamp tracking for all modifications
- Responsive Design: Clean, intuitive interface that works on desktop screens
- Docker Support: Containerized deployment with docker-compose
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Frontend │ │ Backend │ │ Google Sheets │
│ (React) │◄──►│ (Node.js) │◄──►│ (Database) │
│ Port: 5173 │ │ Port: 3001 │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Content │ │ Bot │
│ Managers │ │ Integration │
└─────────────────┘ └─────────────────┘
- Node.js 16+ and npm
- Docker and Docker Compose (for containerized deployment)
- Google Cloud Platform account
- Google Sheet with proper structure (see setup instructions)
- Node.js with Express.js framework
- Google Sheets API for data persistence
- JWT for authentication
- bcryptjs for password hashing
- express-rate-limit for API protection
- React 18 with Vite
- React Query for data fetching and caching
- React Router for navigation
- Lucide React for icons
- React Hot Toast for notifications
- Docker for containerization
- Nginx for frontend serving
- Google Cloud Service Account for API authentication
git clone <repository-url>
cd bot-content-manager
- Go to Google Cloud Console
- Create a new project or select an existing one
- Enable the Google Sheets API
- Navigate to IAM & Admin > Service Accounts
- Click Create Service Account
- Fill in the details:
- Name:
bot-content-manager
- Description:
Service account for Bot Content Manager
- Name:
- Click Create and Continue
- Skip role assignment for now
- Click Done
- Click on the created service account
- Go to the Keys tab
- Click Add Key > Create New Key
- Choose JSON format
- Download the key file
- Create a new Google Sheet
- Name it "Bot Content Manager"
- Add the following headers in row 1:
A1: key B1: response_text C1: notes D1: last_updated
- Share the sheet with your service account email (found in the JSON key file)
- Give it Editor permissions
Copy the example environment file and configure it:
cp env.example .env
Edit .env
with your configuration:
# Server Configuration
PORT=3001
NODE_ENV=development
FRONTEND_URL=http://localhost:5173
# JWT Configuration
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
JWT_EXPIRES_IN=24h
# Google Sheets Configuration
GOOGLE_SHEET_ID=your-google-sheet-id-from-url
GOOGLE_SERVICE_ACCOUNT_EMAIL=your-service-account@your-project.iam.gserviceaccount.com
GOOGLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nYOUR_PRIVATE_KEY_HERE\n-----END PRIVATE KEY-----\n"
# API Key for Delivery Endpoints
DELIVERY_API_KEY=your-delivery-api-key-for-bot-integration
# Admin Credentials
ADMIN_USERNAME=admin
ADMIN_PASSWORD=admin123
Important Notes:
- Extract
GOOGLE_SHEET_ID
from your Google Sheet URL:https://docs.google.com/spreadsheets/d/[SHEET_ID]/edit
- Copy the
client_email
from your JSON key file toGOOGLE_SERVICE_ACCOUNT_EMAIL
- Copy the
private_key
from your JSON key file toGOOGLE_PRIVATE_KEY
(keep the quotes and newlines)
CRITICAL: Never use the default secrets in production! Generate strong, unique secrets for both JWT_SECRET
and DELIVERY_API_KEY
.
# Generate JWT_SECRET (64 character random string)
openssl rand -base64 64 | tr -d "=+/" | cut -c1-64
# Generate DELIVERY_API_KEY (128 character secure key)
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
# Generate both secrets at once
node -e "console.log('JWT_SECRET=' + require('crypto').randomBytes(48).toString('base64')); console.log('DELIVERY_API_KEY=' + require('crypto').randomBytes(64).toString('hex'));"
- ✅ Store secrets securely (environment variables, secret managers)
- ✅ Never commit secrets to version control (.gitignore protection)
- ✅ Use different secrets for different environments (dev/staging/prod)
- ✅ Enable HTTPS in production
- ✅ Consider implementing secret rotation strategies
- ✅ Backup secrets securely in case of emergency
# Example of what generated secrets might look like (DO NOT USE THESE - GENERATE YOUR OWN!)
JWT_SECRET=X8rtdIhBhZQIZN2zZkuolKSC9uyj8MsdvNFO0D2Y22vRp10ZHCVJO2joKr9AIq3G8m8JJcveyB0oh4ueP0F5g
DELIVERY_API_KEY=77e3cb7f797bebd8262db062275245033b5692d1993491035c9c05c1cbf9a216dfeb25130e44b4c3ea32d0ffab5466b61f63591839c739babd34afa69e053af0
It's important to understand which token to use for different purposes:
- Token Type: JWT Token (Bearer authentication)
- Generated From: Login endpoint (
/api/auth/login
) - Usage: Used in
Authorization: Bearer <jwt-token>
header - Access: Management API endpoints (CRUD operations)
- Token Type: API Key (X-API-Key authentication)
- Generated From: Environment variable
DELIVERY_API_KEY
- Usage: Used in
X-API-Key: <delivery-api-key>
header - Access: Delivery API endpoints (read-only)
Quick Reference:
# Content Manager API calls
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# Bot Integration API calls
X-API-Key: 77e3cb7f797bebd8262db062275245033b5692d1993491035c9c05c1cbf9a216dfeb25130e44b4c3ea32d0ffab5466b61f63591839c739babd34afa69e053af0
# Install root dependencies
npm install
# Install backend dependencies
cd backend
npm install
# Install frontend dependencies
cd ../frontend
npm install
# From the root directory
npm run dev
This will start:
- Backend server on http://localhost:3001
- Frontend development server on http://localhost:5173
- Open http://localhost:5173 in your browser
- Login with the default credentials:
- Username: admin
- Password: admin123
# Build and start all services
docker-compose up -d
# View logs
docker-compose logs -f
# Stop services
docker-compose down
- Frontend: http://localhost:3000
- Backend API: http://localhost:3001
- Health Check: http://localhost:3001/health
The application expects a Google Sheet with the following structure:
Column | Name | Type | Description |
---|---|---|---|
A | key | String | Unique identifier for the response (Primary Key) |
B | response_text | String | The bot's response message |
C | notes | String | Internal notes for context |
D | last_updated | Timestamp | Automatically updated timestamp |
key | response_text | notes | last_updated |
---|---|---|---|
greeting_welcome | Hello! How can I help you today? | Default welcome message | 2024-01-15 10:30:00 |
order_confirmation | Thank you! Your order #{orderNumber} has been confirmed. | Uses order number variable | 2024-01-15 11:45:00 |
error_generic | I'm sorry, I didn't understand that. Could you please try again? | Fallback error message | 2024-01-15 12:00:00 |
All management endpoints require JWT authentication via Authorization: Bearer <token>
header.
POST /api/auth/login
- Login with username/passwordGET /api/auth/verify
- Verify JWT token
GET /api/management/responses
- Get all responses (with search/pagination)POST /api/management/responses
- Create new responseGET /api/management/responses/:key
- Get specific responsePUT /api/management/responses/:key
- Update responseDELETE /api/management/responses/:key
- Delete response
All delivery endpoints require API key authentication via X-API-Key: <key>
header.
GET /api/delivery/responses/:key
- Get response for bot integrationGET /api/delivery/responses
- Get all responses for bot integration
// Get a specific response
const response = await fetch('https://your-app.com/api/delivery/responses/greeting_welcome', {
headers: {
'X-API-Key': 'your-delivery-api-key' // Generated DELIVERY_API_KEY from .env
}
});
const data = await response.json();
console.log(data.response_text); // "Hello! How can I help you today?"
- ✅ Generate secure
DELIVERY_API_KEY
using the methods above - ✅ Store the API key securely in your bot's configuration
- ✅ Include the key in the
X-API-Key
header for all requests - ✅ Handle HTTP 401 errors for invalid keys
- ✅ Consider implementing automatic retry logic
- ✅ Monitor API usage and implement rate limiting on the bot side
- JWT Authentication: Secure token-based authentication for management API
- API Key Authentication: Separate authentication for bot integration
- Rate Limiting: Protection against abuse (100 requests per 15 minutes per IP)
- Input Validation: Server-side validation for all inputs
- CORS Protection: Configurable CORS settings
- Helmet Security: Security headers via Helmet.js
- Non-root Docker: Containers run as non-root user
- Responsive Table: Clean, sortable table with all responses
- Real-time Search: Instant filtering by key or content
- Inline Editing: Click-to-edit functionality for quick updates
- Modal Forms: User-friendly forms for creating new responses
- Visual Feedback: Loading states, success/error notifications
- Delete Confirmation: Prevents accidental deletions
- Clean & Simple: Minimalist design focused on functionality
- Intuitive: Easy-to-use interface for non-technical users
- Responsive: Works well on standard desktop screen sizes
- Accessible: Proper contrast ratios and keyboard navigation
# Production deployment
docker-compose up -d
# With custom environment file
docker-compose --env-file .env.production up -d
cd backend
npm install --production
npm start
cd frontend
npm install
npm run build
# Serve the dist/ directory with your web server
The application can be deployed to:
- AWS: Using ECS, EKS, or EC2
- Google Cloud: Using Cloud Run or GKE
- Azure: Using Container Instances or AKS
- Heroku: With some modifications for environment variables
bot-content-manager/
├── backend/ # Node.js backend
│ ├── routes/ # API routes
│ ├── services/ # Business logic
│ ├── middleware/ # Express middleware
│ ├── Dockerfile # Backend container
│ └── package.json
├── frontend/ # React frontend
│ ├── src/
│ │ ├── components/ # React components
│ │ ├── pages/ # Page components
│ │ ├── contexts/ # React contexts
│ │ ├── services/ # API services
│ │ └── main.jsx
│ ├── Dockerfile # Frontend container
│ └── package.json
├── docker-compose.yml # Docker orchestration
├── .env.example # Environment template
└── README.md # This file
- Backend Changes: Add routes in
backend/routes/
, services inbackend/services/
- Frontend Changes: Add components in
frontend/src/components/
, pages infrontend/src/pages/
- Database Changes: Modify the Google Sheet schema and update the service accordingly
# Backend tests
cd backend
npm test
# Frontend tests (when implemented)
cd frontend
npm test
Variable | Description | Required | Default |
---|---|---|---|
PORT |
Backend server port | No | 3001 |
NODE_ENV |
Environment mode | No | development |
JWT_SECRET |
JWT signing secret (MIN 32 chars) | Yes | - |
JWT_EXPIRES_IN |
Token expiration | No | 24h |
GOOGLE_SHEET_ID |
Google Sheet ID | Yes | - |
GOOGLE_SERVICE_ACCOUNT_EMAIL |
Service account email | Yes | - |
GOOGLE_PRIVATE_KEY |
Service account private key | Yes | - |
DELIVERY_API_KEY |
API key for bot integration (MIN 64 chars) | Yes | - |
ADMIN_USERNAME |
Default admin username | No | admin |
ADMIN_PASSWORD |
Default admin password | No | admin123 |
JWT_SECRET
and DELIVERY_API_KEY
must be cryptographically strong random strings. Never use the default placeholder values in production!
The Google Sheet must:
- Have the correct column headers (key, response_text, notes, last_updated)
- Be shared with the service account email
- Have Editor permissions for the service account
- Error: "The caller does not have permission"
- Solution: Ensure the service account email has access to the Google Sheet
- Error: "Invalid or expired token"
- Solution: Check JWT_SECRET configuration and token expiration
- Error: CORS policy blocking requests
- Solution: Verify FRONTEND_URL environment variable
- Error: Container fails to start
- Solution: Check environment variables and Google Sheets configuration
Enable debug logging by setting:
NODE_ENV=development
- Backend:
GET http://localhost:3001/health
- Frontend:
GET http://localhost:3000/health
- Backend logs: Available in Docker container logs
- Application logs: Written to stdout/stderr
- Health check:
/health
- API status: Available through health check response
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
This project is licensed under the MIT License - see the LICENSE file for details.
For support and questions:
- Check the troubleshooting section above
- Review the API documentation
- Create an issue in the repository
- Pull the latest changes
- Update environment variables if needed
- Restart the services
The Google Sheet serves as the single source of truth, making it easy to:
- Export data from the sheet
- Import into your new bot platform
- Update the bot's integration to use the new platform
Built with ❤️ for content managers who want to control their bot's personality without developer assistance.