56 Commits

Author SHA1 Message Date
dependabot[bot] 4c70a75a74 build(deps): bump better-auth from 1.6.2 to 1.6.14 2026-06-06 03:00:43 -04:00
dependabot[bot] 7c35d9251d build(deps): bump hono from 4.12.18 to 4.12.21 2026-06-06 02:49:56 -04:00
saltbo f31278b434 refactor(cli): move zpan command module to cmd 2026-06-06 01:49:41 -04:00
saltbo 3f7a859e73 refactor(downloader): consolidate api contract generation 2026-06-05 15:24:56 -04:00
saltbo 53076d7873 feat(downloads): add remote download workers 2026-06-03 02:21:50 -04:00
saltbo c3864c1d2c fix(store): use updated cloud sdk pricing contract 2026-06-02 13:52:55 -04:00
saltbo 30bc6e1a12 build: migrate project to pnpm 2026-06-01 10:44:33 -04:00
dependabot[bot] 312413d0a8 build(deps): bump ws, @cloudflare/vite-plugin, @cloudflare/vitest-pool-workers, miniflare and wrangler (#402)
Bumps [ws](https://github.com/websockets/ws) to 8.20.1 and updates ancestor dependencies [ws](https://github.com/websockets/ws), [@cloudflare/vite-plugin](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/vite-plugin-cloudflare), [@cloudflare/vitest-pool-workers](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/vitest-pool-workers), [miniflare](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/miniflare) and [wrangler](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler). These dependencies need to be updated together.


Updates `ws` from 8.18.0 to 8.20.1
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.18.0...8.20.1)

Updates `@cloudflare/vite-plugin` from 1.35.0 to 1.39.0
- [Release notes](https://github.com/cloudflare/workers-sdk/releases)
- [Changelog](https://github.com/cloudflare/workers-sdk/blob/main/packages/vite-plugin-cloudflare/CHANGELOG.md)
- [Commits](https://github.com/cloudflare/workers-sdk/commits/HEAD/packages/vite-plugin-cloudflare)

Updates `@cloudflare/vitest-pool-workers` from 0.15.2 to 0.16.10
- [Release notes](https://github.com/cloudflare/workers-sdk/releases)
- [Changelog](https://github.com/cloudflare/workers-sdk/blob/main/packages/vitest-pool-workers/CHANGELOG.md)
- [Commits](https://github.com/cloudflare/workers-sdk/commits/HEAD/packages/vitest-pool-workers)

Updates `miniflare` from 4.20260430.0 to 4.20260526.0
- [Release notes](https://github.com/cloudflare/workers-sdk/releases)
- [Changelog](https://github.com/cloudflare/workers-sdk/blob/main/packages/miniflare/CHANGELOG.md)
- [Commits](https://github.com/cloudflare/workers-sdk/commits/HEAD/packages/miniflare)

Updates `wrangler` from 4.87.0 to 4.95.0
- [Release notes](https://github.com/cloudflare/workers-sdk/releases)
- [Commits](https://github.com/cloudflare/workers-sdk/commits/HEAD/packages/wrangler)

---
updated-dependencies:
- dependency-name: ws
  dependency-version: 8.20.1
  dependency-type: indirect
- dependency-name: "@cloudflare/vite-plugin"
  dependency-version: 1.39.0
  dependency-type: direct:development
- dependency-name: "@cloudflare/vitest-pool-workers"
  dependency-version: 0.16.10
  dependency-type: direct:development
- dependency-name: miniflare
  dependency-version: 4.20260526.0
  dependency-type: indirect
- dependency-name: wrangler
  dependency-version: 4.95.0
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-01 10:10:11 -04:00
saltbo 2438275ea8 feat(archive): queue streaming archive jobs 2026-05-15 09:59:08 -04:00
Jasper Van 8b45c6218a fix: use better auth captcha providers (#401)
Agent-Profile: https://agent-kanban.dev/agents/a318237412dd8b98
2026-05-13 16:39:09 -04:00
saltbo 8560b1b2ef fix(webdav): support unknown length streaming PUT 2026-05-12 23:22:19 -04:00
Jasper Van 0694d5511a feat: add local archive background jobs 2026-05-11 20:42:01 -04:00
saltbo 9efdcbb424 refactor(cloud-store): use cloud sdk contracts 2026-05-11 15:03:34 -04:00
saltbo 800cda9f9c test(cloud): add tunnel-backed store e2e 2026-05-09 22:17:52 -04:00
dependabot[bot] f386fd2b4f build(deps): bump hono from 4.12.14 to 4.12.18 (#380)
Bumps [hono](https://github.com/honojs/hono) from 4.12.14 to 4.12.18.
- [Release notes](https://github.com/honojs/hono/releases)
- [Commits](https://github.com/honojs/hono/compare/v4.12.14...v4.12.18)

---
updated-dependencies:
- dependency-name: hono
  dependency-version: 4.12.18
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-09 18:45:46 -04:00
saltbo dfc8c708d5 docs: switch license to AGPLv3 2026-05-06 12:41:02 -04:00
saltbo eb1610b5e0 chore(deps): align Cloudflare test runtime 2026-05-05 01:05:58 -04:00
saltbo b781652c95 chore(deps): sync recent package updates 2026-05-05 00:42:01 -04:00
Jasper Van 8f4d55d0e4 feat: show oauth provider icons
Show OAuth provider icons on login buttons and admin OAuth provider UI.

Use simple-icons with existing Lucide fallbacks, and add coverage for built-in provider mappings plus icon rendering branches.
2026-05-03 10:47:56 -04:00
saltbo fecff6d949 fix(announcements): use full markdown editor 2026-05-02 23:02:24 -04:00
saltbo f0e7af2798 feat(licensing): redesign Pro license binding 2026-04-29 20:20:18 -04:00
saltbo 7cb85606a5 fix: remove QR code, keep countdown timer in pairing modal
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-24 23:20:53 -04:00
saltbo 79adf45c91 feat: add QR code and countdown timer to pairing modal
- Add qrcode.react dependency for QR code generation
- Display QR code in PairingModal encoding the pairing URL
- Add countdown timer showing time remaining until code expires
- Auto-expire and stop polling when countdown reaches 0
- All 2809 tests pass

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-24 23:16:43 -04:00
Jasper Van b9cea3e912 feat(licensing): v2.6 Z2 — Ed25519 verify + PUBLIC_KEYS + entitlement cache (#341)
* feat(licensing): add server/licensing module with Ed25519 verify + entitlement cache

- Add paseto-ts dependency (WebCrypto Ed25519, works on all 7 deploy targets)
- server/licensing/public-keys.ts: PUBLIC_KEYS array with DEV placeholder PASERK key
- server/licensing/verify.ts: verifyCertificate() iterates PUBLIC_KEYS, validates
  signature, expiry and instance_id — returns null (never throws) on invalid certs
- server/licensing/entitlement.ts: loadEntitlement() with 60s in-process memoization;
  invalidateEntitlementCache() for post-refresh invalidation
- Update LicenseEntitlement.expires_at / issued_at to string (ISO-8601 wire format)
- Unit tests: valid cert, invalid sig, expired, wrong instance_id, key rotation

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* fix(licensing): apply biome lint fixes (import order, template literal)

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

---------

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
2026-04-24 07:34:32 -04:00
Jasper Van d33800f23e feat: Azure Functions deployment target (v4, Node 22) (#330)
* feat: add Azure Functions deployment target (v4, Node 22)

- server/entry-azure.ts: Azure Functions v4 handler wrapping the Hono
  app via app.http(); uses createLibsqlPlatform for Turso and serves
  the SPA from ./dist via @hono/node-server/serve-static
- server/azure-host.json: runtime manifest (extensionBundle v4)
- deploy/azure-functions/main.bicep: idempotent Bicep template
  provisioning Storage Account, Consumption plan and Function App;
  BETTER_AUTH_SECRET handled separately by the workflow
- .github/workflows/deploy-azure.yml: 8-step workflow (secret check,
  checkout, Node setup, az login, Bicep deploy, build, db:migrate,
  func publish) with BETTER_AUTH_SECRET generate-if-missing logic
- package.json: build:azure script + @azure/functions dependency
- docs/deploy/azure-functions.md: setup guide covering SP JSON format,
  required secrets, and local emulation with func start

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* fix: address review issues in Azure Functions deploy

- Move BETTER_AUTH_SECRET and APP_URL setup to before func publish
  (bootstrap.ts throws on missing secret; any request between publish
  and the old secret-set step would have returned 500)
- Remove placeholder appUrl Bicep param; workflow sets APP_URL and
  BETTER_AUTH_URL via appsettings after Bicep, before publish
- Fix HttpRequest→Request body handling: construct a proper Web API
  Request with body cast and duplex option instead of double-casting
  HttpRequest, ensuring POST/PUT/PATCH body-reading routes work
- Add push: branches: [master] trigger + upstream guard to match other
  deploy workflow conventions; document the auto-deploy behaviour
- Update docs/deploy/azure-functions.md to reflect the push trigger

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* ci: re-trigger CI for review fixes

---------

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
2026-04-22 02:08:54 -04:00
Jasper Van 0f403f8081 feat: v2.5.0 T4 — Netlify deployment target (#329)
* feat: v2.5.0 T4 — Netlify deployment target

- server/entry-netlify.ts: Netlify Functions v2 (ESM) handler using hono/netlify
  adapter; connects to Turso via @libsql/client; skips in-process migrations
  (workflow applies them before deploy via drizzle-kit)
- deploy/netlify/netlify.toml: build command, functions directory, SPA fallback redirect
- .github/workflows/deploy-netlify.yml: 8-step workflow — secret guard, tag resolve,
  Turso migrations, build, netlify deploy --prod, BETTER_AUTH_SECRET first-deploy, summary
- package.json: add build:netlify script (tsup ESM → netlify/functions)
- docs/deploy/netlify.md: 5-section setup guide covering Turso, site creation,
  secrets, deploy trigger, first-boot storage setup, and cost breakdown

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* fix: address Netlify deploy review blockers

BLOCKER 1 — move BETTER_AUTH_SECRET step before Deploy in workflow so
the function always has the secret set before its first cold start.

BLOCKER 2 — replace inline platform construction in entry-netlify.ts
with createLibsqlPlatform(); removes duplicated db/schema wiring and
re-unifies with the shared factory. migrate() runs at cold start and
is idempotent (~50–100ms) per the workflow's prior drizzle-kit migrate.

BLOCKER 3 — add --external @libsql/client to build:netlify so tsup
leaves the native-binding package for Netlify to resolve; switch
netlify.toml to node_bundler=esbuild so Netlify bundles @libsql/client
from node_modules. Add included_files=["migrations/**"] so the
migrations folder is available in the function zip for migrate().

Minor — replace 2>/dev/null with 2>&1 in deploy step so netlify-cli
errors surface in CI logs instead of being silently swallowed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 01:28:08 -04:00
Jasper Van b5be4c5ebd feat: v2.5.0 T2 — AWS Lambda deployment (entry + SAM + workflow + docs) (#331)
- server/entry-lambda.ts: Hono app via hono/aws-lambda handle(); lazy init
  pattern for CJS compatibility and warm-start reuse; serves SPA static
  files from dist/ with MIME detection and index.html fallback
- deploy/aws-lambda/template.yaml: SAM template with Function URL (no API
  Gateway), Node 22, TURSO_* / BETTER_AUTH_SECRET / APP_URL env vars,
  minimal IAM (AWSLambdaBasicExecutionRole)
- .github/workflows/deploy-aws-lambda.yml: 8-step contract (guard upstream,
  check secrets with exact names, resolve tag, checkout, ensure SAM artifact
  bucket, apply Turso migrations, build + sam deploy, post-deploy auto-gen
  BETTER_AUTH_SECRET + patch BETTER_AUTH_URL + write URL to summary)
- package.json: build:lambda script (tsup CJS, external @libsql/client)
- docs/deploy/aws-lambda.md: Prerequisites / Secrets / Trigger /
  First-boot storage / Cost sections
- README.md, V2_ROADMAP.md: link new doc

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
2026-04-22 01:28:05 -04:00
Jasper Van 5593eec3ce feat: v2.5.0 T3 — Vercel deployment (entry + vercel.json + workflow + docs) (#328)
* feat: add Vercel deployment target (Node runtime + Turso)

Adds first-class Vercel support: server/entry-vercel.ts using hono/vercel
handler, deploy/vercel/vercel.json with nodejs22.x function config and SPA
rewrites, build:vercel npm script producing api/entry-vercel.js + dist/,
deploy-vercel GitHub Actions workflow (8-step: secrets check, tag resolve,
checkout, install, migrate, build, link, deploy), and docs/deploy/vercel.md
documenting secrets, quick-start, local dev, and pricing notes.

Edge runtime is explicitly not used — @aws-sdk/client-s3 requires Node APIs.

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* fix: auto-generate BETTER_AUTH_SECRET on first Vercel deploy

Remove BETTER_AUTH_SECRET from the required secrets check. Add a
dedicated step that detects whether the secret already exists in the
Vercel project env via `vercel env ls production`, then either upserts
the user-supplied GitHub secret, auto-generates one with openssl on
first deploy, or skips if already present. Auto-generation case appends
a backup warning to GITHUB_STEP_SUMMARY. Docs move BETTER_AUTH_SECRET
to Optional Secrets with a note about the auto-gen behaviour.

Matches the existing CF Workers deploy.yml pattern (step 8 contract).

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

---------

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
2026-04-22 01:14:54 -04:00
Jasper Van 8005defd97 feat: v2.5.0 T1 — libSQL (Turso) platform adapter + Docker Turso opt-in (#326)
* feat: add libSQL (Turso) platform adapter and Docker opt-in

- server/platform/libsql.ts: createLibsqlPlatform() using @libsql/client +
  drizzle-orm/libsql; accepts plain env record; async migrate at boot;
  authToken optional for file:// URLs
- server/entry-node.ts: select platform at startup — libsql when
  TURSO_DATABASE_URL is set, otherwise existing SQLite via createNodePlatform()
- drizzle.config.ts: switch to turso dialect when TURSO_DATABASE_URL is set
- vitest.libsql.config.ts + server/platform/libsql.libsql-test.ts: smoke suite
  covering connect, migrations, insert/select against users + storages tables
- package.json: add @libsql/client dependency; add test:libsql script;
  externalize @libsql/client in build:node tsup command
- vitest.config.ts: exclude *.libsql-test.ts from coverage
- docs/deploy/docker.md: document Turso opt-in with copy-pasteable
  docker-compose snippet
- CONTRIBUTING.md: add Turso migrate path paragraph under Database Migrations

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* refactor: turn bootstrap.ts into a Platform-accepting factory

- server/bootstrap.ts: replace singleton module-scope script with
  exportable createBootstrap(platform) async factory; reads
  BETTER_AUTH_SECRET/BETTER_AUTH_URL/TRUSTED_ORIGINS from platform.getEnv
  so every future entry (Lambda, Vercel, Netlify, Azure) can reuse it
- server/entry-node.ts: slim down to platform selection + createBootstrap
  call; no more duplicate auth/app wiring
- server/dev.ts: thin vite-dev-server entry that creates NodePlatform and
  calls createBootstrap; replaces the former default export in bootstrap.ts
- vite.config.ts: update node dev server entry to server/dev.ts
- server/platform/libsql.ts: fix getEnv to check env record before
  falling back to process.env, matching the cloudflare.ts pattern

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* style: apply biome auto-fixes for pre-existing lint issues

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

---------

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
2026-04-22 00:48:21 -04:00
saltbo a575e055fe fix(docker): resolve 404 on port 8222 and simplify build
Docker build ran vite with the cloudflare plugin, which put SPA output
under dist/client/, while entry-node.ts serves from ./dist — causing
404 on every request in the container.

- Add build:node (vite --mode node) so the SPA lands in dist/, and
  fold build:server into it as a single command
- Move better-sqlite3 to dependencies (Node runtime needs it; CF
  Workers build tree-shakes it out anyway)
- Collapse Dockerfile from 4 stages to 2: one npm ci with a BuildKit
  cache mount, then npm prune --omit=dev in place. Drops the
  duplicate install and the cross-stage better-sqlite3 copy hack.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 23:19:46 -04:00
saltbo 3d8f51f175 ci: add fork-based deploy workflow for Cloudflare Workers
- Add deploy.yml: auto-deploys latest upstream release tag on fork sync
- Support manual trigger with optional version override
- Auto-create D1 database, apply migrations, and set BETTER_AUTH_SECRET
- Only runs on forks (skipped on saltbo/zpan)
- Validate required secrets with clear error message
- Update README with fork + GitHub Actions deploy instructions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-21 19:57:04 -04:00
Jasper Van 9dbece49ac feat: v2.4.0 T7 — Image Host gallery page (#324)
* feat: v2.4.0 T7 — Image Host gallery page with FileManager reuse

- Add ImageHostView component that wires FileManager with image-host-specific
  config: upload via /api/ihost/images presigned flow, delete with 5s undo toast,
  copy URL in raw/Markdown/HTML/BBCode formats, and thumbnail rendering

- Extend FileManager with new capabilities (copyUrl, delete), getThumbnailUrl
  prop, onDeleteItems/onCopyUrl callbacks, and viewModeStorageKey for isolated
  view-mode persistence per page

- Extend FilesGrid with optional getThumbnailUrl prop: renders lazy-loaded image
  thumbnails with FileIcon fallback on error; backward-compatible with Files page

- Extend FileRowActions with Copy URL submenu (raw/Markdown/HTML/BBCode) and a
  Delete action separate from Move to Trash; fully backward-compatible

- Extend UploadDropzone with optional uploadFn prop to bypass the default
  object-upload flow; Files page behavior unchanged

- Parameterize useViewMode hook with optional storageKey argument

- Add API wrappers: listIhostImages, createIhostImagePresign, confirmIhostImage,
  deleteIhostImage with matching tests in api.test.ts

- Add useClipboard hook; refactor navigator.clipboard.writeText usage in
  share-dialog.tsx and shares/index.tsx to use the hook

- Add IhostRoute to rpc.ts

- Add ihost.copy.*, ihost.delete.*, ihost.upload.*, ihost.table.* i18n keys
  to en.json and zh.json; add common.copied key

Agent-Profile: https://agent-kanban.dev/agents/b724a773425e397c

* fix: use Hono RPC client for ihost API, add presign endpoint, expand test coverage

- Rewrite server/routes/ihost.ts to use method chaining, fixing Hono RPC type
  inference (imperative app.post() calls prevented the schema from being typed)
- Extract POST /images/presign as a dedicated typed endpoint (zValidator) for the
  browser client; POST /images becomes multipart-only for API-key/PicGo compat
- Frontend: replace raw ihostFetch() with ihostApi RPC calls for all four
  wrappers (listIhostImages, createIhostImagePresign, confirmIhostImage,
  deleteIhostImage); mime parameter typed as AllowedImageMime
- Update integration tests to use /images/presign for JSON presign cases; adjust
  status expectations to 400 (Zod) vs 413/415 (manual checks no longer needed)
- Add unit tests: use-clipboard, image-host-data-source, image-host-view,
  file-row-actions, upload-dropzone, use-view-mode custom-key
- Add e2e/image-host.spec.ts: enable feature gate, upload (mocked S3 PUT),
  grid→table view switching, copy Markdown URL, delete with Undo, delete permanently

Agent-Profile: https://agent-kanban.dev/agents/b724a773425e397c

* test: improve patch coverage for ihost routes and file-row-actions

Add missing 503/401 integration tests for multipart endpoint and API key
error paths. Extract testable pure functions from file-row-actions.tsx and
image-host-view.tsx and update tests to import from source files.

Agent-Profile: https://agent-kanban.dev/agents/b724a773425e397c

* test: fix coverage cascade, add component rendering tests and branch tests

Revert buildCopyText export which caused file-manager/files-grid/upload-dropzone
to appear in coverage at 0% via transitive imports. Restore inline switch logic
in handleCopyUrl and define buildCopyText locally in the test.

Install @testing-library/react + jsdom, add React plugin to vitest unit project,
and write FileRowActions rendering tests (file-row-actions.render.test.tsx) to
cover JSX branches including Copy URL sub-menu and delete item.

Add missing DELETE 403 (no config) and storage-null branch tests to
ihost.integration.test.ts to cover uncovered branches in ihost.ts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: update ihost.cf-test.ts for multipart-only POST /images endpoint

POST /api/ihost/images now returns 415 for JSON (multipart only).
Add separate test for POST /api/ihost/images/presign returning 403
when image hosting is not enabled.

Agent-Profile: https://agent-kanban.dev/agents/b724a773425e397c

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 11:21:08 -04:00
saltbo 0137f009d4 feat(docker): add deploy variants and auto-generate auth secret
- Add --external better-sqlite3 to tsup build to fix ESM runtime error
- Add docker-entrypoint.sh to auto-generate BETTER_AUTH_SECRET if not set
- Persist generated secret to /data/.auth_secret across restarts
- Move image-based compose files to deploy/ directory
- Add deploy/docker-compose.rustfs.yml for ZPan + RustFS setup
- Keep build-from-source docker-compose.yml at project root

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-21 10:41:24 -04:00
Jasper Van 2dca9d9ffb feat(db): image hosting schema, apiKey plugin, migration 0011 (v2.4.0 T1) (#315)
* feat(db): image hosting schema, apiKey plugin, migration 0011

Adds the DB infrastructure for the v2.4 image hosting feature:

- server/db/schema.ts: new `image_hosting_configs` (per-org singleton,
  custom domain + referer allowlist) and `image_hostings` tables with
  all required indexes and cascade-delete FKs
- server/db/auth-schema.ts: `apikey` table for @better-auth/api-key
  plugin (referenceId-indexed, rate-limiting fields, permissions column)
- server/auth.ts: enable apiKey plugin (references='organization',
  image-hosting:upload permission declared)
- shared/types/index.ts: ImageHostingConfig, ImageHosting, ImageHostingStatus
- migrations/0011_image-hosting.sql: generated by drizzle-kit (NOT
  hand-authored); creates all three new tables + indexes
- migrations/meta/0011_snapshot.json: baseline snapshot for future
  migration generation (repo was missing snapshots 0002–0010)
- package.json: add @better-auth/api-key ^1.6.2 dep, bump
  better-auth to ^1.6.2

Note: migration is tagged 0011_image-hosting (idx=11 in journal) rather
than 0012 because the existing 0011_notifications was registered as idx=10
(pre-existing numbering offset from a skipped 0006_ slot).

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* fix: resolve coverage gap and migration filename collision

- Add getTableConfig FK reference tests to cover deferred .references()
  callbacks in schema.ts (fixes Codecov patch coverage < 94.98%)
- Rename 0011_image-hosting → 0012_image-hosting to avoid filename
  collision with pre-existing 0011_notifications migration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 03:18:52 -04:00
saltbo f215439a89 fix(v2.3.0): save-to-drive crash, media preview, share dialog polish
Fixes surfaced by full e2e on the staging preview:

- save-to-drive-dialog.tsx: Radix Select rejects empty-string values.
  The "Root" option used value="" and crashed the dialog on open.
  Use a '__root__' sentinel in the UI and convert to '' in the payload.

- file-preview.tsx: fetch(..., credentials:'include') failed on the 302
  redirect to an R2 presigned URL because R2 returns
  Access-Control-Allow-Origin: * which can't combine with credentialed
  requests. The presigned URL is already signature-authenticated;
  credentials aren't needed for cross-origin. Drop the option.

- share-dialog.tsx: switching to Direct now defaults Expires to "Never"
  (matches spec; embeds are meant to live indefinitely).

- file-manager-dialogs.tsx: pass onViewShares to ShareDialog so the
  "View in My Shares" button actually renders in the success view.

- folder-browser.tsx: public share empty state was reusing
  files.emptyState ("Drop files here or create a folder to get
  started."). Use new share.folderEmpty key instead.

- package.json: add db:migrate:d1:staging and db:migrate:d1:prod
  scripts so remote D1 migrations are a one-liner. Needed because
  the T1 notifications table migration wasn't applied to staging D1,
  causing /api/notifications/* to 500 in production.

Follow-up for maintainer: run
  npm run db:migrate:d1:staging
  npm run db:migrate:d1:prod
to pick up 0010_shares and 0011_notifications on the remote D1s.
2026-04-20 13:58:34 -04:00
dependabot[bot] 0c813a92f7 chore(deps): bump hono from 4.12.12 to 4.12.14 (#303)
Bumps [hono](https://github.com/honojs/hono) from 4.12.12 to 4.12.14.
- [Release notes](https://github.com/honojs/hono/releases)
- [Commits](https://github.com/honojs/hono/compare/v4.12.12...v4.12.14)

---
updated-dependencies:
- dependency-name: hono
  dependency-version: 4.12.14
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-16 14:11:37 -04:00
Jasper Van 4b20e6b5d9 feat: add email service abstraction with SMTP and HTTP API drivers (#280)
Unified email service supporting two drivers configured via system_options:
- SMTP driver using nodemailer (dynamic import for CF Workers compatibility)
- HTTP API driver using fetch (Resend-compatible)

Includes admin API at /api/admin/email-config for GET/PUT/POST test
endpoints with secret masking and discriminated union validation.

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
2026-04-12 21:25:49 -04:00
saltbo 7d5d0a5d08 fix(ui): add tw-animate-css for shadcn animation support and tune sheet speed
Tailwind v4 dropped the tailwindcss-animate plugin. Replace with
tw-animate-css so Dialog, Sheet, Dropdown animations work. Also reduce
sheet open/close duration from 500/300ms to 300/200ms.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 14:50:00 -04:00
saltbo 2ed2a67aee fix(dev): use staging D1 for local CF Workers development
Prevents accidentally modifying production data during local dev.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 13:15:10 -04:00
saltbo 877ef9d212 refactor(deploy): inline staging env detection into npm build script
Move CLOUDFLARE_ENV logic from ci-build.sh into package.json build
script. Workers Builds can use default build command (npm run build)
without custom Dashboard configuration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 13:14:23 -04:00
saltbo 5d7f358a64 fix(deploy): remove vars from top-level wrangler config to avoid local dev conflict
Top-level [vars] would override local dev with placeholder values.
BETTER_AUTH_URL and TRUSTED_ORIGINS are not needed in the deploy form —
better-auth auto-infers from the request URL on first deploy.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 10:52:31 -04:00
saltbo 41d103d3b3 fix(deploy): separate vars from secrets in deploy button form
- Move BETTER_AUTH_URL and TRUSTED_ORIGINS from .dev.vars.example to
  wrangler.toml [vars] so they render as text inputs, not password fields
- Keep only BETTER_AUTH_SECRET in .dev.vars.example (true secret)
- Add cloudflare.bindings descriptions in package.json for deploy form
- Remove unused concurrently dependency

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 10:48:57 -04:00
saltbo 3b80eff413 feat: migrate from CF Pages to Workers with one-click deploy button
- Replace Pages Functions with Workers entry (`workers/bootstrap.ts`)
- Add Deploy to Cloudflare button in README
- Integrate `@cloudflare/vite-plugin` for CF dev with HMR
- Integrate `@hono/vite-dev-server` for Node dev with HMR
- `npm run dev` now defaults to CF Workers mode
- Add `run_worker_first = ["/api/*"]` so static assets stay free
- Extract shared Node bootstrap (`server/bootstrap.ts`) for reuse
- Update all docs from Pages to Workers references

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 10:40:15 -04:00
saltbo 3d14f3ceae chore(dev): enhance db:reset for D1 local and add top-level D1 binding
- db:reset script supports --pages flag to reset D1 local database
- Added db:reset:pages npm script
- Added top-level D1 binding in wrangler.toml for local dev
- Standardized dev env var names in .dev.vars

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 23:52:24 -04:00
saltbo bce4da3ebf feat(quota): add default org quota with admin settings UI and db:reset script
New users get a 10MB default storage quota (configurable via admin settings).
Admin can set the default in Settings with MB/GB unit selector. Added db:generate,
db:migrate, and db:reset scripts; dev server now reads .dev.vars automatically.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 23:37:54 -04:00
saltbo 7d98848c09 chore(deps): bump drizzle-orm to 0.45.2, drizzle-kit to 0.31.10
Fixes GHSA-gpj5-g38j-94v9 / CVE-2026-39356 (high severity) — drizzle-orm
versions <0.45.2 allowed SQL injection via improperly escaped SQL
identifiers. Our codebase does not actually feed user input through sql
identifier interpolation (all template values are Drizzle column
references or parameterized values), so the CVE was not exploitable,
but upgrading is still the right move:

- Silences the Dependabot high-severity alert.
- Resolves a long-standing peer dependency violation: better-auth 1.6.2
  requires drizzle-orm >=0.41.0 and drizzle-kit >=0.31.4, but the
  project had drizzle-orm 0.39.3 and drizzle-kit 0.30.6 installed via
  lockfile dedup against a stale range.

npm install --save moves both deps to the versions the project
package.json already specifies (^0.45.2 and ^0.31.10). Full quality
gate clean: typecheck, 1000 Node tests, 18 CF Worker tests, biome lint.

A remaining moderate-severity alert (GHSA-67mh-4wv8-2f99 — esbuild dev
server exposure) is carried transitively via drizzle-kit →
@esbuild-kit/esm-loader → esbuild and is not fixable without a
drizzle-kit internal update. The vulnerability only affects esbuild's
dev server, which drizzle-kit does not spin up during its CLI
operations, so there is no real-world exposure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:19:47 -04:00
saltbo 67475be340 feat: replace SVAR with custom file manager, path-based navigation, sidebar folder tree
- Replace @svar-ui/react-filemanager with custom file manager built on
  @tanstack/react-table, @dnd-kit, and shadcn/ui (ContextMenu, Breadcrumb,
  Table, Checkbox, ToggleGroup, Collapsible)
- List view with sortable columns (folders-first), Grid view with card layout
- Right-click context menu, row dropdown actions, drag-and-drop file moving
- Dialogs: rename, new folder, delete confirm, move (with folder picker)
- URL-based path navigation (?path=folder/subfolder), breadcrumb from URL
- Backend: parent field stores materialized path instead of folder ID,
  cascade rename/move updates all descendants, self-move protection
- Backend: type filter API (?type=photos|videos|music|documents) for
  cross-folder file browsing by MIME type
- Sidebar: My Files with lazy-loading folder tree (auto-expands to current
  path), Photos/Videos/Music/Documents categories, Trash
- Settings moved to user avatar dropdown menu
- Migrate pnpm references to npm across docs and config
- i18n: all new keys in en.json and zh.json

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 11:26:52 -04:00
dependabot[bot] f9e879935a chore(deps): bump esbuild and drizzle-kit (#274)
Bumps [esbuild](https://github.com/evanw/esbuild) to 0.27.3 and updates ancestor dependency [drizzle-kit](https://github.com/drizzle-team/drizzle-orm). These dependencies need to be updated together.


Updates `esbuild` from 0.18.20 to 0.27.3
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG-2023.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.18.20...v0.27.3)

Updates `drizzle-kit` from 0.30.6 to 0.31.10
- [Release notes](https://github.com/drizzle-team/drizzle-orm/releases)
- [Commits](https://github.com/drizzle-team/drizzle-orm/compare/drizzle-kit@0.30.6...drizzle-kit@0.31.10)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-version: 0.27.3
  dependency-type: indirect
- dependency-name: drizzle-kit
  dependency-version: 0.31.10
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-10 08:37:08 -04:00
saltbo d70295f48e chore: upgrade vite to 7.x and related plugins
- vite 6.x → 7.3.2
- @vitejs/plugin-react-swc 3.x → 4.3.0
- @tailwindcss/vite → 4.2.2
- @tanstack/router-plugin → 1.167.12

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 00:36:13 -04:00
saltbo 7c77731e96 chore: update CI for npm, upgrade Node to 24 with volta pin
- Replace pnpm with npm in GitHub Actions workflow
- Upgrade Node.js from 20 to 24 in CI, Dockerfile, and engines
- Pin Node 24.14.1 via volta

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 00:31:08 -04:00