mirror of
https://github.com/saltbo/zpan.git
synced 2026-10-29 18:05:37 +08:00
feat: fix CF Pages deployment and update project docs
- Restructure for wrangler Workers mode (main + [assets] + run_worker_first) - Move wrangler.toml to project root, add nodejs_compat flag - Move migrations to root, managed by wrangler d1 migrations - Web builds to root dist/, wrangler bundles worker entry directly - Update CONTRIBUTING.md with full dev workflow and quality gates - CLAUDE.md indexes docs instead of duplicating content Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
729e64e60d
commit
175c52a548
@@ -2,37 +2,23 @@
|
||||
|
||||
## Project Overview
|
||||
|
||||
ZPan v2 is an open-source, S3-native file hosting platform written in TypeScript. It supports deployment to Cloudflare Pages (zero-ops) and Docker (self-hosted).
|
||||
ZPan v2 is an open-source, S3-native file hosting platform written in TypeScript. Cloudflare Pages is the primary deployment target, Node.js (Docker) is backup.
|
||||
|
||||
Core architecture: clients upload directly to S3-compatible storage via presigned URLs, bypassing server bandwidth.
|
||||
|
||||
## Tech Stack
|
||||
## Key Context
|
||||
|
||||
- **Runtime**: Hono (runs on CF Workers + Node.js)
|
||||
- **Database**: Drizzle ORM (D1 / SQLite / PostgreSQL)
|
||||
- **Auth**: Better Auth (email/password, social login, OIDC)
|
||||
- **Storage**: @aws-sdk/client-s3 (R2, AWS S3, MinIO, any S3-compatible)
|
||||
- **Frontend**: TBD
|
||||
- Monorepo: `packages/server` (Hono API), `packages/web` (React SPA), `packages/shared` (types/schemas)
|
||||
- Dual runtime: `entry-cloudflare.ts` (CF Workers + D1) and `entry-node.ts` (Node + SQLite)
|
||||
- Tests are co-located: `*.test.ts` (Node), `*.cf-test.ts` (CF Workers)
|
||||
- Migrations: drizzle-kit generates SQL → wrangler manages D1 state
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── app/ # Business logic (routes, middleware, services)
|
||||
├── platform/ # Platform abstraction (CF vs Node.js)
|
||||
├── entry-cloudflare.ts # CF Pages Functions entry
|
||||
└── entry-node.ts # Docker / Node.js entry
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
pnpm install # Install dependencies
|
||||
pnpm dev # Start local dev server
|
||||
pnpm test # Run tests
|
||||
pnpm build # Build for production
|
||||
```
|
||||
## Docs Index
|
||||
|
||||
- [CONTRIBUTING.md](CONTRIBUTING.md) — setup, commands, quality gates, migration workflow, deployment
|
||||
- [docs/architecture.md](docs/architecture.md) — system architecture, tech decisions, platform abstraction
|
||||
- [V2_ROADMAP.md](V2_ROADMAP.md) — product positioning, release plan (v2.0–v2.9)
|
||||
- [docs/roadmap/](docs/roadmap/) — per-version technical specs (v2.0.md–v2.9.md)
|
||||
## Commit Convention
|
||||
|
||||
Use Conventional Commits (feat:, fix:, docs:, etc.). PRs target the master branch.
|
||||
Conventional Commits (`feat:`, `fix:`, `docs:`, etc.). PRs target `master`.
|
||||
|
||||
+66
-13
@@ -4,12 +4,7 @@ By participating in this project, you agree to abide by our [Code of Conduct](CO
|
||||
|
||||
## Setup
|
||||
|
||||
ZPan v2 is written in TypeScript.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Node.js 20+
|
||||
- pnpm
|
||||
Prerequisites: Node.js 20+, pnpm 10+
|
||||
|
||||
```sh
|
||||
git clone git@github.com:saltbo/zpan.git
|
||||
@@ -20,18 +15,76 @@ pnpm install
|
||||
## Development
|
||||
|
||||
```sh
|
||||
pnpm dev # Start local dev server
|
||||
pnpm test # Run tests
|
||||
pnpm build # Build for production
|
||||
pnpm dev # Start server + web dev servers
|
||||
pnpm pages:dev # Build & run as Cloudflare Pages locally
|
||||
```
|
||||
|
||||
## Commits
|
||||
## Quality Gates
|
||||
|
||||
We use [Conventional Commits](https://www.conventionalcommits.org). Please format your commit messages accordingly.
|
||||
Every commit and PR must pass these checks. Lefthook enforces them on pre-commit.
|
||||
|
||||
## Pull Requests
|
||||
```sh
|
||||
pnpm lint # Biome — lint + format check
|
||||
pnpm typecheck # TypeScript strict mode, all packages
|
||||
pnpm test # Unit + integration tests (Node runtime, 90% coverage gate)
|
||||
pnpm test:cf # Integration tests (Cloudflare Workers runtime)
|
||||
pnpm e2e # Playwright E2E tests
|
||||
```
|
||||
|
||||
Push your branch to your fork and open a pull request against the `master` branch.
|
||||
## Adding a Feature
|
||||
|
||||
1. **Write code** in the relevant package (`packages/server`, `packages/web`, `packages/shared`)
|
||||
2. **Write tests** — co-locate with source as `*.test.ts` (Node) or `*.cf-test.ts` (CF Workers)
|
||||
3. **Run checks** — `pnpm lint && pnpm typecheck && pnpm test && pnpm test:cf`
|
||||
4. **Coverage** — new code must maintain 90%+ line coverage on `packages/server`
|
||||
5. **Commit** — use [Conventional Commits](https://www.conventionalcommits.org) (`feat:`, `fix:`, `docs:`, etc.)
|
||||
6. **PR** — target the `master` branch
|
||||
|
||||
## Database Migrations
|
||||
|
||||
Schema is defined in `packages/server/src/db/schema.ts` and `auth-schema.ts`.
|
||||
|
||||
```sh
|
||||
# After modifying schema, generate migration SQL
|
||||
pnpm --filter @zpan/server exec drizzle-kit generate
|
||||
|
||||
# Apply locally
|
||||
wrangler d1 migrations apply zpan-db --local
|
||||
|
||||
# Apply to production
|
||||
wrangler d1 migrations apply zpan-db --remote
|
||||
```
|
||||
|
||||
Migration files live in `migrations/` at project root. Always commit them.
|
||||
|
||||
## Deployment
|
||||
|
||||
Primary target is Cloudflare Pages. Node.js (Docker) is the backup runtime.
|
||||
|
||||
```sh
|
||||
pnpm build:pages # Build frontend to dist/
|
||||
pnpm deploy # Build + deploy to Cloudflare Pages
|
||||
```
|
||||
|
||||
The wrangler config (`wrangler.toml`) uses:
|
||||
- `main` → worker entry point (bundled by wrangler)
|
||||
- `[assets]` → static frontend with SPA fallback
|
||||
- `run_worker_first = ["/api/*"]` → only API routes hit the worker
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
zpan/
|
||||
├── packages/
|
||||
│ ├── server/ # Hono API (routes, middleware, auth, platform abstraction)
|
||||
│ ├── web/ # React frontend (TanStack Router, shadcn/ui)
|
||||
│ └── shared/ # Shared types, Zod schemas, constants
|
||||
├── migrations/ # D1/SQLite migrations (drizzle-kit generated, wrangler managed)
|
||||
├── e2e/ # Playwright E2E tests
|
||||
├── wrangler.toml # Cloudflare Pages + Workers config
|
||||
├── biome.json # Lint + format config
|
||||
└── lefthook.yml # Pre-commit hooks
|
||||
```
|
||||
|
||||
## Financial Contributions
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
"scripts": {
|
||||
"dev": "pnpm --filter @zpan/server --filter @zpan/web --parallel dev",
|
||||
"build": "pnpm --filter @zpan/shared build && pnpm --filter @zpan/server build && pnpm --filter @zpan/web build",
|
||||
"build:pages": "pnpm --filter @zpan/web exec vite build",
|
||||
"pages:dev": "pnpm build:pages && wrangler dev",
|
||||
"deploy": "pnpm build:pages && wrangler deploy",
|
||||
"typecheck": "pnpm -r typecheck",
|
||||
"test": "pnpm -r --parallel test",
|
||||
"test:cf": "pnpm --filter @zpan/server test:cf",
|
||||
|
||||
@@ -2,7 +2,7 @@ import { defineConfig } from 'drizzle-kit'
|
||||
|
||||
export default defineConfig({
|
||||
schema: ['./src/db/schema.ts', './src/db/auth-schema.ts'],
|
||||
out: './drizzle',
|
||||
out: '../../migrations',
|
||||
dialect: 'sqlite',
|
||||
dbCredentials: {
|
||||
url: process.env.DATABASE_URL || 'zpan.db',
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
import { Hono } from 'hono'
|
||||
import { handle } from 'hono/cloudflare-pages'
|
||||
import { createApp } from './app'
|
||||
import { createAuth } from './auth'
|
||||
import { createCloudflarePlatform } from './platform/cloudflare'
|
||||
|
||||
type Env = {
|
||||
Bindings: {
|
||||
DB: D1Database
|
||||
BETTER_AUTH_SECRET: string
|
||||
BETTER_AUTH_URL?: string
|
||||
}
|
||||
interface Env {
|
||||
DB: D1Database
|
||||
BETTER_AUTH_SECRET: string
|
||||
BETTER_AUTH_URL?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
const app = new Hono<Env>().all('/*', (c) => {
|
||||
const platform = createCloudflarePlatform(c.env)
|
||||
const auth = createAuth(platform.db, c.env.BETTER_AUTH_SECRET, c.env.BETTER_AUTH_URL)
|
||||
return createApp(platform, auth).fetch(c.req.raw)
|
||||
})
|
||||
|
||||
export const onRequest = handle(app)
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const platform = createCloudflarePlatform(env)
|
||||
const auth = createAuth(platform.db, env.BETTER_AUTH_SECRET, env.BETTER_AUTH_URL)
|
||||
return createApp(platform, auth).fetch(request)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@ const api = createApp(platform, auth)
|
||||
|
||||
const app = new Hono()
|
||||
app.route('/', api)
|
||||
app.use('/*', serveStatic({ root: '../web/dist' }))
|
||||
app.get('/*', serveStatic({ root: '../web/dist', path: 'index.html' }))
|
||||
app.use('/*', serveStatic({ root: '../../dist' }))
|
||||
app.get('/*', serveStatic({ root: '../../dist', path: 'index.html' }))
|
||||
|
||||
const port = Number(process.env.PORT) || 8222
|
||||
console.log(`ZPan server running on http://localhost:${port}`)
|
||||
|
||||
@@ -5,11 +5,11 @@ import { defineConfig } from 'vitest/config'
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
cloudflareTest(async () => {
|
||||
const migrationsPath = path.join(__dirname, 'drizzle')
|
||||
const migrationsPath = path.join(__dirname, '../../migrations')
|
||||
const migrations = await readD1Migrations(migrationsPath)
|
||||
|
||||
return {
|
||||
wrangler: { configPath: './wrangler.toml' },
|
||||
wrangler: { configPath: '../../wrangler.toml' },
|
||||
miniflare: {
|
||||
bindings: { TEST_MIGRATIONS: migrations },
|
||||
},
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
name = "zpan"
|
||||
compatibility_date = "2024-12-01"
|
||||
pages_build_output_dir = "../../dist"
|
||||
|
||||
[vars]
|
||||
BETTER_AUTH_SECRET = "zpan-test-secret"
|
||||
|
||||
[[d1_databases]]
|
||||
binding = "DB"
|
||||
database_name = "zpan-db"
|
||||
database_id = "local"
|
||||
@@ -5,6 +5,10 @@ import react from '@vitejs/plugin-react-swc'
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
export default defineConfig({
|
||||
build: {
|
||||
outDir: '../../dist',
|
||||
emptyOutDir: true,
|
||||
},
|
||||
plugins: [TanStackRouterVite({}), react(), tailwindcss()],
|
||||
resolve: {
|
||||
alias: {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
name = "zpan"
|
||||
compatibility_date = "2024-12-01"
|
||||
compatibility_flags = ["nodejs_compat"]
|
||||
main = "./packages/server/src/entry-cloudflare.ts"
|
||||
|
||||
[assets]
|
||||
directory = "./dist"
|
||||
not_found_handling = "single-page-application"
|
||||
binding = "ASSETS"
|
||||
run_worker_first = ["/api/*"]
|
||||
|
||||
[vars]
|
||||
BETTER_AUTH_SECRET = "zpan-test-secret"
|
||||
|
||||
[[d1_databases]]
|
||||
binding = "DB"
|
||||
database_name = "zpan-db"
|
||||
database_id = "local"
|
||||
migrations_dir = "./migrations"
|
||||
Reference in New Issue
Block a user