From 77ce6315a5e943d24e2790dfee5101325dc7b47a Mon Sep 17 00:00:00 2001 From: 24c02 <163450896+24c02@users.noreply.github.com> Date: Fri, 30 Jan 2026 13:36:50 -0500 Subject: [PATCH] read me --- .env.example | 68 +++++++++++++++---- README.md | 188 +++++++++++++++++++++++---------------------------- 2 files changed, 142 insertions(+), 114 deletions(-) diff --git a/.env.example b/.env.example index aee85de..f85a825 100644 --- a/.env.example +++ b/.env.example @@ -1,14 +1,58 @@ -# S3 Config CF in this example -AWS_ACCESS_KEY_ID=1234567890abcdef -AWS_SECRET_ACCESS_KEY=abcdef1234567890 -AWS_BUCKET_NAME=my-cdn-bucket -AWS_REGION=auto -AWS_ENDPOINT=https://.r2.cloudflarestorage.com -AWS_CDN_URL=https://cdn.beans.com +# ============================================================================= +# Cloudflare R2 Storage (S3-compatible) +# ============================================================================= +R2_ACCESS_KEY_ID=your_access_key_id +R2_SECRET_ACCESS_KEY=your_secret_access_key +R2_BUCKET_NAME=your-bucket-name +R2_ENDPOINT=https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com -# API -API_TOKEN=beans # Set a secure random string -PORT=3000 +# Public hostname for CDN URLs (used in generated links) +CDN_HOST=cdn.hackclub.com -# Sentry -SENTRY_DSN=https://your-sentry-dsn@sentry.io/project-id \ No newline at end of file +# ============================================================================= +# Hack Club OAuth +# ============================================================================= +# Get credentials from Hack Club Auth (https://auth.hackclub.com) +HACKCLUB_CLIENT_ID=your_client_id +HACKCLUB_CLIENT_SECRET=your_client_secret +# Optional: Override auth URL (defaults to staging in dev, production in prod) +# HACKCLUB_AUTH_URL=https://auth.hackclub.com + +# ============================================================================= +# Encryption Keys +# ============================================================================= +# Generate with: ruby -e "require 'securerandom'; puts SecureRandom.hex(32)" +LOCKBOX_MASTER_KEY=0000000000000000000000000000000000000000000000000000000000000000 +BLIND_INDEX_MASTER_KEY=0000000000000000000000000000000000000000000000000000000000000000 + +# Active Record Encryption (generate with: bin/rails db:encryption:init) +ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=your_primary_key +ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=your_deterministic_key +ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=your_key_derivation_salt + +# ============================================================================= +# Database (production only - dev uses cdn_development/cdn_test) +# ============================================================================= +# DATABASE_HOST=localhost +# DATABASE_USER=cdn +# DATABASE_PASSWORD=your_password +# DATABASE_NAME=cdn_production + +# Solid Cache/Queue/Cable databases (optional, defaults provided) +# CACHE_DATABASE_HOST=localhost +# QUEUE_DATABASE_HOST=localhost +# CABLE_DATABASE_HOST=localhost + +# ============================================================================= +# Optional +# ============================================================================= +# Sentry error tracking +# SENTRY_DSN=https://your-sentry-dsn@sentry.io/project-id + +# HashID salt (defaults to SECRET_KEY_BASE if not set) +# HASHID_SALT=your_hashid_salt + +# Rails configuration +# PORT=3000 +# RAILS_MAX_THREADS=5 +# SECRET_KEY_BASE=your_secret_key_base diff --git a/README.md b/README.md index 2c1b1f0..166f6d8 100644 --- a/README.md +++ b/README.md @@ -14,122 +14,106 @@ +--- -## 📡 API Usage +A Rails 8 application for hosting and managing CDN uploads, with OAuth authentication via Hack Club. -- All API endpoints require authentication via `Authorization: Bearer api-token` header -- Use the API_TOKEN from your environment configuration -- Failure to include a valid token will result in 401 Unauthorized responses +## Prerequisites -### V3 API (Latest) -Version 3 +- Ruby 3.4.4 (see `.ruby-version`) +- PostgreSQL +- Node.js + Yarn (for Vite frontend) +- A Cloudflare R2 bucket (or S3-compatible storage) -**Endpoint:** `POST https://cdn.hackclub.com/api/v3/new` +## Setup -**Headers:** -``` -Authorization: Bearer api-token -Content-Type: application/json -``` +1. **Clone and install dependencies:** + ```bash + git clone https://github.com/hackclub/cdn.git + cd cdn + bundle install + yarn install + ``` -**Request Example:** +2. **Configure environment variables:** + ```bash + cp .env.example .env + ``` + Edit `.env` with your credentials (see below for details). + +3. **Setup the database:** + ```bash + bin/rails db:create db:migrate + ``` + +4. **Generate encryption keys** (for API key encryption): + ```bash + # Generate a 32-byte hex key for Lockbox + ruby -e "require 'securerandom'; puts SecureRandom.hex(32)" + + # Generate a 32-byte hex key for BlindIndex + ruby -e "require 'securerandom'; puts SecureRandom.hex(32)" + + # Generate Active Record encryption keys + bin/rails db:encryption:init + ``` + +5. **Start the development servers:** + ```bash + # In one terminal, run the Vite dev server: + bin/vite dev + + # In another terminal, run the Rails server: + bin/rails server + ``` + +## Environment Variables + +See `.env.example` for the full list. Key variables: + +| Variable | Description | +|----------|-------------| +| `R2_ACCESS_KEY_ID` | Cloudflare R2 access key | +| `R2_SECRET_ACCESS_KEY` | Cloudflare R2 secret key | +| `R2_BUCKET_NAME` | R2 bucket name | +| `R2_ENDPOINT` | R2 endpoint URL | +| `CDN_HOST` | Public hostname for CDN URLs | +| `HACKCLUB_CLIENT_ID` | OAuth client ID from Hack Club Auth | +| `HACKCLUB_CLIENT_SECRET` | OAuth client secret | +| `LOCKBOX_MASTER_KEY` | 64-char hex key for encrypting API keys | +| `BLIND_INDEX_MASTER_KEY` | 64-char hex key for searchable encryption | + +## API + +The API uses bearer token authentication. Create an API key from the web dashboard after logging in. + +**Upload a file:** ```bash -curl --location 'https://cdn.hackclub.com/api/v3/new' \ ---header 'Authorization: Bearer beans' \ ---header 'Content-Type: application/json' \ ---data '[ - "https://assets.hackclub.com/flag-standalone.svg", - "https://assets.hackclub.com/flag-orpheus-left.png" -]' +curl -X POST https://cdn.hackclub.com/api/v4/upload \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -F "file=@image.png" ``` -**Response:** -```json -{ - "files": [ - { - "deployedUrl": "https://hc-cdn.hel1.your-objectstorage.com/s/v3/64a9472006c4472d7ac75f2d4d9455025d9838d6_flag-standalone.svg", - "file": "0_64a9472006c4472d7ac75f2d4d9455025d9838d6_flag-standalone.svg", - "sha": "64a9472006c4472d7ac75f2d4d9455025d9838d6", - "size": 4365 - }, - { - "deployedUrl": "https://hc-cdn.hel1.your-objectstorage.com/s/v3/d926bfd9811ebfe9172187793a171a5cbcc61992_flag-orpheus-left.png", - "file": "1_d926bfd9811ebfe9172187793a171a5cbcc61992_flag-orpheus-left.png", - "sha": "d926bfd9811ebfe9172187793a171a5cbcc61992", - "size": 8126 - } - ], - "cdnBase": "https://hc-cdn.hel1.your-objectstorage.com" -} +**Upload from URL:** +```bash +curl -X POST https://cdn.hackclub.com/api/v4/upload_from_url \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"url": "https://example.com/image.png"}' ``` -
-V2 API +See `/docs` in the running app for full API documentation. -Version 2 +## Architecture -**Endpoint:** `POST https://cdn.hackclub.com/api/v2/new` - -**Headers:** -``` -Authorization: Bearer api-token -Content-Type: application/json -``` - -**Request Example:** -```json -[ - "https://assets.hackclub.com/flag-standalone.svg", - "https://assets.hackclub.com/flag-orpheus-left.png" -] -``` - -**Response:** -```json -{ - "flag-standalone.svg": "https://cdn.example.dev/s/v2/flag-standalone.svg", - "flag-orpheus-left.png": "https://cdn.example.dev/s/v2/flag-orpheus-left.png" -} -``` -
- -
-V1 API - -Version 1 - -**Endpoint:** `POST https://cdn.hackclub.com/api/v1/new` - -**Headers:** -``` -Authorization: Bearer api-token -Content-Type: application/json -``` - -**Request Example:** -```json -[ - "https://assets.hackclub.com/flag-standalone.svg", - "https://assets.hackclub.com/flag-orpheus-left.png" -] -``` - -**Response:** -```json -[ - "https://cdn.example.dev/s/v1/0_flag-standalone.svg", - "https://cdn.example.dev/s/v1/1_flag-orpheus-left.png" -] -``` -
- -# Technical Details - -- **Storage Structure:** `/s/v3/{HASH}_{filename}` -- **File Naming:** `/s/{slackUserId}/{unix}_{sanitizedFilename}` +- **Rails 8** with **Vite** for frontend assets +- **Phlex** + **Primer ViewComponents** for UI +- **Active Storage** with Cloudflare R2 backend +- **Solid Queue/Cache/Cable** for background jobs and caching (production) +- **Pundit** for authorization +- **Lockbox + BlindIndex** for API key encryption

Made with 💜 for Hack Club

-
\ No newline at end of file +