## CLI ``` Usage: ./cli [options] Options: --create-user=NAME Create a new user with the given name --list-users List all users --delete-user=USER_ID Delete a user by ID --update-parsers Download all required data files ``` ## Run It Anywhere ### Docker Compose ```bash docker-compose up # Optional: Generate an api key # docker-compose exec -it app cli --create-user=Admin ``` ### Docker CLI ```bash docker run \ --name bit \ -p 4000:4000 \ -e ENV="production" \ -e DATABASE_URL="sqlite3://./sqlite/data.db" \ -e APP_URL="http://localhost:4000" \ -e ADMIN_NAME="Admin" \ -e ADMIN_API_KEY=$(openssl rand -base64 32) \ sjdonado/bit # Create a new user # docker exec -it bit cli --create-user=Admin ``` ### Dokku ```dockerfile FROM sjdonado/bit ``` ```bash dokku apps:create bit dokku domains:set bit bit.donado.co dokku letsencrypt:enable bit dokku storage:ensure-directory bit-sqlite dokku storage:mount bit /var/lib/dokku/data/storage/bit-sqlite:/usr/src/app/sqlite/ dokku config:set bit DATABASE_URL="sqlite3://./sqlite/data.db" APP_URL=https://bit.donado.co ADMIN_NAME=Admin ADMIN_API_KEY=$(openssl rand -base64 32) dokku ports:add bit http:80:4000 dokku ports:add bit https:443:4000 # Create a new user # dokku run bit cli --create-user=Admin ``` ### Dokku (same network) Recommended for lower latency communication (no host network traversal) ```bash dokku network:create bit-net dokku network:set bit attach-post-create bit-net dokku network:set myapp attach-post-create bit-net ``` ## Local Development ### Requirements - Crystal 1.18+ - Shards package manager - SQLite3 ### Install Dependencies - linux ```bash sudo apt-get update && sudo apt-get install -y crystal libssl-dev libsqlite3-dev ``` - macos ```bash brew tap amberframework/micrate brew install micrate ``` ### Install Shards and Run ```bash shards run bit ``` - Generate the `X-Api-Key` ```bash shards run cli -- --create-user=Admin ``` - Run tests ```bash ENV=test crystal spec ``` ## Benchmark ### Run ``` shards build --release --no-debug --progress --stats shards run benchmark ``` ### Output Chip: Apple M4 Pro. Memory: 24GB ``` 1762075350 ~/p/bit> shards build --release --no-debug --progress --stats shards run benchmark Dependencies are satisfied Building: bit Parse: 00:00:00.000652375 ( 1.17MB) Semantic (top level): 00:00:00.419246250 ( 163.45MB) Semantic (new): 00:00:00.001636125 ( 163.45MB) Semantic (type declarations): 00:00:00.019569792 ( 179.45MB) Semantic (abstract def check): 00:00:00.009145125 ( 195.45MB) Semantic (restrictions augmenter): 00:00:00.008421709 ( 195.45MB) Semantic (ivars initializers): 00:00:00.019696584 ( 211.45MB) Semantic (cvars initializers): 00:00:00.106829666 ( 211.50MB) Semantic (main): 00:00:00.649298375 ( 499.88MB) Semantic (cleanup): 00:00:00.000765250 ( 499.88MB) Semantic (recursive struct check): 00:00:00.000752250 ( 499.88MB) Codegen (crystal): 00:00:00.521307417 ( 532.38MB) Codegen (bc+obj): 00:00:00.143842542 ( 532.38MB) Codegen (linking): 00:00:00.236228750 ( 532.38MB) Macro runs: - /opt/homebrew/Cellar/crystal/1.18.2/share/crystal/src/ecr/process.cr: reused previous compilation (00:00:00.003593375) Codegen (bc+obj): - all previous .o files were reused Building: cli Parse: 00:00:00.000053291 ( 1.17MB) Semantic (top level): 00:00:00.323534167 ( 163.45MB) Semantic (new): 00:00:00.001705500 ( 163.45MB) Semantic (type declarations): 00:00:00.018311958 ( 179.45MB) Semantic (abstract def check): 00:00:00.007766750 ( 195.45MB) Semantic (restrictions augmenter): 00:00:00.005686667 ( 195.45MB) Semantic (ivars initializers): 00:00:00.011239792 ( 211.45MB) Semantic (cvars initializers): 00:00:00.100870833 ( 211.50MB) Semantic (main): 00:00:00.285426750 ( 371.62MB) Semantic (cleanup): 00:00:00.000369875 ( 371.62MB) Semantic (recursive struct check): 00:00:00.000570917 ( 371.62MB) Codegen (crystal): 00:00:00.317534875 ( 387.88MB) Codegen (bc+obj): 00:00:00.097321417 ( 387.88MB) Codegen (linking): 00:00:00.095931000 ( 387.88MB) Codegen (bc+obj): - all previous .o files were reused Building: benchmark Parse: 00:00:00.000228500 ( 1.17MB) Semantic (top level): 00:00:00.242174458 ( 147.78MB) Semantic (new): 00:00:00.000863333 ( 147.78MB) Semantic (type declarations): 00:00:00.011527792 ( 147.78MB) Semantic (abstract def check): 00:00:00.031242333 ( 147.78MB) Semantic (restrictions augmenter): 00:00:00.003593583 ( 147.78MB) Semantic (ivars initializers): 00:00:00.006753667 ( 147.78MB) Semantic (cvars initializers): 00:00:00.028373834 ( 195.78MB) Semantic (main): 00:00:00.152039542 ( 243.83MB) Semantic (cleanup): 00:00:00.000249084 ( 243.83MB) Semantic (recursive struct check): 00:00:00.000460417 ( 243.83MB) Codegen (crystal): 00:00:00.075461000 ( 259.83MB) Codegen (bc+obj): 00:00:04.834914333 ( 259.83MB) Codegen (linking): 00:00:00.119920416 ( 259.83MB) Codegen (bc+obj): - no previous .o files were reused Dependencies are satisfied Building: benchmark Executing: benchmark Cleaning up benchmark database... Deleted existing database: ./sqlite/data.benchmark.db Database cleanup completed. Running database migrations... Migrating db, current version: 0, target: 20250319192003 OK 20240512214223_create_links.sql OK 20240512225208_add_slug_index_to_links.sql OK 20240513115731_create_users.sql OK 20240513130054_add_api_key_index_to_users.sql OK 20240711224103_create_clicks.sql OK 20240714215409_update_slug_size_links.sql OK 20250316102350_add_country_to_clicks.sql OK 20250316111734_replace_unkwown_with_null.sql OK 20250318072657_replace_slug_index_with_covering_index.sql OK 20250319192003_convert_all_tables_text_ids_to_integer.sql Migrations completed successfully. Seeding benchmark database... Database seeded successfully. Starting application: ./bit... Application output will be saved to: app_output.log Application started with PID: 11638 Using database: ./sqlite/data.benchmark.db Checking if server is ready at http://localhost:4001... .Server is ready! Fetching links from API... Selected link: http://localhost:4001/slug9391 Starting benchmark with 100000 requests... Bombarding http://localhost:4001/slug9391 with 100000 request(s) using 125 connection(s) 100000 / 100000 [============================================================================] 100.00% 11078/s 9s Done! Statistics Avg Stdev Max Reqs/sec 11427.28 8889.68 30270.91 Latency 11.02ms 6.55ms 53.91ms Latency Distribution 50% 1.85ms 75% 5.37ms 90% 39.36ms 95% 39.87ms 99% 42.66ms HTTP codes: 1xx - 0, 2xx - 0, 3xx - 100000, 4xx - 0, 5xx - 0 others - 0 Throughput: 3.08MB/s Benchmark completed successfully. **** Resource Usage Statistics **** Measurements: 12 Average CPU Usage: 71.5% Average Memory Usage: 39.8 MiB Peak CPU Usage: 100.0% Peak Memory Usage: 53.41 MiB **** Files Generated **** Resource stats: resource_usage.log Application log: app_output.log Database: ./sqlite/data.benchmark.db Stopping application... Application stopped. ```