TreeMap
A free, open-source disk-space visualizer. Squarified treemap, duplicate hunting, disk-full forecasting, and cleanup that only ever moves files to the Trash.
翻译准备中
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证Text, Links und Dateien schnell zwischen Geräten teilen – ohne Login. Astro + Cloudflare Workers, Durable Objects, R2.
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗Live: https://tee.chapter42.de
Move text, links and files between devices quickly (e.g. phone → laptop), no login.
The name comes from the Unix command tee: one input, many outputs. Whatever one device sends into a room arrives on all the others.
Rooms are deleted automatically 24 h after the last activity (at the latest 7 days after creation), including all files.
Browser ──HTTP──▶ Worker (src/worker.ts)
▲ ├─ /api/* → src/server/api.ts ──RPC──▶ Durable Object "Room" (one per room, SQLite)
│ │ │ ├─ items/meta in SQLite
│ │ └─ upload/download ──▶ R2 (rooms/<CODE>/<id>)
│ └─ everything else → Astro (SSR + static assets)
└────WebSocket (Hibernation API) ◀──────────────────────────┘
src/worker.ts. It exports the Room Durable Object and hands every non-API request to Astro (@astrojs/cloudflare/handler).src/server/room.ts), SQLite-backed:
meta (code, encrypted, created_at, last_activity) and items (text/link/file, file metadata, status pending / ready).ctx.acceptWebSocket). ping/pong is handled by setWebSocketAutoResponse, so the DO does not have to wake up for it.rooms/<CODE>/ and then the entire DO storage.FixedLengthStream, then the DO commits the item and broadcasts it to every device.cf-connecting-ip).Quota Durable Object (src/server/quota.ts, idFromName('global')):
siteverify. Without configured keys, Turnstile is disabled.src/middleware.ts) picks the language from the lang cookie, then Accept-Language, then falls back to English. ?lang=de / ?lang=en sets the cookie and redirects to the URL without the parameter. Room URLs stay language-neutral (/<CODE>). All UI strings live in src/i18n/index.ts.Rooms are end-to-end encrypted by default (src/scripts/crypto.ts, Web Crypto, AES-256-GCM):
/ABC234#k=…. Browsers never send the fragment to the server; the QR code and "copy link" include it.{ k: 'text'|'link', c } encrypted as JSON, Base64url(IV | ciphertext).TEE1 | nonce prefix[8] | chunk size, IV per chunk = prefix | chunk index, and the AAD marks the last chunk (detects reordering and truncation). Name, type and original size travel encrypted in the X-Filename header (X-Encrypted: 1).409 wrong_mode).Web Crypto is only available in secure contexts (HTTPS,
localhost). When testing overhttp://<LAN-IP>, encrypted rooms therefore do not work; unencrypted ones do.
src/server/security.ts adds security headers to every Worker response (except WebSocket upgrades):
challenges.cloudflare.com); no inline scripts or styles, frame-ancestors 'none', object-src 'none'. Astro is configured to always ship JS and CSS as files (inlineStylesheets: 'never', assetsInlineLimit: 0). Skipped in astro dev, where Vite injects styles at runtime.When adding third-party resources, extend the CSP in security.ts, otherwise the browser blocks them.
Runbook: docs/abuse.md.
Moderation Durable Object (180 days) and emailed to the operator via the send_email binding (Email Routing).ADMIN_SECRET, valid 14 days) to /admin/<CODE>: metadata and reports, never content, plus the actions block, freeze (keep up to 90 days for the authorities), unblock and delete.451 everywhere and disconnect all devices./nutzungsbedingungen.| Method & path | Purpose |
|---|---|
POST /api/rooms | Body { turnstileToken, encrypted } → { code } (Turnstile + rate limit RL_CREATE) |
GET /api/rooms/:code | Does the room exist? 200 / 404 |
GET /api/rooms/:code/ws | WebSocket for live sync |
POST /api/rooms/:code/text | { text } (links are detected) or { ciphertext } in encrypted rooms |
POST /api/rooms/:code/files | Raw body. Plain: headers X-Filename (URL-encoded) and Content-Type. Encrypted: X-Encrypted: 1, X-Filename = encrypted metadata |
GET /api/rooms/:code/files/:id | Download (Content-Disposition: attachment) |
DELETE /api/rooms/:code/items/:id | Delete an item |
POST /api/rooms/:code/report | { category, description, contact?, itemId?, goodFaith: true } → { id, blocked } |
POST /api/admin/rooms/:code | { action: block|freeze|unblock|delete, exp, sig } (signed link from the report email) |
WebSocket messages (server → client): init, item, delete, devices. See src/shared/types.ts.
Error responses are { error, message }. The client translates the error code; message is English and meant for debugging.
| Max. file size | 100 MB (src/shared/limits.ts) |
| Max. items per room | 50 |
| Max. text length | 100 KB |
| Room creation | 10 / minute / IP |
| Uploads, text, deletes | 30 / minute / IP |
| Upload volume | 2 GB / day / IP (IPv6: per /64) |
| Total file storage | 50 GB (uploads are rejected beyond that) |
| Lifetime | 24 h after the last activity, at most 7 days |
The rate limiting binding counts per Cloudflare location and is not exact. That is good enough for abuse protection here. Cloudflare limits the request body size by plan (Free/Pro: 100 MB). Higher limits would need multipart uploads.
src/
├─ worker.ts Worker entrypoint (API + Astro + export Room)
├─ middleware.ts Language detection (cookie / Accept-Language / ?lang=)
├─ server/
│ ├─ api.ts API routes, validation, rate limiting, Turnstile, R2 streaming
│ ├─ room.ts Durable Object per room (SQLite, WebSockets, alarm, block/freeze)
│ ├─ quota.ts Durable Object enforcing storage and upload limits
│ ├─ moderation.ts Durable Object for reports, signed admin links, report emails
│ └─ security.ts Security headers (CSP, HSTS, …) for every response
├─ shared/ Code shared by client and server
│ ├─ codes.ts Room code generation / validation
│ ├─ limits.ts Limits & TTL
│ ├─ types.ts Item and message types
│ ├─ legal.ts Provider details for Impressum / privacy policy
│ └─ site.ts Repo and support links
├─ i18n/index.ts UI strings DE/EN, error codes → messages
├─ pages/
│ ├─ index.astro Landing page
│ ├─ [code].astro Room view (404 for unknown codes, noindex)
│ ├─ admin/[code].astro Moderation page (signed link only)
│ ├─ nutzungsbedingungen.astro Terms of use
│ ├─ impressum.astro Legal notice
│ └─ datenschutz.astro Privacy policy
├─ scripts/
│ ├─ room-client.ts WebSocket, reconnect, uploads with progress, drag & drop, decryption
│ ├─ crypto.ts End-to-end encryption (keys, texts, chunked files)
│ ├─ report-client.ts "Report content" dialog
│ ├─ turnstile.ts Turnstile widget (explicit rendering)
│ └─ ui.ts Toast, clipboard
├─ components/ SiteHeader, SiteFooter, Logo, LangSwitch, TransferScene (animation)
├─ layouts/ Base, LegalPage
└─ styles/global.css Color tokens, light/dark mode, base styles
test/ Vitest suites (Workers runtime) and test entrypoint
scripts/smoke.mjs Smoke test against the build
og/ Template and render script for the social preview images
docs/abuse.md Runbook for reports and illegal content
Requirements: Node.js ≥ 22, a Cloudflare account for deployment.
npm install
cp .dev.vars.example .dev.vars # Turnstile test keys for local development
npm run dev # astro dev, runs in workerd incl. Durable Objects, R2 & rate limits (simulated locally)
To test the production build locally with wrangler dev:
npm run preview # astro build && wrangler dev
Local data (DO storage, R2) lives in .wrangler/state/ and can simply be deleted.
To test on a phone on the same Wi-Fi: npm run dev -- --host, then open http://<LAN-IP>:4321 (unencrypted rooms only, see above).
More scripts:
npm run check # type check (astro check)
npm run cf-typegen # regenerate env types from wrangler.jsonc (worker-configuration.d.ts)
Social preview images for shared links (public/og-de.png, public/og-en.png) are rendered from the template og/og.html:
npm run og # renders both languages with headless Chrome (macOS path, otherwise set CHROME=…)
Tip: to test the alarm locally, temporarily set
ROOM_TTL_MSinsrc/shared/limits.tsto e.g.5000.
npm test # Vitest inside the Workers runtime (Durable Objects, R2, rate limits, all local)
npm run check # type check app and tests
npm run build && npm run smoke # real build under wrangler dev: pages, security headers, no inline code
test/ covers encryption (incl. tampering), room codes, rooms and live sync, expiry, quotas, reports, signed moderation links and actions, and security headers. It uses test/worker.ts, which exports the same Durable Objects and API as src/worker.ts but skips the Astro page renderer.scripts/smoke.mjs covers the pages, which only exist after astro build..github/workflows/ci.yml) runs type check, tests, build and smoke test on every push to main and on pull requests. It needs no Cloudflare credentials and does not deploy.npx wrangler login
npx wrangler r2 bucket create tee-files --jurisdiction eu
npx wrangler r2 bucket create tee-files-preview --jurisdiction eu # for previews
npm run deploy # astro build && wrangler deploy
On the first deploy, migration v1 creates the SQLite class Room.routes in wrangler.jsonc (currently tee.chapter42.de). The zone has to be in the same Cloudflare account; the deploy then creates the DNS record itself:
"routes": [{ "pattern": "tee.chapter42.de", "custom_domain": true }]
For tee this is already done: the widget "tee Raumerstellung" exists, the site key is in wrangler.jsonc, and the secret key is stored as a secret for production and the main preview. For a fresh setup:
npx wrangler login # needs the challenge-widgets.write scope
npx wrangler turnstile widget create "tee Raumerstellung" \
--domain tee.chapter42.de --domain tee.<account>.workers.dev \
--domain main-tee.<account>.workers.dev --mode managed
wrangler.jsonc under vars.TURNSTILE_SITE_KEY and previews.vars.TURNSTILE_SITE_KEY.npx wrangler secret put TURNSTILE_SECRET_KEY
npx wrangler preview secret put TURNSTILE_SECRET_KEY --name main
npm run deployFor another custom domain, add it to the widget with wrangler turnstile widget update <sitekey> --domain ….
While either key is missing, Turnstile stays off. Rate limiting still applies.
openssl rand -base64 32 | npx wrangler secret put ADMIN_SECRET # HMAC key for moderation links
npx wrangler secret put REPORT_TO # verified Email Routing destination
Set both for the preview as well (npx wrangler preview secret put … --name main).
The previews block in wrangler.jsonc gives previews their own R2 bucket (tee-files-preview) and their own rate limit namespaces. Prerequisite: the Worker has been deployed normally at least once.
npm run deploy:preview -- --name my-feature # without --name: the current git branch
URL: https://<name>-tee.<account>.workers.dev, plus a unique URL per deployment.
Delete with npx wrangler preview delete --name <name>. wrangler preview is currently in open beta.
Live logs:
npx wrangler tail
src/shared/limits.tsratelimits in wrangler.jsonc (period must be 10 or 60 seconds)r2_buckets in wrangler.jsoncsrc/shared/legal.tsAfter changing wrangler.jsonc, regenerate the types with npm run cf-typegen. The dev and build scripts do that automatically.
Accounts, persistence beyond 24 h, multipart uploads > 100 MB, password-protected rooms. In unencrypted rooms, anyone who knows the code has access. With 28⁶ ≈ 481 million possible codes and the rate limit, guessing is impractical, but the code is not a secret. Encrypted rooms are additionally protected by the 256-bit key in the link.