Skip to content

Repository files navigation

tools.frrazers.com

Small web tools by Frazer Searle, served at tools.frrazers.com via Cloudflare Pages.

Structure

site/ is the deployed tree and the single source of truth — edit pages there directly.

site/
  index.html                 landing page
  mobile-buttons/            Roblox mobile button sheet generator
  thumbnails/                Thumbforge — AI Roblox thumbnail studio
  dataset/                   Icons & Thumbnail Dataset download page
  roblox-devproducts/        Dev Products MCP install page
  roblox-analytics/          Analytics MCP install page
  theme.css / theme.js       shared frrazers.com design system + theme toggle
  logo.svg, favicons/        shared branding assets
  _redirects                 /install.* + /roblox-analytics/install.* → GitHub raw scripts

blank-template.png and jump-template.png are the source assets embedded as base64 inside site/mobile-buttons/index.html.

Dataset

The Icons & Thumbnail Dataset page is built from C:\Users\Frazer\Desktop\GAME THUMBNAILS (icons/ + thumbs/) by the scripts in tools/, each a self-contained uv script — uv run tools/<name>.py:

build_zips.py             icons/thumbs/both → dist-dataset/*.zip (Zip64, stored)
build_previews.py         samples 72 icons + 12 thumbs → site/dataset/previews/
dataset-page.template.html  page source of truth, with __PLACEHOLDER__ tokens
build_page.py             template + previews + real zip sizes → site/dataset/index.html
serve.py                  local preview of site/ on :8788, /dataset/files/ → dist-dataset/

site/dataset/index.html is generated — edit the template and re-run build_page.py.

The archives total ~13 GB and are not deployable to Pages (25 MB per file, 20k file cap), so they live in dist-dataset/ — outside site/, and gitignored — and are served from R2 by a Pages Function.

Serving + download analytics

functions/ holds the Pages Functions (beside site/, not inside it — functions/ sits at the project root alongside pages_build_output_dir):

functions/dataset/files/[file].ts   streams an archive from R2, logs the download to D1
functions/dataset/stats.ts          GET /dataset/stats → per-file download counts as JSON
tools/analytics-schema.sql          the D1 downloads table
tools/upload_r2.py                  multipart upload of the archives to R2

The route is /dataset/files/<name>.zip, which is exactly what tools/serve.py serves locally — so the page's download links need no environment-specific rewriting.

Only un-ranged GETs are counted: download managers and resumed transfers issue a Range header per chunk, and counting those would inflate the number several-fold. Range requests are still served correctly (206 + Content-Range), they just don't tick the counter. The log stores file, timestamp, and country — no IP, no user agent.

One-time setup

Needs an API token with Workers R2 Storage: Edit and D1: Edit — the original Pages-only token can't do either — plus R2 access keys for the upload.

D1 is already provisioned: frrazers-dataset-analytics (76eabc3d-3f65-4092-86cd-54a9b61410c2, region WEUR) with the schema applied and its database_id set in wrangler.jsonc. Recreating it is only necessary from scratch:

npx wrangler d1 create frrazers-dataset-analytics       # put database_id in wrangler.jsonc
npx wrangler d1 execute frrazers-dataset-analytics --remote --file=tools/analytics-schema.sql

R2 must be enabled on the account first (Dashboard → R2 → Enable; needs a payment method even inside the free tier). Until then the API returns code: 10042 and the S3 endpoint rejects the TLS handshake outright. Then:

npx wrangler r2 bucket create frrazers-dataset

export R2_ACCESS_KEY_ID=...        # R2 → API → Manage API tokens → Object Read & Write
export R2_SECRET_ACCESS_KEY=...
uv run tools/upload_r2.py          # ~13 GB, multipart, re-runnable (skips what's there)

npx wrangler pages deploy          # reads wrangler.jsonc, uploads site/ + functions/

Reading the counts:

curl https://tools.frrazers.com/dataset/stats
npx wrangler d1 execute frrazers-dataset-analytics --remote \
  --command="SELECT file, COUNT(*) FROM downloads GROUP BY file"

Local testing with bindings

npx wrangler d1 execute frrazers-dataset-analytics --local --file=tools/analytics-schema.sql
npx wrangler r2 object put frrazers-dataset/roblox-icons.zip --file=<some.zip> --local
npx wrangler pages dev --port 8789

Local R2/D1 are simulated on disk under .wrangler/, so don't push the real 13 GB through them — a small stand-in archive is enough to exercise the function.

Theme

All pages share the frrazers.com design system (paper/ink palette, Stack Sans Headline, automatic light/dark with a toggle). Tokens live in site/theme.css; pages remap their local variables onto those tokens.

Deploy

npx wrangler pages deploy          # wrangler.jsonc supplies the project, site/, and bindings

About

No description, website, or topics provided.

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages