Generated by Spark: I can auto default to flask backend with docker environment variable. If its not set used IndexedDB.

This commit is contained in:
2026-01-17 18:38:03 +00:00
committed by GitHub
parent 40aeedeeba
commit ca93235cde
19 changed files with 1253 additions and 32 deletions

16
.dockerignore Normal file
View File

@@ -0,0 +1,16 @@
node_modules
dist
.git
.github
.env
.env.local
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.DS_Store
.vscode
.idea
backend
data
pids

4
.env.example Normal file
View File

@@ -0,0 +1,4 @@
# Flask Backend Configuration (Optional)
# If set, the app will automatically use Flask backend instead of IndexedDB
# Example: VITE_FLASK_BACKEND_URL=http://localhost:5000
VITE_FLASK_BACKEND_URL=

260
BACKEND-CONFIG.md Normal file
View File

@@ -0,0 +1,260 @@
# Backend Configuration Guide
This guide explains how to configure CodeSnippet to use different storage backends.
## Storage Options
CodeSnippet supports two storage backends:
### 1. IndexedDB (Default)
- **Local browser storage** using SQLite compiled to WebAssembly
- No server required
- Data persists only on the current device/browser
- Best for personal use or offline scenarios
### 2. Flask Backend
- **Remote server storage** with a Flask REST API
- Requires running a Flask backend server
- Data accessible from any device
- Best for team use or multi-device access
## Configuration Methods
### Method 1: Automatic Configuration (Environment Variable)
The simplest way to configure Flask backend is using the `VITE_FLASK_BACKEND_URL` environment variable.
**When to use:**
- Production deployments
- Docker/containerized environments
- When you want to enforce backend usage
**How it works:**
- App automatically connects to Flask backend on startup
- Manual backend selection is disabled in Settings UI
- Overrides any saved user preferences
**Setup:**
Create `.env` file in project root:
```bash
VITE_FLASK_BACKEND_URL=http://localhost:5000
```
Or set in Docker:
```yaml
# docker-compose.yml
services:
frontend:
environment:
- VITE_FLASK_BACKEND_URL=http://backend:5000
```
Or for Docker build:
```bash
docker build --build-arg VITE_FLASK_BACKEND_URL=http://api.example.com .
```
### Method 2: Manual Configuration (Settings Page)
Users can manually switch backends in the Settings page.
**When to use:**
- Development/testing
- User preference scenarios
- When Flask backend is optional
**How it works:**
- Navigate to Settings → Storage Backend
- Select "Flask Backend (Remote Server)"
- Enter Flask backend URL
- Test connection
- Save settings
**Note:** Manual configuration is automatically disabled when `VITE_FLASK_BACKEND_URL` is set.
## Backend Deployment Scenarios
### Scenario 1: Full Docker Stack (Recommended for Production)
Both frontend and backend in Docker with automatic configuration:
```bash
docker-compose up -d
```
Frontend: `http://localhost:3000`
Backend: `http://localhost:5000`
The frontend automatically connects to backend via internal Docker network.
### Scenario 2: Local Development
Backend in Docker, frontend in dev mode:
```bash
# Terminal 1: Start backend
docker-compose up backend
# Terminal 2: Start frontend with env var
echo "VITE_FLASK_BACKEND_URL=http://localhost:5000" > .env
npm run dev
```
### Scenario 3: Backend Only
Run backend separately, users configure manually:
```bash
cd backend
python app.py
```
Users go to Settings and configure `http://localhost:5000` manually.
### Scenario 4: Remote Backend
Point frontend to a remote Flask API:
```bash
# .env
VITE_FLASK_BACKEND_URL=https://api.example.com
```
Or configure manually in Settings with the remote URL.
## Data Migration
### From IndexedDB to Flask
1. Ensure Flask backend is running
2. Go to Settings → Storage Backend
3. Select "Flask Backend"
4. Enter backend URL and test connection
5. Click "Migrate IndexedDB Data to Flask"
6. Save storage settings
### From Flask to IndexedDB
1. Ensure you have Flask backend URL configured
2. Go to Settings → Storage Backend
3. Click "Migrate Flask Data to IndexedDB"
4. Select "IndexedDB (Local Browser Storage)"
5. Save storage settings
**Note:** Migration copies data, it doesn't move it. Original data remains in the source.
## Environment Variable Reference
| Variable | Description | Default | Example |
|----------|-------------|---------|---------|
| `VITE_FLASK_BACKEND_URL` | Flask backend URL. When set, forces Flask backend usage. | (none) | `http://localhost:5000` |
## Troubleshooting
### "Connection failed" error
**Causes:**
- Backend server not running
- Incorrect URL
- CORS issues
- Network/firewall blocking
**Solutions:**
1. Verify backend is running: `curl http://localhost:5000/health`
2. Check URL spelling and port number
3. Review backend logs for errors
4. Ensure CORS is enabled in Flask app
### Environment variable not working
**Causes:**
- File named incorrectly (must be `.env`)
- Variable not prefixed with `VITE_`
- Server not restarted after change
**Solutions:**
1. Ensure file is named `.env` (not `.env.local` or `.env.txt`)
2. Restart dev server: `npm run dev`
3. For production builds: rebuild with `npm run build`
4. Verify with: `console.log(import.meta.env.VITE_FLASK_BACKEND_URL)`
### Settings page is read-only
This is expected when `VITE_FLASK_BACKEND_URL` is set. To enable manual configuration:
1. Remove the environment variable from `.env`
2. Restart the application
3. Settings will become editable
### Data not syncing between devices
Ensure:
1. All devices are configured to use the same Flask backend URL
2. Backend server is accessible from all devices (not localhost if remote)
3. Backend has persistent storage (volume mounted in Docker)
## Security Considerations
### Production Deployment
1. **Use HTTPS:** Always use `https://` URLs in production
2. **Authentication:** Consider adding authentication to Flask backend
3. **CORS:** Configure CORS to allow only your frontend domain
4. **Network:** Run backend in private network, not exposed to internet
### Example secure configuration:
```python
# backend/app.py
from flask_cors import CORS
CORS(app, origins=['https://your-frontend-domain.com'])
```
```bash
# .env (production)
VITE_FLASK_BACKEND_URL=https://api.your-domain.com
```
## Best Practices
1. **Development:** Use IndexedDB or local Flask backend
2. **Staging:** Use Flask backend with test data
3. **Production:** Use Flask backend with environment variable
4. **Backup:** Regularly export database from Settings page
5. **Migration:** Test data migration with small dataset first
## Architecture Diagram
```
┌─────────────────────────────────────┐
│ React Frontend │
│ │
│ ┌──────────────────────────────┐ │
│ │ Storage Config Layer │ │
│ │ (checks env var first) │ │
│ └──────────┬───────────────────┘ │
│ │ │
│ ┌──────┴──────┐ │
│ │ │ │
│ ┌───▼───┐ ┌───▼───────┐ │
│ │ IDB │ │ Flask │ │
│ │Adapter│ │ Adapter │ │
│ └───────┘ └─────┬─────┘ │
└────────────────────┼───────────────┘
│ HTTP
┌──────▼──────┐
│ Flask │
│ Backend │
│ + SQLite │
└─────────────┘
```
## Additional Resources
- [Flask Backend README](./backend/README.md)
- [Backend API Documentation](./backend/README.md#api-endpoints)
- [Docker Compose Configuration](./docker-compose.yml)
- [Example .env file](./.env.example)

25
Dockerfile Normal file
View File

@@ -0,0 +1,25 @@
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
ARG VITE_FLASK_BACKEND_URL
ENV VITE_FLASK_BACKEND_URL=$VITE_FLASK_BACKEND_URL
RUN npm run build
# Production stage
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 3000
CMD ["nginx", "-g", "daemon off;"]

202
IMPLEMENTATION.md Normal file
View File

@@ -0,0 +1,202 @@
# Implementation Summary: Auto Backend Configuration
## Overview
Implemented automatic Flask backend configuration via Docker environment variable (`VITE_FLASK_BACKEND_URL`). When set, the application automatically uses the Flask backend instead of IndexedDB, with manual configuration disabled.
## Changes Made
### 1. Storage Configuration (`src/lib/storage.ts`)
**Added:**
- `getDefaultConfig()` function that checks for `VITE_FLASK_BACKEND_URL` environment variable
- Automatic backend selection based on environment variable presence
- Priority: Environment variable > Saved config > Default (IndexedDB)
**Behavior:**
- If `VITE_FLASK_BACKEND_URL` is set, always use Flask backend
- Environment variable overrides any saved user preferences
- Auto-initializes with Flask adapter when env var is present
### 2. Database Layer (`src/lib/db.ts`)
**Added:**
- Auto-load storage config on first database operation
- Persistent config loading with `configLoaded` flag
- Seamless switching between storage backends
### 3. Settings UI (`src/pages/SettingsPage.tsx`)
**Added:**
- Environment variable detection and display
- Read-only mode when env var is set
- Status card showing auto-configuration details
- Connection status indicator
- Disabled form inputs when env var controls backend
**Features:**
- Visual indicator for auto-configured backend
- Shows current backend URL from env var
- Displays configuration source
- Test connection button for auto-configured backends
### 4. Visual Indicators (`src/components/BackendIndicator.tsx`)
**Added:**
- Header badge showing active storage backend
- "Local" badge for IndexedDB
- "Backend" badge with dot indicator for auto-configured Flask
- Tooltips explaining storage type
### 5. TypeScript Definitions (`src/vite-end.d.ts`)
**Added:**
- Type definitions for `VITE_FLASK_BACKEND_URL`
- Proper `ImportMetaEnv` interface
### 6. Docker Configuration
**Added:**
- `Dockerfile` - Multi-stage build for production frontend
- `nginx.conf` - Nginx configuration with API proxy
- `.dockerignore` - Optimized Docker builds
- Updated `docker-compose.yml` - Full stack with auto-configuration
- `docker-compose.backend-only.yml` - Backend-only deployment
**Features:**
- Frontend and backend containers
- Automatic environment variable passing
- Persistent data volumes
- Build-time and runtime env var support
### 7. Documentation
**Created:**
- `.env.example` - Environment variable template
- `QUICKSTART.md` - Quick start guide for all scenarios
- `BACKEND-CONFIG.md` - Comprehensive backend configuration guide
- `docker-compose.README.md` - Docker deployment examples
- Updated `README.md` - New main readme with features
- Updated `README-APP.md` - Enhanced with env var docs
- Updated `backend/README.md` - Auto-configuration instructions
**Documentation covers:**
- Environment variable usage
- Multiple deployment scenarios
- Docker configurations
- Manual vs automatic configuration
- Troubleshooting guide
- Migration procedures
- Security considerations
## Configuration Methods
### Method 1: Environment Variable (Automatic)
```bash
# .env file
VITE_FLASK_BACKEND_URL=http://localhost:5000
```
**Result:** App automatically uses Flask backend, Settings locked
### Method 2: Docker Compose
```yaml
services:
frontend:
environment:
- VITE_FLASK_BACKEND_URL=http://backend:5000
```
**Result:** Full stack with auto-configured backend
### Method 3: Manual (Settings Page)
Navigate to Settings → Select Flask Backend → Enter URL → Save
**Result:** User-controlled backend selection
## Priority Order
1. **Environment Variable** (`VITE_FLASK_BACKEND_URL`) - Highest priority
2. **Saved User Preference** (localStorage)
3. **Default** (IndexedDB)
## User Experience
### With Environment Variable Set:
1. App starts and detects `VITE_FLASK_BACKEND_URL`
2. Automatically initializes Flask backend adapter
3. Shows "Backend" badge in header (with dot indicator)
4. Settings page displays auto-configuration card
5. Backend selection controls are disabled
6. "Save Storage Settings" button is disabled
### Without Environment Variable:
1. App starts with default IndexedDB
2. Shows "Local" badge in header
3. Settings page allows backend selection
4. Users can manually configure Flask backend
5. All controls are enabled
## Testing Scenarios
### Scenario 1: Development with Local Backend
```bash
echo "VITE_FLASK_BACKEND_URL=http://localhost:5000" > .env
docker-compose up backend
npm run dev
```
### Scenario 2: Full Docker Stack
```bash
docker-compose up -d
# Access at http://localhost:3000
```
### Scenario 3: Local Storage Only
```bash
npm run dev
# No env var, uses IndexedDB
```
### Scenario 4: Remote Backend
```bash
echo "VITE_FLASK_BACKEND_URL=https://api.example.com" > .env
npm run dev
```
## Key Benefits
1. **Production Ready:** Environment variable ensures consistent backend usage
2. **Developer Friendly:** Easy local development with auto-configuration
3. **Docker Native:** Seamless integration with container orchestration
4. **User Choice:** Manual configuration still available when needed
5. **Clear Feedback:** UI clearly shows which backend is active
6. **Zero Config:** Full stack works out of the box with docker-compose
## Backwards Compatibility
- Existing apps without env var continue using saved preferences
- Manual configuration still works when env var not set
- No breaking changes to existing functionality
- Data migration tools remain functional
## Security Considerations
- Environment variables not exposed to client (compile-time only)
- CORS configured in Flask backend
- HTTPS recommended for production
- No credentials stored in environment variables
## Future Enhancements
Potential improvements:
- Add backend health check on startup
- Show connection quality indicator
- Support multiple backend URLs for failover
- Add authentication token via env var
- Implement read-only mode configuration

267
QUICKSTART.md Normal file
View File

@@ -0,0 +1,267 @@
# Quick Start Guide
## Choose Your Setup
### 🚀 Full Stack with Docker (Easiest)
Everything runs in Docker with automatic backend configuration.
```bash
docker-compose up -d
```
✅ Frontend: http://localhost:3000
✅ Backend: http://localhost:5000
✅ Auto-configured to use Flask backend
---
### 💻 Local Development
Backend in Docker, frontend in development mode.
```bash
# Terminal 1: Start backend
docker-compose -f docker-compose.backend-only.yml up -d
# Terminal 2: Configure and start frontend
echo "VITE_FLASK_BACKEND_URL=http://localhost:5000" > .env
npm install
npm run dev
```
✅ Frontend: http://localhost:5173 (Vite dev server)
✅ Backend: http://localhost:5000
✅ Auto-configured to use Flask backend
---
### 🌐 Frontend Only (Local Storage)
No backend required - uses browser IndexedDB.
```bash
npm install
npm run dev
```
✅ Frontend: http://localhost:5173
✅ Data stored locally in browser
✅ No server needed
---
### ⚙️ Backend Only
Run backend separately, configure frontend manually.
```bash
cd backend
pip install -r requirements.txt
python app.py
```
Then in a separate terminal:
```bash
npm install
npm run dev
```
✅ Backend: http://localhost:5000
✅ Frontend: http://localhost:5173
⚠️ Must configure backend URL in Settings page
---
## Key Features by Setup
| Feature | Full Stack Docker | Local Dev | Frontend Only | Backend Only |
|---------|------------------|-----------|---------------|--------------|
| Auto-configured backend | ✅ | ✅ | ❌ | ❌ |
| Hot reload | ❌ | ✅ | ✅ | ✅ |
| Multi-device sync | ✅ | ✅ | ❌ | ✅* |
| No dependencies | ❌ | ❌ | ✅ | ❌ |
| Production-ready | ✅ | ❌ | ❌ | ⚠️ |
*Requires manual configuration
---
## Environment Variables
### `VITE_FLASK_BACKEND_URL`
**What it does:**
Automatically configures the app to use a Flask backend instead of IndexedDB.
**When set:**
- App connects to Flask backend on startup
- Settings page backend selection is disabled
- Overrides any manual configuration
**Examples:**
Local backend:
```bash
VITE_FLASK_BACKEND_URL=http://localhost:5000
```
Docker internal:
```bash
VITE_FLASK_BACKEND_URL=http://backend:5000
```
Remote backend:
```bash
VITE_FLASK_BACKEND_URL=https://api.example.com
```
**Setup methods:**
`.env` file:
```bash
echo "VITE_FLASK_BACKEND_URL=http://localhost:5000" > .env
```
Docker Compose:
```yaml
services:
frontend:
environment:
- VITE_FLASK_BACKEND_URL=http://backend:5000
```
Docker build:
```bash
docker build --build-arg VITE_FLASK_BACKEND_URL=http://api.example.com .
```
---
## Common Commands
### Docker
```bash
# Start all services
docker-compose up -d
# Stop all services
docker-compose down
# View logs
docker-compose logs -f
# Rebuild after code changes
docker-compose up -d --build
# Start backend only
docker-compose -f docker-compose.backend-only.yml up -d
```
### NPM
```bash
# Install dependencies
npm install
# Development server
npm run dev
# Production build
npm run build
# Preview production build
npm run preview
```
### Backend (Python)
```bash
cd backend
# Install dependencies
pip install -r requirements.txt
# Run server
python app.py
# Run with custom database path
DB_PATH=/custom/path/snippets.db python app.py
```
---
## Troubleshooting
### "Connection failed" in Settings
**Problem:** Can't connect to Flask backend
**Solutions:**
1. Verify backend is running: `curl http://localhost:5000/health`
2. Check URL is correct (include `http://`)
3. Ensure no firewall is blocking port 5000
4. Check backend logs: `docker-compose logs backend`
### Environment variable not working
**Problem:** `VITE_FLASK_BACKEND_URL` not taking effect
**Solutions:**
1. Restart dev server after creating/modifying `.env`
2. Ensure file is named `.env` (not `.env.txt`)
3. Variable must start with `VITE_`
4. For production builds: rebuild with `npm run build`
### Settings page is read-only
**This is expected** when `VITE_FLASK_BACKEND_URL` is set.
**To enable manual configuration:**
1. Remove the variable from `.env`
2. Restart the application
### Port already in use
**Problem:** "Port 3000 (or 5000) is already in use"
**Solutions:**
Change frontend port:
```yaml
# docker-compose.yml
services:
frontend:
ports:
- "8080:3000" # Access at http://localhost:8080
```
Change backend port:
```yaml
# docker-compose.yml
services:
backend:
ports:
- "8000:5000" # Access at http://localhost:8000
environment:
- VITE_FLASK_BACKEND_URL=http://backend:5000 # Keep internal port same
```
Or stop the conflicting service:
```bash
# Find process using port
lsof -i :3000
# Kill process (replace PID)
kill -9 <PID>
```
---
## Next Steps
- 📖 [Full documentation](./README-APP.md)
- 🔧 [Backend configuration guide](./BACKEND-CONFIG.md)
- 🐳 [Docker examples](./docker-compose.README.md)
- 🚀 [Backend API docs](./backend/README.md)

View File

@@ -34,6 +34,8 @@ CodeSnippet offers flexible data storage with two backend options:
### Switching Storage Backends
#### Manual Configuration
Visit the **Settings** page to:
- Choose between IndexedDB and Flask backend
- Configure Flask backend URL
@@ -42,6 +44,31 @@ Visit the **Settings** page to:
- View database statistics
- Export/import database backups
#### Automatic Configuration with Environment Variable
You can automatically configure the Flask backend using a Docker environment variable. When `VITE_FLASK_BACKEND_URL` is set, the app will:
- Automatically use the Flask backend instead of IndexedDB
- Override any manual configuration
- Disable manual backend selection in the Settings page
**Setup:**
1. Create a `.env` file in the project root:
```bash
VITE_FLASK_BACKEND_URL=http://localhost:5000
```
2. Or set it in your Docker environment:
```yaml
# docker-compose.yml
services:
frontend:
environment:
- VITE_FLASK_BACKEND_URL=http://backend:5000
```
**Note:** When the environment variable is set, the storage backend configuration in Settings becomes read-only. To change backends, remove the environment variable and restart the application.
## Backend Setup
### Running Flask Backend Locally
@@ -54,7 +81,24 @@ python app.py
Server runs on `http://localhost:5000` by default.
### Running with Docker
### Running with Docker Compose (Full Stack)
The easiest way to run both frontend and backend with automatic Flask backend configuration:
```bash
# Start both frontend and backend
docker-compose up -d
# View logs
docker-compose logs -f
# Stop services
docker-compose down
```
Access the app at `http://localhost:3000`. The frontend is automatically configured to use the Flask backend at `http://backend:5000`.
### Running Backend Only with Docker
Build and run:
```bash
@@ -62,11 +106,30 @@ docker build -t codesnippet-backend ./backend
docker run -p 5000:5000 -v $(pwd)/data:/data codesnippet-backend
```
Or use docker-compose:
```bash
docker-compose up -d
Then configure the frontend manually in Settings to use `http://localhost:5000`.
### Environment Variable Configuration
When deploying, you can set the `VITE_FLASK_BACKEND_URL` environment variable to automatically configure the Flask backend:
**For Docker Compose:**
```yaml
services:
frontend:
environment:
- VITE_FLASK_BACKEND_URL=http://backend:5000
```
**For Local Development (.env file):**
```bash
VITE_FLASK_BACKEND_URL=http://localhost:5000
```
When this variable is set:
- The app automatically uses Flask backend instead of IndexedDB
- Manual backend configuration in Settings is disabled
- Perfect for production deployments where backend is always available
### Backend API
The Flask backend provides a REST API:

100
README.md
View File

@@ -1,23 +1,95 @@
# ✨ Welcome to Your Spark Template!
You've just launched your brand-new Spark Template Codespace — everythings fired up and ready for you to explore, build, and create with Spark!
# CodeSnippet - Code Snippet Manager
This template is your blank canvas. It comes with a minimal setup to help you get started quickly with Spark development.
A powerful code snippet management application with flexible storage backends and an integrated component library showcase.
🚀 What's Inside?
- A clean, minimal Spark environment
- Pre-configured for local development
- Ready to scale with your ideas
## 🚀 Quick Start
🧠 What Can You Do?
Choose the setup that works best for you:
Right now, this is just a starting point — the perfect place to begin building and testing your Spark applications.
### Option 1: Full Stack with Docker (Recommended)
```bash
docker-compose up -d
```
Access at: http://localhost:3000 (auto-configured with Flask backend)
🧹 Just Exploring?
No problem! If you were just checking things out and dont need to keep this code:
### Option 2: Local Development
```bash
# Start backend
docker-compose -f docker-compose.backend-only.yml up -d
- Simply delete your Spark.
- Everything will be cleaned up — no traces left behind.
# Configure frontend
echo "VITE_FLASK_BACKEND_URL=http://localhost:5000" > .env
📄 License For Spark Template Resources
# Start frontend
npm install
npm run dev
```
Access at: http://localhost:5173
### Option 3: Frontend Only (No Backend)
```bash
npm install
npm run dev
```
Access at: http://localhost:5173 (uses local IndexedDB storage)
📖 **[See detailed setup instructions →](./QUICKSTART.md)**
## 🔑 Key Features
- 📝 **Snippet Management** - Create, edit, and organize code snippets with syntax highlighting
- 🔍 **Smart Search** - Real-time search across title, description, language, and code
- 👁️ **Live Preview** - Split-screen editor with live React component preview
- 💾 **Flexible Storage** - Choose between local IndexedDB or Flask backend
- 🔄 **Auto-Configuration** - Automatically use Flask backend via environment variable
- 🗂️ **Component Library** - Showcase organized by atomic design principles
- 📤 **Export/Import** - Backup and restore your entire database
- 🎨 **Beautiful UI** - Modern dark theme with purple and cyan accents
## 🎯 Storage Backends
CodeSnippet supports two storage backends:
### IndexedDB (Default)
- Local browser storage
- No server required
- Perfect for personal use
### Flask Backend (Optional)
- Remote server storage
- Multi-device sync
- Requires Flask backend
**🔧 Auto-Configuration:**
Set `VITE_FLASK_BACKEND_URL` environment variable to automatically use Flask backend:
```bash
# .env file
VITE_FLASK_BACKEND_URL=http://localhost:5000
```
When set, the app automatically connects to Flask backend and disables manual configuration.
📖 **[Complete backend configuration guide →](./BACKEND-CONFIG.md)**
## 📚 Documentation
- **[Quick Start Guide](./QUICKSTART.md)** - Get up and running quickly
- **[Application Guide](./README-APP.md)** - Features and usage
- **[Backend Configuration](./BACKEND-CONFIG.md)** - Detailed backend setup
- **[Backend API](./backend/README.md)** - Flask API documentation
- **[Docker Examples](./docker-compose.README.md)** - Docker deployment options
## 🛠️ Technology Stack
- React 19 + TypeScript
- SQL.js (SQLite in WebAssembly)
- Flask (Python backend)
- Monaco Editor (VS Code editor)
- Framer Motion (animations)
- Shadcn UI (component library)
- Tailwind CSS (styling)
## 📄 License
The Spark Template files and resources from GitHub are licensed under the terms of the MIT license, Copyright GitHub, Inc.

View File

@@ -67,6 +67,30 @@ Default: `/data/snippets.db`
## Frontend Integration
### Automatic Configuration (Recommended)
The frontend can be automatically configured to use the Flask backend via environment variable:
**For Docker Compose (full stack):**
```bash
docker-compose up -d
```
The frontend will automatically connect to the backend at `http://backend:5000`.
**For local development:**
```bash
# .env file in project root
VITE_FLASK_BACKEND_URL=http://localhost:5000
```
When `VITE_FLASK_BACKEND_URL` is set:
- Frontend automatically uses Flask backend
- Manual backend selection in Settings is disabled
- Perfect for production deployments
### Manual Configuration
In the CodeSnippet app:
1. Navigate to Settings page
2. Select "Flask Backend (Remote Server)"
@@ -75,6 +99,8 @@ In the CodeSnippet app:
5. Click "Save Storage Settings"
6. Optionally migrate existing IndexedDB data to Flask
See [BACKEND-CONFIG.md](../BACKEND-CONFIG.md) for detailed configuration guide.
## Docker Details
### Building the Image

71
docker-compose.README.md Normal file
View File

@@ -0,0 +1,71 @@
# Docker Compose Examples
This directory contains example Docker Compose configurations for different deployment scenarios.
## Files
- `docker-compose.yml` - Default full stack with auto-configured backend
- `docker-compose.backend-only.yml` - Backend service only
- `docker-compose.dev.yml` - Development setup with hot reload
## Usage
### Full Stack (Frontend + Backend)
```bash
docker-compose up -d
```
Access:
- Frontend: http://localhost:3000
- Backend API: http://localhost:5000
### Backend Only
```bash
docker-compose -f docker-compose.backend-only.yml up -d
```
Access:
- Backend API: http://localhost:5000
Then run frontend locally:
```bash
npm run dev
```
Configure frontend manually in Settings to use `http://localhost:5000`.
### Development Mode
```bash
docker-compose -f docker-compose.dev.yml up -d
```
This runs:
- Backend in Docker
- Frontend expects you to run `npm run dev` locally with env var set
## Environment Variables
All configurations support these environment variables:
### Backend
- `DB_PATH` - SQLite database path (default: `/data/snippets.db`)
### Frontend
- `VITE_FLASK_BACKEND_URL` - Flask backend URL (enables auto-configuration)
## Persistence
All configurations use a Docker volume `snippet-data` for persistent storage.
To backup:
```bash
docker run --rm -v codesnippet_snippet-data:/data -v $(pwd):/backup alpine tar czf /backup/snippets-backup.tar.gz /data
```
To restore:
```bash
docker run --rm -v codesnippet_snippet-data:/data -v $(pwd):/backup alpine tar xzf /backup/snippets-backup.tar.gz -C /
```

View File

@@ -0,0 +1,15 @@
version: '3.8'
services:
backend:
build: ./backend
ports:
- "5000:5000"
volumes:
- snippet-data:/data
environment:
- DB_PATH=/data/snippets.db
restart: unless-stopped
volumes:
snippet-data:

View File

@@ -11,5 +11,18 @@ services:
- DB_PATH=/data/snippets.db
restart: unless-stopped
frontend:
build:
context: .
args:
- VITE_FLASK_BACKEND_URL=http://localhost:5000
ports:
- "3000:3000"
environment:
- VITE_FLASK_BACKEND_URL=http://backend:5000
depends_on:
- backend
restart: unless-stopped
volumes:
snippet-data:

19
nginx.conf Normal file
View File

@@ -0,0 +1,19 @@
server {
listen 3000;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}

View File

@@ -2,6 +2,7 @@ import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
import { motion } from 'framer-motion'
import { Code } from '@phosphor-icons/react'
import { Navigation, NavigationProvider, NavigationSidebar, useNavigation } from '@/components/Navigation'
import { BackendIndicator } from '@/components/BackendIndicator'
import { HomePage } from '@/pages/HomePage'
import { DemoPage } from '@/pages/DemoPage'
import { AtomsPage } from '@/pages/AtomsPage'
@@ -62,6 +63,13 @@ function AppContent() {
CodeSnippet
</h1>
</motion.div>
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.4, delay: 0.1 }}
>
<BackendIndicator />
</motion.div>
</div>
</div>
</header>

View File

@@ -0,0 +1,53 @@
import { useState, useEffect } from 'react'
import { Database, CloudCheck } from '@phosphor-icons/react'
import { getStorageConfig } from '@/lib/storage'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
export function BackendIndicator() {
const [backend, setBackend] = useState<'indexeddb' | 'flask'>('indexeddb')
const [isEnvConfigured, setIsEnvConfigured] = useState(false)
useEffect(() => {
const config = getStorageConfig()
setBackend(config.backend)
setIsEnvConfigured(Boolean(import.meta.env.VITE_FLASK_BACKEND_URL))
}, [])
if (backend === 'indexeddb') {
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center gap-2 px-3 py-1.5 rounded-full bg-muted/50 border border-border">
<Database size={16} className="text-muted-foreground" />
<span className="text-xs font-medium text-muted-foreground">Local</span>
</div>
</TooltipTrigger>
<TooltipContent>
<p>Using IndexedDB (Local Storage)</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)
}
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center gap-2 px-3 py-1.5 rounded-full bg-accent/10 border border-accent/30">
<CloudCheck size={16} className="text-accent" weight="fill" />
<span className="text-xs font-medium text-accent">Backend</span>
{isEnvConfigured && (
<span className="w-1.5 h-1.5 rounded-full bg-accent" />
)}
</div>
</TooltipTrigger>
<TooltipContent>
<p>Using Flask Backend</p>
{isEnvConfigured && <p className="text-xs text-muted-foreground">Auto-configured</p>}
</TooltipContent>
</Tooltip>
</TooltipProvider>
)
}

View File

@@ -1,10 +1,11 @@
import initSqlJs, { Database } from 'sql.js'
import type { Snippet, SnippetTemplate } from './types'
import { getStorageConfig, FlaskStorageAdapter } from './storage'
import { getStorageConfig, FlaskStorageAdapter, loadStorageConfig } from './storage'
let dbInstance: Database | null = null
let sqlInstance: any = null
let flaskAdapter: FlaskStorageAdapter | null = null
let configLoaded = false
const DB_KEY = 'codesnippet-db'
const IDB_NAME = 'CodeSnippetDB'
@@ -195,6 +196,11 @@ async function saveDB() {
}
function getFlaskAdapter(): FlaskStorageAdapter | null {
if (!configLoaded) {
loadStorageConfig()
configLoaded = true
}
const config = getStorageConfig()
if (config.backend === 'flask' && config.flaskUrl) {
if (!flaskAdapter || flaskAdapter['baseUrl'] !== config.flaskUrl) {

View File

@@ -7,13 +7,33 @@ export interface StorageConfig {
flaskUrl?: string
}
let currentConfig: StorageConfig = {
backend: 'indexeddb'
}
const STORAGE_CONFIG_KEY = 'codesnippet-storage-config'
function getDefaultConfig(): StorageConfig {
const flaskUrl = import.meta.env.VITE_FLASK_BACKEND_URL
if (flaskUrl) {
return {
backend: 'flask',
flaskUrl: flaskUrl
}
}
return {
backend: 'indexeddb'
}
}
let currentConfig: StorageConfig = getDefaultConfig()
export function loadStorageConfig(): StorageConfig {
const defaultConfig = getDefaultConfig()
if (defaultConfig.backend === 'flask' && defaultConfig.flaskUrl) {
currentConfig = defaultConfig
return currentConfig
}
try {
const saved = localStorage.getItem(STORAGE_CONFIG_KEY)
if (saved) {

View File

@@ -29,6 +29,7 @@ export function SettingsPage() {
const [flaskUrl, setFlaskUrl] = useState('')
const [flaskConnectionStatus, setFlaskConnectionStatus] = useState<'unknown' | 'connected' | 'failed'>('unknown')
const [testingConnection, setTestingConnection] = useState(false)
const [envVarSet, setEnvVarSet] = useState(false)
const loadStats = async () => {
setLoading(true)
@@ -62,8 +63,13 @@ export function SettingsPage() {
useEffect(() => {
loadStats()
const config = loadStorageConfig()
const envFlaskUrl = import.meta.env.VITE_FLASK_BACKEND_URL
const isEnvSet = Boolean(envFlaskUrl)
setEnvVarSet(isEnvSet)
setStorageBackend(config.backend)
setFlaskUrl(config.flaskUrl || 'http://localhost:5000')
setFlaskUrl(config.flaskUrl || envFlaskUrl || 'http://localhost:5000')
if (config.backend === 'flask' && config.flaskUrl) {
testFlaskConnection(config.flaskUrl)
}
@@ -244,6 +250,57 @@ export function SettingsPage() {
</div>
<div className="grid gap-6 max-w-3xl">
{envVarSet && (
<Card className="border-accent">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-accent">
<CloudCheck weight="fill" size={24} />
Backend Auto-Configured
</CardTitle>
<CardDescription>
Flask backend is configured via environment variable
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-3">
<div className="flex items-center justify-between py-2">
<span className="text-sm text-muted-foreground">Backend URL</span>
<code className="text-sm font-mono bg-muted px-2 py-1 rounded">{flaskUrl}</code>
</div>
<div className="flex items-center justify-between py-2">
<span className="text-sm text-muted-foreground">Configuration Source</span>
<code className="text-sm font-mono bg-muted px-2 py-1 rounded">VITE_FLASK_BACKEND_URL</code>
</div>
<div className="flex items-center justify-between py-2">
<span className="text-sm text-muted-foreground">Status</span>
{flaskConnectionStatus === 'connected' && (
<span className="flex items-center gap-2 text-sm text-green-600">
<CloudCheck weight="fill" size={16} />
Connected
</span>
)}
{flaskConnectionStatus === 'failed' && (
<span className="flex items-center gap-2 text-sm text-destructive">
<CloudSlash weight="fill" size={16} />
Connection Failed
</span>
)}
{flaskConnectionStatus === 'unknown' && (
<Button
onClick={handleTestConnection}
variant="outline"
size="sm"
disabled={testingConnection}
>
{testingConnection ? 'Testing...' : 'Test Connection'}
</Button>
)}
</div>
</div>
</CardContent>
</Card>
)}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
@@ -255,11 +312,26 @@ export function SettingsPage() {
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<RadioGroup value={storageBackend} onValueChange={(value) => setStorageBackend(value as StorageBackend)}>
{envVarSet && (
<Alert className="border-accent bg-accent/10">
<AlertDescription className="flex items-center gap-2">
<CloudCheck weight="fill" size={16} className="text-accent" />
<span>
Storage backend is configured via <code className="px-1.5 py-0.5 rounded bg-muted text-xs font-mono">VITE_FLASK_BACKEND_URL</code> environment variable and cannot be changed here.
</span>
</AlertDescription>
</Alert>
)}
<RadioGroup
value={storageBackend}
onValueChange={(value) => setStorageBackend(value as StorageBackend)}
disabled={envVarSet}
>
<div className="flex items-start space-x-3 space-y-0">
<RadioGroupItem value="indexeddb" id="storage-indexeddb" />
<RadioGroupItem value="indexeddb" id="storage-indexeddb" disabled={envVarSet} />
<div className="flex-1">
<Label htmlFor="storage-indexeddb" className="font-semibold cursor-pointer">
<Label htmlFor="storage-indexeddb" className={`font-semibold ${envVarSet ? 'opacity-50' : 'cursor-pointer'}`}>
IndexedDB (Local Browser Storage)
</Label>
<p className="text-sm text-muted-foreground mt-1">
@@ -269,9 +341,9 @@ export function SettingsPage() {
</div>
<div className="flex items-start space-x-3 space-y-0 mt-4">
<RadioGroupItem value="flask" id="storage-flask" />
<RadioGroupItem value="flask" id="storage-flask" disabled={envVarSet} />
<div className="flex-1">
<Label htmlFor="storage-flask" className="font-semibold cursor-pointer">
<Label htmlFor="storage-flask" className={`font-semibold ${envVarSet ? 'opacity-50' : 'cursor-pointer'}`}>
Flask Backend (Remote Server)
</Label>
<p className="text-sm text-muted-foreground mt-1">
@@ -295,6 +367,7 @@ export function SettingsPage() {
setFlaskUrl(e.target.value)
setFlaskConnectionStatus('unknown')
}}
disabled={envVarSet}
/>
<Button
onClick={handleTestConnection}
@@ -342,7 +415,7 @@ export function SettingsPage() {
)}
<div className="pt-2">
<Button onClick={handleSaveStorageConfig} className="gap-2">
<Button onClick={handleSaveStorageConfig} className="gap-2" disabled={envVarSet}>
<Database weight="bold" size={16} />
Save Storage Settings
</Button>

8
src/vite-end.d.ts vendored
View File

@@ -1,3 +1,11 @@
/// <reference types="vite/client" />
declare const GITHUB_RUNTIME_PERMANENT_NAME: string
declare const BASE_KV_SERVICE_URL: string
interface ImportMetaEnv {
readonly VITE_FLASK_BACKEND_URL?: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}