Maintain a CHANGELOG.md (Keep a Changelog format) at the repo root and surface
it on the admin About page:
- The page now shows the running build's short commit hash next to the version,
linked to the GitHub commit. Commit is injected at build time via a new
resolveAppCommit() (ZPAN_APP_COMMIT -> WORKERS_CI_COMMIT_SHA -> git rev-parse),
wired through vite/tsup defines, the node entry, Docker, and CI.
- A new admin-only GET /api/system/changelog endpoint fetches CHANGELOG.md from
master on GitHub, caches it, parses the latest released version, and reports
whether an update is available (semver compare against the running version).
- The About page renders a "latest version" row with an update-available badge
and a side drawer that displays the changelog markdown.
Tests cover the semver compare, changelog parse/fetch caching, the API wrapper,
and the route (admin-gated, parsed payload).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cloudflare Workers Builds deploys master continuously without setting
ZPAN_APP_VERSION, and its checkout has no tags, so git describe --always fell
back to a bare commit hash — the deployed Worker showed a commit instead of a
version. package.json is present in every build environment, so read the
version from it when ZPAN_APP_VERSION is unset. Release/Docker/GH-action
deploys still pass the exact tag via the env var, which keeps priority.
Release process: bump package.json on each release so Workers Builds reports
the right version.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The bundled app-version.mjs self-exec block (`import.meta.url ===
file://${process.argv[1]}`) fired at runtime once tsup inlined it into
entry-node.js, since the container runs `node dist-server/entry-node.js`.
It shelled out to `git describe`, which is absent in the image, crashing
the server on boot and leaving the compose stack unhealthy. Drop the
unused CLI entry; resolveAppVersion is still build-time inlined.
Also stop the CLI build from overwriting the `latest` tag: metadata-action
defaults to latest=auto, so cli-meta re-added a bare `latest` that, running
after the server build, clobbered it with the downloader image. Pin
latest=false on cli-meta.
Add a docker-smoke CI job that runs `docker compose up --wait` so an image
that fails to boot turns CI red, and give the GeoIP download --retry to cut
flake on that external fetch.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(version): resolve app version at runtime in node entry
E2E runs the Node server via tsx, which bypasses the tsup build-time
define, leaving __ZPAN_APP_VERSION__ unset so getAppVersion throws and
/api/licensing/pair returns 500. Resolve the version at runtime via
resolveAppVersion (git describe) when the global is unset; in the built
output the define inlines the constant, so the branch is never reached
and git is not invoked in production. Keeps the git-describe version as
the single source of truth across all paths.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(version): inject app version into docker build
The Docker build excludes .git from its context (.dockerignore), so the
build-time git describe in build:node would throw and break the image
build. Let resolveAppVersion read ZPAN_APP_VERSION, set it from an
APP_VERSION build arg in the builder stage, and pass the release tag from
the release workflow. git describe stays the default everywhere else, so
the version is unified across CF Workers, tsx, and Docker.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Support independent Pro and Business licensing, migrate Cloud store integration through the SDK, gate Business-only credit billing features, and validate the Cloud store E2E flow.
- 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>
Reads GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET from .dev.vars
and inserts the OAuth provider config into system_options during
database reset, so GitHub login works out of the box locally.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(auth): add dynamic OAuth provider system
Admin can configure OAuth/OIDC providers in the database via API.
All 35 built-in better-auth providers are registered as async functions
that read config from system_options at runtime. Custom OIDC providers
use the genericOAuth plugin with configs loaded at auth init time.
New endpoints:
- GET /api/auth-providers (public, enabled only, no secrets)
- GET /api/auth-providers/admin (admin, all configs, masked secrets)
- PUT /api/auth-providers/admin/:providerId (admin, upsert)
- DELETE /api/auth-providers/admin/:providerId (admin, remove)
Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: async createTestApp compat in email and invite test files
createAuth became async in the OAuth PR, which made createTestApp async.
Email and invite code test files need await + Awaited<> type wrappers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
db-reset.ts referenced non-existent 'zpan-db-local'; update to
'zpan-db-staging' with --env staging to match wrangler.toml. Add
npm run db:migrate:d1 shortcut and update CONTRIBUTING.md.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: remove custom filePath, enforce tenant-isolated storage path
Replace user-customizable filePath template with a hardcoded
tenant-isolated pattern ($ORG_ID/$UID/$NOW_DATE/$RAND_16KEY$RAW_EXT).
This ensures proper tenant isolation and removes unnecessary complexity.
The DB column is preserved to avoid migration; code simply stops
reading/writing it.
Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f
* refactor: clean up dead tokens, locale keys, and simplify path template
Remove unused template tokens ($UUID, $RAW_NAME, $NOW_YEAR, $NOW_MONTH,
$NOW_DAY) and corresponding TemplateVars fields (uuid, rawName) since
the hardcoded template doesn't use them. Remove orphaned i18n keys for
fieldFilePath. Update DB schema default to empty string. Add storage
service unit tests.
Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f
---------
Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
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>
- 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>
- 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>
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>