Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ English | [简体中文](README.zh-CN.md)

# shotsync

Your own cross-device image & text pool, deployable to Cloudflare's free tier in a few minutes. Drop a screenshot or photo on one device, grab it on another. No app to install (the phone client is a PWA), no third-party image host — your data lives only in your own Cloudflare account.
Your own cross-device image & text pool, deployable to Cloudflare's free tier in a few minutes. Drop a screenshot or photo on one device, grab it on another. No app to install (the phone client is a PWA), no third-party image host — when self-hosted, your data lives only in your own Cloudflare account.

**[Use ShotSync online →](https://shotsync-hosted.defiabell.workers.dev)** No deployment needed. Sign up with an email and password to sync images and text. Beta limited to 100 accounts; new accounts keep files for 7 days by default.

**🎬 Live demo (read-only sample pool): https://shotsync-demo.defiabell.workers.dev**

Expand All @@ -15,10 +17,20 @@ Your own cross-device image & text pool, deployable to Cloudflare's free tier in
| Mode | Best for | Access | File storage |
| --- | --- | --- | --- |
| **Self-hosted (default)** | Your own image and text pool | Deploy, then enter your own `AUTH_TOKEN` | Your Cloudflare R2 |
| Hosted service (beta) | Using the operator's service without deploying | Register/sign in at the hosted URL | The operator's Cloudflare R2 |
| Hosted service (beta) | Using the operator's service without deploying | [Sign in or create an account](https://shotsync-hosted.defiabell.workers.dev) | The operator's Cloudflare R2 |
| Read-only demo | Exploring the interface | Open the demo above; uploads are disabled | Public samples |

For self-hosting, follow [the steps below](#deploy-your-own-5-min). See the [hosted guide](docs/hosted.md) for availability and quotas: the current beta still has a Free-plan CPU limitation and its authentication upgrade is not deployed. [Mode details and FAQ](docs/deployment-modes.md).
For self-hosting, follow [the steps below](#deploy-your-own-5-min). See the [hosted guide](docs/hosted.md) for availability and quotas: the current beta still has a Free-plan CPU limitation and authentication now uses Supabase Auth. [Mode details and FAQ](docs/deployment-modes.md).

## Start using the hosted service

1. Open the [hosted service](https://shotsync-hosted.defiabell.workers.dev), register with an email and password, and save your recovery code. No email verification is required.
2. Choose **+ 图片** to upload an image or **文字** to send text. Sign in to the same account on another device to retrieve it.
3. Open settings to copy the gallery address, check quotas and retention, or generate a device token for the Mac app / iOS Shortcut.

On a phone, follow the [home-screen shortcut guide](https://shotsync-hosted.defiabell.workers.dev/mobile) for iPhone or Android to open your gallery from an app icon.

The read-only demo contains public samples and does not accept uploads. Hosted content is isolated by account; users do not need Cloudflare or Supabase configuration.

![shotsync gallery](docs/screenshot.png)

Expand Down
18 changes: 15 additions & 3 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

# shotsync

属于你自己的跨设备图片 & 文字中转池,几分钟就能部署到 Cloudflare 免费档。在一台设备上截图或存图,另一台设备随手就能拿到。手机端无需装 App(是 PWA),不经任何第三方图床——数据只待在你自己的 Cloudflare 账户里。
属于你自己的跨设备图片 & 文字中转池,几分钟就能部署到 Cloudflare 免费档。在一台设备上截图或存图,另一台设备随手就能拿到。手机端无需装 App(是 PWA),不经任何第三方图床——自部署时,数据只待在你自己的 Cloudflare 账户里。

**[直接使用在线版 →](https://shotsync-hosted.defiabell.workers.dev)** 无需部署,邮箱+密码注册后即可同步图片和文字。免费试用限 100 个账号,新账号默认保留 7 天。

**🎬 在线演示(只读示例池):https://shotsync-demo.defiabell.workers.dev**

Expand All @@ -13,10 +15,20 @@
| 方式 | 适合谁 | 如何进入 | 数据存放 |
| --- | --- | --- | --- |
| **自己部署(默认)** | 想拥有自己的图片与文字池 | 部署后输入自己设置的 `AUTH_TOKEN` | 自己的 Cloudflare R2 |
| 公共托管服务(试用) | 不想部署,接受运营方存储数据和使用限额 | 在服务地址注册/登录 | 运营方的 Cloudflare R2 |
| 公共托管服务(试用) | 不想部署,接受运营方存储数据和使用限额 | [注册/登录在线版](https://shotsync-hosted.defiabell.workers.dev) | 运营方的 Cloudflare R2 |
| 只读演示 | 先看看界面 | 打开上方演示地址;不能上传 | 公开样例 |

自部署按[下面的步骤](#自己部署约-5-分钟)操作即可。公共服务的可用状态和限额见[托管版说明](docs/hosted.md);托管版目前仍有免费 CPU 限制,认证升级尚未上线。[模式区别与常见问题](docs/deployment-modes.md)。
自部署按[下面的步骤](#自己部署约-5-分钟)操作即可。公共服务的可用状态和限额见[托管版说明](docs/hosted.md);托管版目前仍有免费 CPU 限制,已接入 Supabase Auth。[模式区别与常见问题](docs/deployment-modes.md)。

## 直接使用在线版

1. 打开[在线版](https://shotsync-hosted.defiabell.workers.dev),注册邮箱和密码,无需邮件验证;保存页面给出的恢复码。
2. 点击「+ 图片」上传,或点击「文字」发送;另一台设备登录同一账号即可取回。
3. 设置中可复制相册地址、查看额度和保留期,或生成设备令牌连接 Mac 客户端/快捷指令。

手机用户可打开[手机快捷入口设置](https://shotsync-hosted.defiabell.workers.dev/mobile),按 iPhone / Android 指引添加到主屏幕,以后点图标就能进入相册。

只读 Demo 展示公开样例,不能上传;在线版的内容按账号隔离。使用在线版无需配置 Cloudflare 或 Supabase。

![shotsync gallery](docs/screenshot.png)

Expand Down
8 changes: 4 additions & 4 deletions docs/deployment-modes.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@

## 可选:直接使用公共托管服务

不想部署的用户可使用运营方提供的账号服务。浏览器通过邮箱和密码进入自己的文件池;文件存放在运营方的 Cloudflare 中,受服务人数、上传、存储和保留时间限制。使用者不需要开通 Cloudflare 或 Supabase。
不想部署的用户可直接打开[ShotSync 在线版](https://shotsync-hosted.defiabell.workers.dev),注册邮箱和密码,保存恢复码。浏览器通过邮箱和密码进入自己的文件池;文件存放在运营方的 Cloudflare 中,受服务人数、上传、存储和保留时间限制。使用者不需要开通 Cloudflare 或 Supabase。

托管服务与自部署实例的数据、登录账号和访问令牌不互通,不会自动迁移或同步。Mac App/快捷指令应填写所选服务的地址:个人池使用共享 `AUTH_TOKEN`,托管服务使用登录后创建的设备令牌。

当前公共服务仍为试用,原登录实现存在免费 CPU 限制,认证升级尚未部署;实际状态见[托管说明](hosted.md)。只读 demo 仅用于浏览公开样例,不接受上传,也不是公共账号服务。
当前公共服务已接入 Supabase Auth,限 100 个账号试用,新账号默认保留 7 天;免费 CPU 容量限制仍需注意,实际状态见[托管说明](hosted.md)。只读 demo 仅用于浏览公开样例,不接受上传,也不是公共账号服务。

## 仅运营者:部署多用户账号服务

[托管部署文档](hosted.md)面向想为多人运营服务的人。它有独立的 Worker 入口、D1 和 R2 资源;外部认证升级另需运营者的 Supabase 配置。不要把这套配置作为个人池的安装前提,也不要复制维护者的数据库 ID、存储桶或认证项目配置。
[托管部署文档](hosted.md)面向想为多人运营服务的人。它有独立的 Worker 入口、D1 和 R2 资源;账号认证需要运营者的 Supabase 配置。不要把这套配置作为个人池的安装前提,也不要复制维护者的数据库 ID、存储桶或认证项目配置。

| 命令 | 配置 | 部署内容 |
| --- | --- | --- |
Expand All @@ -38,6 +38,6 @@

Self-hosting remains the default: Worker + R2 + one shared `AUTH_TOKEN`. Run `npm run deploy`; no ShotSync account, Supabase, D1, email or CAPTCHA setup is required. Token holders share the entire pool. Configure the 30-day R2 lifecycle explicitly.

The optional hosted service uses individual accounts and the operator's storage/quotas. Only its operator needs the extra infrastructure; end users sign in at that service's URL. Accounts, files and credentials are not automatically shared with personal deployments. The hosted beta is still undergoing an authentication upgrade; see its guide for current availability.
The optional hosted service uses individual accounts and the operator's storage/quotas. Only its operator needs the extra infrastructure; end users sign in at that service's URL. Accounts, files and credentials are not automatically shared with personal deployments. [Open the hosted beta](https://shotsync-hosted.defiabell.workers.dev): authentication uses Supabase Auth, with a 100-account cap and 7-day default retention. See its guide for quotas and capacity limits.

`npm run deploy:hosted` is an explicit, separate operator workflow. It never replaces the default personal deployment. The read-only demo is only for exploring sample content.
6 changes: 4 additions & 2 deletions docs/hosted.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ The hosted entry point (`src/hosted/index.ts`) adds email/password accounts with

## What people can do

After sign-in, the homepage is a compact gallery for moving content between devices. Use **+ 图片** to choose images (selection uploads immediately), drop or paste an image, or open **文字** to send a note. Tap a gallery item to view it, copy text or retrieve an image, share it, or delete it. Visible pages check for new content periodically; hidden pages pause automatic syncing. Account information, the actual retention period, quota details and device tokens are available under **设置** instead of occupying the main workspace.
After sign-in, the homepage is a compact gallery for moving content between devices. Use **+ 图片** to choose images (selection uploads immediately), drop or paste an image, or open **文字** to send a note. Tap a gallery item to view it, copy text or retrieve an image, share it, or delete it. Use **选择** to select multiple items and confirm a batch deletion; failed deletions remain selected for retry. Settings includes a copyable gallery address and device tokens: tokens created on this page can be revealed or copied again during the current page session, and are masked when settings closes. Reloading or logging out clears these copies; the server stores only hashes and cannot recover older tokens. Visible pages check for new content periodically; hidden pages pause automatic syncing. Account information, the actual retention period, quota details and device tokens are available under **设置** instead of occupying the main workspace.

The public **[/mobile](https://shotsync-hosted.defiabell.workers.dev/mobile)** guide explains iPhone Safari and Android Chrome home-screen shortcuts plus a Shortcuts “Open URL” entry. It is also linked from Settings. The manifest opens `/` in standalone mode when supported; real 192/512-pixel PNG and 180-pixel Apple touch icons are served locally. These routes contain only the configured public origin, never credentials, and bypass database-backed API counters. No service worker or offline private-data cache is installed; syncing requires connectivity. Browser menus and installation support vary, and a new home-screen context may require sign-in. Instructions follow [Apple](https://support.apple.com/guide/iphone/iphea86e5236/ios) and [Chrome](https://support.google.com/chrome/answer/9658361?co=GENIE.Platform%3DAndroid).

Register with an email and password, complete Turnstile, and save the recovery code shown once before signing in. The email is an unverified username, not proof of mailbox ownership. Recovery requires the email, recovery code and a new password; a successful reset replaces the recovery code and invalidates all old sessions and device tokens. Save the replacement code. Losing both password and recovery code means there is no self-service recovery; no reset emails are sent.

Supabase Auth owns passwords and browser sessions; there is no local PBKDF2/scrypt fallback. Login returns a short-lived Supabase access JWT held only in browser memory. The refresh token lives in a Secure/HttpOnly/SameSite cookie; `/api/account/refresh` rotates it and restores the session after reload. Protected requests send the access JWT as a bearer credential. The Worker verifies its signature using cached Supabase JWKS and checks the account state, authentication version and session revocation in D1. D1 retains application users, device tokens, recovery-code hashes, revocations and quotas; R2 retains files. Ordinary file requests need no provider call once signing keys are cached.

Accounts are specific to ShotSync: using the same registration style as Yixi does not share accounts or its database. A dedicated personal Supabase project is required. Public Supabase signup stays disabled, including after launch; website registration remains available because ShotSync performs admin creation only after its own Turnstile and admission checks. Admin creation uses `email_confirm: true` only to allow password sign-in without mail; it is not evidence of mailbox ownership. IDs and server-controlled application metadata bind identities, never an email match alone. `verified_at` remains NULL for new accounts and is reserved for a future, explicit mailbox verification flow. Never link accounts across products or grant mailbox-based trust from an email string alone. Migration `0003_recovery.sql` only adds a nullable hash column; it does not mark existing accounts verified, delete users, or give legacy accounts guessed recovery codes. The obsolete mail-token table is retained for non-destructive migration compatibility, but mail endpoints and delivery code are removed.

Browser refresh cookies are scoped to the hosted origin. Logout revokes the Supabase session and records its session ID in D1 so an already issued access JWT cannot continue accessing ShotSync. Accepted access JWT lifetimes are at most 24 hours; successful remote logout retains the revocation for two days, and uncertain remote logout retains it indefinitely. An expired, correctly signed JWT can only revoke its session, never authorize a file request. Recovery increments the D1 authentication version before changing the provider password; signed application metadata must match that version. Mac and iOS clients use individually revocable device tokens (shown once, maximum 10, expire after 90 days). Use the hosted origin as the existing client's base URL and the device token as its bearer credential; the multipart `full`/optional `thumb` upload protocol is retained. Browser gallery JSON is specific to hosted mode.
Browser refresh cookies are scoped to the hosted origin. Logout revokes the Supabase session and records its session ID in D1 so an already issued access JWT cannot continue accessing ShotSync. Accepted access JWT lifetimes are at most 24 hours; successful remote logout retains the revocation for two days, and uncertain remote logout retains it indefinitely. An expired, correctly signed JWT can only revoke its session, never authorize a file request. Recovery increments the D1 authentication version before changing the provider password; signed application metadata must match that version. Mac and iOS clients use individually revocable device tokens (issued once by the server, maximum 10, expire after 90 days). Use the hosted origin as the existing client's base URL and the device token as its bearer credential; the multipart `full`/optional `thumb` upload protocol is retained. Browser gallery JSON is specific to hosted mode.

File access, deletion and sharing resolve ownership from authenticated account IDs, never from caller-supplied user IDs. Files are not end-to-end encrypted: the service operator has access to storage. The UI doesn't persist credentials/files in localStorage or a service worker. Shared links are bearer credentials: anyone holding one can access its file until expiry or revocation.

Expand Down
Loading
Loading