diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7425de8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Local development +clickhouse-data/ +config.local.js + +# Docker +docker-compose.override.yml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..f90ff72 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,153 @@ +# Local Development Setup + +This guide explains how to run Pastila locally with a Docker-based ClickHouse instance. + +## Prerequisites + +- Docker and Docker Compose +- Python 3 (for the HTTP server) + +## Quick Start + +1. **Start ClickHouse in Docker:** + ```bash + docker-compose up -d + ``` + + This will: + - Start a local ClickHouse server on ports 18123 (HTTP) and 19000 (native) + - Automatically run the initialization script (`init-db.sql`) + - Create the database schema, users, quotas, and views + +2. **Wait for ClickHouse to be ready:** + ```bash + # Check if ClickHouse is healthy + docker-compose ps + + # Or watch the logs + docker-compose logs -f clickhouse + ``` + +3. **Start the local web server:** + ```bash + python3 -m http.server 8080 + ``` + +4. **Open in browser:** + ``` + http://localhost:8080 + ``` + + The application will automatically detect it's running locally and connect to `http://localhost:18123`. + +## Configuration + +### Automatic Environment Detection + +The `config.js` file automatically detects if you're running locally: +- **Local development** (localhost/127.0.0.1): Uses `http://localhost:18123/?user=paste` +- **Production** (pastila.nl): Uses the ClickHouse Cloud URL + +### Manual Override + +You can manually override the ClickHouse URL in the browser console: + +```javascript +localStorage.setItem('clickhouse_url', 'http://your-custom-url:18123/?user=paste'); +// Then reload the page +``` + +To clear the override: +```javascript +localStorage.removeItem('clickhouse_url'); +``` + +## Useful Commands + +### Check ClickHouse is running +```bash +curl http://localhost:18123/ +# Should return "Ok." +``` + +### Query the database directly +```bash +docker-compose exec clickhouse clickhouse-client +``` + +```sql +-- Show databases +SHOW DATABASES; + +-- Show tables in paste database +SHOW TABLES FROM paste; + +-- View recent pastes +SELECT fingerprint, hash, length(content) as size, time +FROM paste.data +ORDER BY time DESC +LIMIT 10; +``` + +### View logs +```bash +docker-compose logs -f clickhouse +``` + +### Stop services +```bash +docker-compose down +``` + +### Clean up (removes all data) +```bash +docker-compose down -v +rm -rf clickhouse-data/ +``` + +## Database Schema + +The database schema is defined in `init-db.sql` and includes: +- **paste.data** table with constraints for hash validation, size limits, and spam prevention +- **paste.data_view** for access control +- **paste** and **paste_sys** users with specific permissions +- **Quotas** for rate limiting + +## Differences from Production + +The local setup uses: +- **MergeTree** engine instead of **ReplicatedMergeTree** (single node) +- **No SSL** (http:// instead of https://) +- **Docker-based** ClickHouse instead of ClickHouse Cloud + +## Troubleshooting + +### Port 18123 or 19000 already in use +The default setup uses ports 18123 (HTTP) and 19000 (native) to avoid conflicts with other services. + +If you need to use different ports, modify `docker-compose.yml`: +```yaml +ports: + - "28123:8123" # Use port 28123 instead + - "29000:9000" # Use port 29000 instead +``` + +Then update your manual override: +```javascript +localStorage.setItem('clickhouse_url', 'http://localhost:28123/?user=paste'); +``` + +### Database initialization fails +Check the logs: +```bash +docker-compose logs clickhouse +``` + +You can manually re-run the initialization: +```bash +docker-compose exec clickhouse clickhouse-client < init-db.sql +``` + +### CORS errors +Make sure you're accessing via `http://localhost:8080` (or similar) and not `file://`. +The ClickHouse user has `add_http_cors_header = 1` enabled. diff --git a/README.md b/README.md index fb63018..00a7cbb 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Features: previous version of the data is available by clicking the "back" button in bottom right corner; - Added Encryption option to encrypt data in browser before inserting into ClickHouse database, encryption key is kept in anchor tag which never leaves the user's browser. +- Burn after reading: documents can be viewed only once, then automatically deleted - all intermediate versions are also deleted when enabling this feature. ## Motivation @@ -35,6 +36,8 @@ and unusual scenarios to find potential flaws and to explore new possibilities. ## Contributing +For local development setup with Docker, see [CONTRIBUTING.md](CONTRIBUTING.md). + Please send a pull request to https://github.com/ClickHouse/pastila @@ -82,6 +85,7 @@ CREATE TABLE paste.data prev_fingerprint_hex String EPHEMERAL '', prev_hash_hex String EPHEMERAL '', is_encrypted UInt8, + burn_after_reading UInt8 DEFAULT 0, CONSTRAINT length CHECK length(content) < 10 * 1024 * 1024, CONSTRAINT hash_is_correct CHECK sipHash128(content) = reinterpretAsFixedString(hash), @@ -126,8 +130,9 @@ TO paste; CREATE VIEW paste.data_view DEFINER = 'paste_sys' AS SELECT * FROM paste.data WHERE fingerprint = reinterpretAsUInt32(unhex({fingerprint:String})) AND hash = reinterpretAsUInt128(unhex({hash:String})) ORDER BY time LIMIT 1; +-- Grant permissions GRANT INSERT ON paste.data TO paste; GRANT SELECT ON paste.data_view TO paste; - +GRANT ALTER UPDATE ON paste.data TO paste; GRANT SELECT ON paste.data TO paste_sys; ``` diff --git a/config.js b/config.js new file mode 100644 index 0000000..ab38c1a --- /dev/null +++ b/config.js @@ -0,0 +1,26 @@ +// ClickHouse Configuration +// This file is loaded before the main script and configures the ClickHouse URL + +// Auto-detect environment based on hostname +const isLocalDevelopment = window.location.hostname === 'localhost' || + window.location.hostname === '127.0.0.1' || + window.location.hostname === ''; + +// Configure ClickHouse URL based on environment +// For local development, this points to local Docker instance +// For production, this points to ClickHouse Cloud +const detectedUrl = isLocalDevelopment + ? "http://localhost:18123/?user=paste" + : "https://uzg8q0g12h.eu-central-1.aws.clickhouse.cloud/?user=paste"; + +// You can also manually override by setting localStorage: +// localStorage.setItem('clickhouse_url', 'http://your-custom-url:8123/?user=paste'); +const manual_override = localStorage.getItem('clickhouse_url'); +if (manual_override) { + console.log('Using manual ClickHouse URL override:', manual_override); +} + +// Export the configured URL +window.CLICKHOUSE_URL = manual_override || detectedUrl; + +console.log('ClickHouse URL configured:', window.CLICKHOUSE_URL); diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..18b940e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,23 @@ +version: '3.8' + +services: + clickhouse: + image: clickhouse/clickhouse-server:latest + container_name: pastila-clickhouse + ports: + - "18123:8123" # HTTP interface + - "19000:9000" # Native protocol + volumes: + - ./clickhouse-data:/var/lib/clickhouse + - ./init-db.sql:/docker-entrypoint-initdb.d/init-db.sql + environment: + CLICKHOUSE_DB: paste + ulimits: + nofile: + soft: 262144 + hard: 262144 + healthcheck: + test: ["CMD", "clickhouse-client", "--query", "SELECT 1"] + interval: 10s + timeout: 5s + retries: 5 diff --git a/index.html b/index.html index 7b46b6a..b9750b7 100644 --- a/index.html +++ b/index.html @@ -7,6 +7,7 @@