mirror of
https://github.com/johndoe6345789/snippet-pastebin.git
synced 2026-04-24 13:34:55 +00:00
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:
16
.dockerignore
Normal file
16
.dockerignore
Normal 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
4
.env.example
Normal 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
260
BACKEND-CONFIG.md
Normal 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
25
Dockerfile
Normal 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
202
IMPLEMENTATION.md
Normal 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
267
QUICKSTART.md
Normal 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)
|
||||||
@@ -34,6 +34,8 @@ CodeSnippet offers flexible data storage with two backend options:
|
|||||||
|
|
||||||
### Switching Storage Backends
|
### Switching Storage Backends
|
||||||
|
|
||||||
|
#### Manual Configuration
|
||||||
|
|
||||||
Visit the **Settings** page to:
|
Visit the **Settings** page to:
|
||||||
- Choose between IndexedDB and Flask backend
|
- Choose between IndexedDB and Flask backend
|
||||||
- Configure Flask backend URL
|
- Configure Flask backend URL
|
||||||
@@ -42,6 +44,31 @@ Visit the **Settings** page to:
|
|||||||
- View database statistics
|
- View database statistics
|
||||||
- Export/import database backups
|
- 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
|
## Backend Setup
|
||||||
|
|
||||||
### Running Flask Backend Locally
|
### Running Flask Backend Locally
|
||||||
@@ -54,7 +81,24 @@ python app.py
|
|||||||
|
|
||||||
Server runs on `http://localhost:5000` by default.
|
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:
|
Build and run:
|
||||||
```bash
|
```bash
|
||||||
@@ -62,11 +106,30 @@ docker build -t codesnippet-backend ./backend
|
|||||||
docker run -p 5000:5000 -v $(pwd)/data:/data codesnippet-backend
|
docker run -p 5000:5000 -v $(pwd)/data:/data codesnippet-backend
|
||||||
```
|
```
|
||||||
|
|
||||||
Or use docker-compose:
|
Then configure the frontend manually in Settings to use `http://localhost:5000`.
|
||||||
```bash
|
|
||||||
docker-compose up -d
|
### 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
|
### Backend API
|
||||||
|
|
||||||
The Flask backend provides a REST API:
|
The Flask backend provides a REST API:
|
||||||
|
|||||||
102
README.md
102
README.md
@@ -1,23 +1,95 @@
|
|||||||
# ✨ Welcome to Your Spark Template!
|
# CodeSnippet - Code Snippet Manager
|
||||||
You've just launched your brand-new Spark Template Codespace — everything’s fired up and ready for you to explore, build, and create with Spark!
|
|
||||||
|
|
||||||
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?
|
## 🚀 Quick Start
|
||||||
- A clean, minimal Spark environment
|
|
||||||
- Pre-configured for local development
|
|
||||||
- Ready to scale with your ideas
|
|
||||||
|
|
||||||
🧠 What Can You Do?
|
|
||||||
|
|
||||||
Right now, this is just a starting point — the perfect place to begin building and testing your Spark applications.
|
Choose the setup that works best for you:
|
||||||
|
|
||||||
🧹 Just Exploring?
|
### Option 1: Full Stack with Docker (Recommended)
|
||||||
No problem! If you were just checking things out and don’t need to keep this code:
|
```bash
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
Access at: http://localhost:3000 (auto-configured with Flask backend)
|
||||||
|
|
||||||
- Simply delete your Spark.
|
### Option 2: Local Development
|
||||||
- Everything will be cleaned up — no traces left behind.
|
```bash
|
||||||
|
# Start backend
|
||||||
|
docker-compose -f docker-compose.backend-only.yml up -d
|
||||||
|
|
||||||
📄 License For Spark Template Resources
|
# Configure frontend
|
||||||
|
echo "VITE_FLASK_BACKEND_URL=http://localhost:5000" > .env
|
||||||
|
|
||||||
|
# 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.
|
The Spark Template files and resources from GitHub are licensed under the terms of the MIT license, Copyright GitHub, Inc.
|
||||||
|
|||||||
@@ -67,6 +67,30 @@ Default: `/data/snippets.db`
|
|||||||
|
|
||||||
## Frontend Integration
|
## 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:
|
In the CodeSnippet app:
|
||||||
1. Navigate to Settings page
|
1. Navigate to Settings page
|
||||||
2. Select "Flask Backend (Remote Server)"
|
2. Select "Flask Backend (Remote Server)"
|
||||||
@@ -75,6 +99,8 @@ In the CodeSnippet app:
|
|||||||
5. Click "Save Storage Settings"
|
5. Click "Save Storage Settings"
|
||||||
6. Optionally migrate existing IndexedDB data to Flask
|
6. Optionally migrate existing IndexedDB data to Flask
|
||||||
|
|
||||||
|
See [BACKEND-CONFIG.md](../BACKEND-CONFIG.md) for detailed configuration guide.
|
||||||
|
|
||||||
## Docker Details
|
## Docker Details
|
||||||
|
|
||||||
### Building the Image
|
### Building the Image
|
||||||
|
|||||||
71
docker-compose.README.md
Normal file
71
docker-compose.README.md
Normal 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 /
|
||||||
|
```
|
||||||
15
docker-compose.backend-only.yml
Normal file
15
docker-compose.backend-only.yml
Normal 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:
|
||||||
@@ -11,5 +11,18 @@ services:
|
|||||||
- DB_PATH=/data/snippets.db
|
- DB_PATH=/data/snippets.db
|
||||||
restart: unless-stopped
|
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:
|
volumes:
|
||||||
snippet-data:
|
snippet-data:
|
||||||
|
|||||||
19
nginx.conf
Normal file
19
nginx.conf
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
|
|||||||
import { motion } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
import { Code } from '@phosphor-icons/react'
|
import { Code } from '@phosphor-icons/react'
|
||||||
import { Navigation, NavigationProvider, NavigationSidebar, useNavigation } from '@/components/Navigation'
|
import { Navigation, NavigationProvider, NavigationSidebar, useNavigation } from '@/components/Navigation'
|
||||||
|
import { BackendIndicator } from '@/components/BackendIndicator'
|
||||||
import { HomePage } from '@/pages/HomePage'
|
import { HomePage } from '@/pages/HomePage'
|
||||||
import { DemoPage } from '@/pages/DemoPage'
|
import { DemoPage } from '@/pages/DemoPage'
|
||||||
import { AtomsPage } from '@/pages/AtomsPage'
|
import { AtomsPage } from '@/pages/AtomsPage'
|
||||||
@@ -62,6 +63,13 @@ function AppContent() {
|
|||||||
CodeSnippet
|
CodeSnippet
|
||||||
</h1>
|
</h1>
|
||||||
</motion.div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
53
src/components/BackendIndicator.tsx
Normal file
53
src/components/BackendIndicator.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
import initSqlJs, { Database } from 'sql.js'
|
import initSqlJs, { Database } from 'sql.js'
|
||||||
import type { Snippet, SnippetTemplate } from './types'
|
import type { Snippet, SnippetTemplate } from './types'
|
||||||
import { getStorageConfig, FlaskStorageAdapter } from './storage'
|
import { getStorageConfig, FlaskStorageAdapter, loadStorageConfig } from './storage'
|
||||||
|
|
||||||
let dbInstance: Database | null = null
|
let dbInstance: Database | null = null
|
||||||
let sqlInstance: any = null
|
let sqlInstance: any = null
|
||||||
let flaskAdapter: FlaskStorageAdapter | null = null
|
let flaskAdapter: FlaskStorageAdapter | null = null
|
||||||
|
let configLoaded = false
|
||||||
|
|
||||||
const DB_KEY = 'codesnippet-db'
|
const DB_KEY = 'codesnippet-db'
|
||||||
const IDB_NAME = 'CodeSnippetDB'
|
const IDB_NAME = 'CodeSnippetDB'
|
||||||
@@ -195,6 +196,11 @@ async function saveDB() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getFlaskAdapter(): FlaskStorageAdapter | null {
|
function getFlaskAdapter(): FlaskStorageAdapter | null {
|
||||||
|
if (!configLoaded) {
|
||||||
|
loadStorageConfig()
|
||||||
|
configLoaded = true
|
||||||
|
}
|
||||||
|
|
||||||
const config = getStorageConfig()
|
const config = getStorageConfig()
|
||||||
if (config.backend === 'flask' && config.flaskUrl) {
|
if (config.backend === 'flask' && config.flaskUrl) {
|
||||||
if (!flaskAdapter || flaskAdapter['baseUrl'] !== config.flaskUrl) {
|
if (!flaskAdapter || flaskAdapter['baseUrl'] !== config.flaskUrl) {
|
||||||
|
|||||||
@@ -7,13 +7,33 @@ export interface StorageConfig {
|
|||||||
flaskUrl?: string
|
flaskUrl?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
let currentConfig: StorageConfig = {
|
|
||||||
backend: 'indexeddb'
|
|
||||||
}
|
|
||||||
|
|
||||||
const STORAGE_CONFIG_KEY = 'codesnippet-storage-config'
|
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 {
|
export function loadStorageConfig(): StorageConfig {
|
||||||
|
const defaultConfig = getDefaultConfig()
|
||||||
|
|
||||||
|
if (defaultConfig.backend === 'flask' && defaultConfig.flaskUrl) {
|
||||||
|
currentConfig = defaultConfig
|
||||||
|
return currentConfig
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const saved = localStorage.getItem(STORAGE_CONFIG_KEY)
|
const saved = localStorage.getItem(STORAGE_CONFIG_KEY)
|
||||||
if (saved) {
|
if (saved) {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export function SettingsPage() {
|
|||||||
const [flaskUrl, setFlaskUrl] = useState('')
|
const [flaskUrl, setFlaskUrl] = useState('')
|
||||||
const [flaskConnectionStatus, setFlaskConnectionStatus] = useState<'unknown' | 'connected' | 'failed'>('unknown')
|
const [flaskConnectionStatus, setFlaskConnectionStatus] = useState<'unknown' | 'connected' | 'failed'>('unknown')
|
||||||
const [testingConnection, setTestingConnection] = useState(false)
|
const [testingConnection, setTestingConnection] = useState(false)
|
||||||
|
const [envVarSet, setEnvVarSet] = useState(false)
|
||||||
|
|
||||||
const loadStats = async () => {
|
const loadStats = async () => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
@@ -62,8 +63,13 @@ export function SettingsPage() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadStats()
|
loadStats()
|
||||||
const config = loadStorageConfig()
|
const config = loadStorageConfig()
|
||||||
|
|
||||||
|
const envFlaskUrl = import.meta.env.VITE_FLASK_BACKEND_URL
|
||||||
|
const isEnvSet = Boolean(envFlaskUrl)
|
||||||
|
setEnvVarSet(isEnvSet)
|
||||||
|
|
||||||
setStorageBackend(config.backend)
|
setStorageBackend(config.backend)
|
||||||
setFlaskUrl(config.flaskUrl || 'http://localhost:5000')
|
setFlaskUrl(config.flaskUrl || envFlaskUrl || 'http://localhost:5000')
|
||||||
if (config.backend === 'flask' && config.flaskUrl) {
|
if (config.backend === 'flask' && config.flaskUrl) {
|
||||||
testFlaskConnection(config.flaskUrl)
|
testFlaskConnection(config.flaskUrl)
|
||||||
}
|
}
|
||||||
@@ -244,6 +250,57 @@ export function SettingsPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid gap-6 max-w-3xl">
|
<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>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="flex items-center gap-2">
|
<CardTitle className="flex items-center gap-2">
|
||||||
@@ -255,11 +312,26 @@ export function SettingsPage() {
|
|||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-6">
|
<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">
|
<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">
|
<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)
|
IndexedDB (Local Browser Storage)
|
||||||
</Label>
|
</Label>
|
||||||
<p className="text-sm text-muted-foreground mt-1">
|
<p className="text-sm text-muted-foreground mt-1">
|
||||||
@@ -269,9 +341,9 @@ export function SettingsPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-start space-x-3 space-y-0 mt-4">
|
<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">
|
<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)
|
Flask Backend (Remote Server)
|
||||||
</Label>
|
</Label>
|
||||||
<p className="text-sm text-muted-foreground mt-1">
|
<p className="text-sm text-muted-foreground mt-1">
|
||||||
@@ -295,6 +367,7 @@ export function SettingsPage() {
|
|||||||
setFlaskUrl(e.target.value)
|
setFlaskUrl(e.target.value)
|
||||||
setFlaskConnectionStatus('unknown')
|
setFlaskConnectionStatus('unknown')
|
||||||
}}
|
}}
|
||||||
|
disabled={envVarSet}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
onClick={handleTestConnection}
|
onClick={handleTestConnection}
|
||||||
@@ -342,7 +415,7 @@ export function SettingsPage() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="pt-2">
|
<div className="pt-2">
|
||||||
<Button onClick={handleSaveStorageConfig} className="gap-2">
|
<Button onClick={handleSaveStorageConfig} className="gap-2" disabled={envVarSet}>
|
||||||
<Database weight="bold" size={16} />
|
<Database weight="bold" size={16} />
|
||||||
Save Storage Settings
|
Save Storage Settings
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
10
src/vite-end.d.ts
vendored
10
src/vite-end.d.ts
vendored
@@ -1,3 +1,11 @@
|
|||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
declare const GITHUB_RUNTIME_PERMANENT_NAME: string
|
declare const GITHUB_RUNTIME_PERMANENT_NAME: string
|
||||||
declare const BASE_KV_SERVICE_URL: string
|
declare const BASE_KV_SERVICE_URL: string
|
||||||
|
|
||||||
|
interface ImportMetaEnv {
|
||||||
|
readonly VITE_FLASK_BACKEND_URL?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImportMeta {
|
||||||
|
readonly env: ImportMetaEnv
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user