edgebase-worker 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.edgebase/schema-history.json +45 -0
- package/.turbo/turbo-build.log +27 -0
- package/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/55cd07de6aa91baef398b67f95584d9bc1e6c1f78e9c8f9e0871b8c18e909ad4.sqlite +0 -0
- package/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/55cd07de6aa91baef398b67f95584d9bc1e6c1f78e9c8f9e0871b8c18e909ad4.sqlite-shm +0 -0
- package/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/55cd07de6aa91baef398b67f95584d9bc1e6c1f78e9c8f9e0871b8c18e909ad4.sqlite-wal +0 -0
- package/.wrangler/tmp/bundle-k6w7ik/middleware-insertion-facade.js +11 -0
- package/.wrangler/tmp/bundle-k6w7ik/middleware-loader.entry.ts +134 -0
- package/.wrangler/tmp/bundle-k6w7ik/strip-cf-connecting-ip-header.js +13 -0
- package/.wrangler/tmp/dev-VDWiSm/index.js +14340 -0
- package/.wrangler/tmp/dev-VDWiSm/index.js.map +8 -0
- package/README.md +166 -0
- package/edgebase.schema.ts +48 -0
- package/migrations/0000_init_system_tables.sql +29 -0
- package/migrations/0001_schema_changes.sql +7 -0
- package/migrations/0002_add_todos_user_id.sql +2 -0
- package/migrations/0003_add_admin_tables.sql +21 -0
- package/package.json +25 -0
- package/src/index.ts +18 -0
- package/tsconfig.json +21 -0
- package/wrangler.toml +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# EdgeBase Worker - Example Deployment
|
|
2
|
+
|
|
3
|
+
This is an **example deployment** of EdgeBase using **published NPM packages**.
|
|
4
|
+
|
|
5
|
+
> **For External Users:** This demonstrates how to use `@edgebasejs/worker` and the EdgeBase CLI to deploy a complete backend to Cloudflare Workers, independently from the monorepo.
|
|
6
|
+
|
|
7
|
+
This package is the Cloudflare Workers backend only.
|
|
8
|
+
The admin UI is deployed separately via `@edgebasejs/admin-console` and connects to this worker URL.
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- **Auth**: Login/Register with email and password
|
|
13
|
+
- **Todos CRUD**: Create, read, update, delete todos
|
|
14
|
+
- **User Isolation**: Each user only sees their own todos
|
|
15
|
+
- **Session Management**: Token-based authentication
|
|
16
|
+
|
|
17
|
+
## Schema & Migrations
|
|
18
|
+
|
|
19
|
+
- Schema source: `apps/worker/edgebase.schema.ts`
|
|
20
|
+
- Migrations: `apps/worker/migrations/*.sql`
|
|
21
|
+
- Worker entrypoint: `apps/worker/src/index.ts` (EdgeBase worker factory)
|
|
22
|
+
|
|
23
|
+
Generate SDK + migrations after schema changes:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
cd apps/worker
|
|
27
|
+
npx edgebase generate
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Apply migrations to local D1:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
cd apps/worker
|
|
34
|
+
npx edgebase migrate
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Note: In development, the worker can auto-initialize tables from the schema.
|
|
38
|
+
In production, run migrations explicitly.
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Install dependencies
|
|
44
|
+
npm install
|
|
45
|
+
|
|
46
|
+
# Start worker development server
|
|
47
|
+
npm run dev
|
|
48
|
+
# Opens: http://localhost:8787 (Worker API)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Start admin console separately (in another terminal):
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
cd ../admin-console
|
|
55
|
+
npm run dev
|
|
56
|
+
# Opens: http://localhost:3001
|
|
57
|
+
# First open asks for Worker URL (e.g. http://localhost:8787)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Build/publish commands are available via the EdgeBase CLI:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Start development
|
|
64
|
+
npm run dev
|
|
65
|
+
|
|
66
|
+
# Build for deployment
|
|
67
|
+
npm run build
|
|
68
|
+
|
|
69
|
+
# Deploy to Cloudflare Workers
|
|
70
|
+
npm run deploy
|
|
71
|
+
|
|
72
|
+
# Publish to npm
|
|
73
|
+
npm run publish
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## API Endpoints
|
|
77
|
+
|
|
78
|
+
### Auth
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Login
|
|
82
|
+
curl -X POST http://localhost:8787/auth/login \
|
|
83
|
+
-H "Content-Type: application/json" \
|
|
84
|
+
-d '{"email":"user@example.com","password":"password123"}'
|
|
85
|
+
|
|
86
|
+
# Register
|
|
87
|
+
curl -X POST http://localhost:8787/auth/register \
|
|
88
|
+
-H "Content-Type: application/json" \
|
|
89
|
+
-d '{"email":"user@example.com","password":"password123"}'
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Todos
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
TOKEN="..." # From login response
|
|
96
|
+
|
|
97
|
+
# Create todo
|
|
98
|
+
curl -X POST http://localhost:8787/todos \
|
|
99
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
100
|
+
-H "Content-Type: application/json" \
|
|
101
|
+
-d '{"title":"Buy milk","completed":false}'
|
|
102
|
+
|
|
103
|
+
# Get all todos
|
|
104
|
+
curl http://localhost:8787/todos \
|
|
105
|
+
-H "Authorization: Bearer $TOKEN"
|
|
106
|
+
|
|
107
|
+
# Get single todo
|
|
108
|
+
curl http://localhost:8787/todos/TODOID \
|
|
109
|
+
-H "Authorization: Bearer $TOKEN"
|
|
110
|
+
|
|
111
|
+
# Update todo
|
|
112
|
+
curl -X PATCH http://localhost:8787/todos/TODOID \
|
|
113
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
114
|
+
-H "Content-Type: application/json" \
|
|
115
|
+
-d '{"completed":true}'
|
|
116
|
+
|
|
117
|
+
# Delete todo
|
|
118
|
+
curl -X DELETE http://localhost:8787/todos/TODOID \
|
|
119
|
+
-H "Authorization: Bearer $TOKEN"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Storage
|
|
123
|
+
|
|
124
|
+
Uses **Cloudflare D1** (SQLite). System tables and entity tables are managed via EdgeBase migrations.
|
|
125
|
+
|
|
126
|
+
## Deployment
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# Build for production
|
|
130
|
+
npm run build
|
|
131
|
+
|
|
132
|
+
# Deploy to Cloudflare Workers
|
|
133
|
+
npm run deploy
|
|
134
|
+
|
|
135
|
+
# Or publish worker package
|
|
136
|
+
npm run publish
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Set environment variables in `wrangler.toml` for production.
|
|
140
|
+
|
|
141
|
+
### About the Admin Console
|
|
142
|
+
|
|
143
|
+
The admin console is a separate deployment:
|
|
144
|
+
- Uses this worker via its URL
|
|
145
|
+
- On first start it asks for the Worker URL
|
|
146
|
+
- Supports login with an existing admin account
|
|
147
|
+
- Supports first-admin setup when no admin exists yet
|
|
148
|
+
|
|
149
|
+
## Architecture
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
Client (React Native + Expo)
|
|
153
|
+
↓
|
|
154
|
+
EdgeBase SDK
|
|
155
|
+
↓
|
|
156
|
+
This Worker (REST API)
|
|
157
|
+
↓
|
|
158
|
+
In-Memory Storage (or D1 in production)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The app communicates with this worker via HTTP. The SDK handles:
|
|
162
|
+
|
|
163
|
+
- Authentication (token management)
|
|
164
|
+
- Offline-first caching
|
|
165
|
+
- Sync logic
|
|
166
|
+
- Query caching
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// EdgeBase schema definition for worker
|
|
2
|
+
import type { DatabaseSchema } from '@edgebasejs/types';
|
|
3
|
+
|
|
4
|
+
const schema: DatabaseSchema = {
|
|
5
|
+
entities: {
|
|
6
|
+
todos: {
|
|
7
|
+
fields: {
|
|
8
|
+
id: {
|
|
9
|
+
type: 'string',
|
|
10
|
+
required: true,
|
|
11
|
+
primary: true,
|
|
12
|
+
},
|
|
13
|
+
userId: {
|
|
14
|
+
type: 'reference',
|
|
15
|
+
reference: 'users',
|
|
16
|
+
required: true,
|
|
17
|
+
},
|
|
18
|
+
title: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
required: true,
|
|
21
|
+
},
|
|
22
|
+
completed: {
|
|
23
|
+
type: 'boolean',
|
|
24
|
+
required: true,
|
|
25
|
+
default: false,
|
|
26
|
+
},
|
|
27
|
+
createdAt: {
|
|
28
|
+
type: 'timestamp',
|
|
29
|
+
required: true,
|
|
30
|
+
},
|
|
31
|
+
updatedAt: {
|
|
32
|
+
type: 'timestamp',
|
|
33
|
+
required: true,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
accessRules: {
|
|
37
|
+
create: (user: any, data: any) => data.userId === user.id,
|
|
38
|
+
read: (user: any, data: any) => data.userId === user.id,
|
|
39
|
+
update: (user: any, existing: any, updates: any) =>
|
|
40
|
+
existing.userId === user.id && (!updates.userId || updates.userId === existing.userId),
|
|
41
|
+
delete: (user: any, existing: any) => existing.userId === user.id,
|
|
42
|
+
},
|
|
43
|
+
indexes: [{ fields: ['userId'] }],
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export default schema;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
2
|
+
id TEXT PRIMARY KEY,
|
|
3
|
+
email TEXT NOT NULL UNIQUE,
|
|
4
|
+
password_hash TEXT NOT NULL,
|
|
5
|
+
created_at INTEGER NOT NULL,
|
|
6
|
+
updated_at INTEGER NOT NULL
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
CREATE TABLE IF NOT EXISTS refresh_tokens (
|
|
10
|
+
id TEXT PRIMARY KEY,
|
|
11
|
+
user_id TEXT NOT NULL,
|
|
12
|
+
token TEXT NOT NULL UNIQUE,
|
|
13
|
+
expires_at INTEGER NOT NULL,
|
|
14
|
+
created_at INTEGER NOT NULL,
|
|
15
|
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
CREATE TABLE IF NOT EXISTS sync_metadata (
|
|
19
|
+
entity TEXT NOT NULL,
|
|
20
|
+
record_id TEXT NOT NULL,
|
|
21
|
+
version INTEGER NOT NULL,
|
|
22
|
+
updated_at INTEGER NOT NULL,
|
|
23
|
+
deleted_at INTEGER,
|
|
24
|
+
PRIMARY KEY (entity, record_id)
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
CREATE INDEX IF NOT EXISTS idx_sync_metadata_updated ON sync_metadata(entity, updated_at);
|
|
28
|
+
CREATE INDEX IF NOT EXISTS idx_refresh_tokens_user ON refresh_tokens(user_id);
|
|
29
|
+
CREATE INDEX IF NOT EXISTS idx_refresh_tokens_token ON refresh_tokens(token);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS admins (
|
|
2
|
+
id TEXT PRIMARY KEY,
|
|
3
|
+
email TEXT NOT NULL UNIQUE,
|
|
4
|
+
password_hash TEXT NOT NULL,
|
|
5
|
+
role TEXT NOT NULL,
|
|
6
|
+
is_active INTEGER NOT NULL DEFAULT 1,
|
|
7
|
+
created_at INTEGER NOT NULL,
|
|
8
|
+
updated_at INTEGER NOT NULL
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
CREATE TABLE IF NOT EXISTS admin_refresh_tokens (
|
|
12
|
+
id TEXT PRIMARY KEY,
|
|
13
|
+
admin_id TEXT NOT NULL,
|
|
14
|
+
token TEXT NOT NULL UNIQUE,
|
|
15
|
+
expires_at INTEGER NOT NULL,
|
|
16
|
+
created_at INTEGER NOT NULL,
|
|
17
|
+
FOREIGN KEY (admin_id) REFERENCES admins(id) ON DELETE CASCADE
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
CREATE INDEX IF NOT EXISTS idx_admin_refresh_tokens_admin ON admin_refresh_tokens(admin_id);
|
|
21
|
+
CREATE INDEX IF NOT EXISTS idx_admin_refresh_tokens_token ON admin_refresh_tokens(token);
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "edgebase-worker",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "EdgeBase Worker - Cloudflare Workers deployment (uses published npm packages)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@edgebasejs/worker": "0.1.12"
|
|
9
|
+
},
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"@cloudflare/workers-types": "^4.20240605.0",
|
|
12
|
+
"@edgebasejs/cli": "^0.9.1",
|
|
13
|
+
"@edgebasejs/types": "^0.1.12",
|
|
14
|
+
"@types/node": "^25.2.2",
|
|
15
|
+
"typescript": "^5.4.5",
|
|
16
|
+
"wrangler": "^3.28.0"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"dev": "npm run dev:worker",
|
|
20
|
+
"dev:worker": "wrangler dev --ip 0.0.0.0",
|
|
21
|
+
"build": "edgebase build",
|
|
22
|
+
"type-check": "../../node_modules/.bin/tsc --noEmit",
|
|
23
|
+
"deploy": "npm run build && wrangler deploy"
|
|
24
|
+
}
|
|
25
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createEdgeBaseWorker } from '@edgebasejs/worker';
|
|
2
|
+
import schema from '../edgebase.schema';
|
|
3
|
+
|
|
4
|
+
const app = createEdgeBaseWorker({
|
|
5
|
+
schema,
|
|
6
|
+
getDb: (c) => c.env.DB,
|
|
7
|
+
getJwtSecret: (c) => c.env.JWT_SECRET,
|
|
8
|
+
getEnvironment: (c) => c.env.ENVIRONMENT,
|
|
9
|
+
getR2Bucket: (c) => c.env.FILES,
|
|
10
|
+
apiVersion: '0.1.0',
|
|
11
|
+
// Auto-initialize database on first request (safe with CREATE TABLE IF NOT EXISTS)
|
|
12
|
+
autoInitialize: true,
|
|
13
|
+
// Admin Console disabled - RSC rendering not supported on Edge Runtime
|
|
14
|
+
// For admin features, deploy a separate instance or use external admin service
|
|
15
|
+
adminConsole: false,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export default app;
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"lib": ["ES2020"],
|
|
4
|
+
"target": "ES2020",
|
|
5
|
+
"outDir": "./dist",
|
|
6
|
+
"types": ["@cloudflare/workers-types", "node"],
|
|
7
|
+
"jsx": "react-jsx",
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"skipDefaultLibCheck": true,
|
|
10
|
+
"noEmit": false,
|
|
11
|
+
"moduleResolution": "node",
|
|
12
|
+
"module": "ES2020",
|
|
13
|
+
"declaration": false,
|
|
14
|
+
"baseUrl": ".",
|
|
15
|
+
"paths": {
|
|
16
|
+
"@edgebasejs/types": ["../../packages/shared-types/src"]
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"include": ["src"],
|
|
20
|
+
"exclude": ["node_modules", "dist", "edgebase.schema.ts"]
|
|
21
|
+
}
|
package/wrangler.toml
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name = "edgebase-worker"
|
|
2
|
+
main = "src/index.ts"
|
|
3
|
+
compatibility_date = "2024-09-23"
|
|
4
|
+
compatibility_flags = ["nodejs_compat"]
|
|
5
|
+
|
|
6
|
+
# Default environment (development/local)
|
|
7
|
+
[[d1_databases]]
|
|
8
|
+
binding = "DB"
|
|
9
|
+
database_name = "edgebase"
|
|
10
|
+
database_id = "2ddd0305-d96d-48ad-84bd-5d7697c33728"
|
|
11
|
+
|
|
12
|
+
[[r2_buckets]]
|
|
13
|
+
binding = "FILES"
|
|
14
|
+
bucket_name = "edgebase-files"
|
|
15
|
+
preview_bucket_name = "edgebase-files-preview"
|
|
16
|
+
|
|
17
|
+
[vars]
|
|
18
|
+
JWT_SECRET = "dev-secret-change-in-production"
|
|
19
|
+
ENVIRONMENT = "development"
|
|
20
|
+
|
|
21
|
+
# Production environment
|
|
22
|
+
[env.production]
|
|
23
|
+
[[env.production.d1_databases]]
|
|
24
|
+
binding = "DB"
|
|
25
|
+
database_name = "edgebase"
|
|
26
|
+
database_id = "2ddd0305-d96d-48ad-84bd-5d7697c33728"
|
|
27
|
+
|
|
28
|
+
[[env.production.r2_buckets]]
|
|
29
|
+
binding = "FILES"
|
|
30
|
+
bucket_name = "edgebase-files"
|
|
31
|
+
|
|
32
|
+
[env.production.vars]
|
|
33
|
+
JWT_SECRET = "2db53d655ad743e29574e8d1912440afbaacf0651fb002e0676dfc490f23a968"
|
|
34
|
+
ENVIRONMENT = "production"
|
|
35
|
+
|
|
36
|
+
[[env.production.routes]]
|
|
37
|
+
pattern = "backend.edgebase.tandamo.com"
|
|
38
|
+
custom_domain = true
|