create-authhero 0.15.0 → 0.17.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/README.md +105 -26
- package/dist/cloudflare-multitenant/README.md +120 -7
- package/dist/cloudflare-multitenant/src/app.ts +8 -6
- package/dist/cloudflare-multitenant/wrangler.toml +33 -10
- package/dist/cloudflare-simple/README.md +131 -27
- package/dist/cloudflare-simple/wrangler.toml +33 -10
- package/dist/create-authhero.js +247 -151
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,9 +18,10 @@ The CLI will guide you through:
|
|
|
18
18
|
|
|
19
19
|
1. **Project name** - Name of your project directory
|
|
20
20
|
2. **Setup type** - Choose between Local, Cloudflare Simple, or Cloudflare Multi-Tenant
|
|
21
|
-
3. **
|
|
22
|
-
4. **
|
|
23
|
-
5. **
|
|
21
|
+
3. **GitHub CI** - (Cloudflare only) Optionally include GitHub Actions workflows with semantic versioning
|
|
22
|
+
4. **Admin credentials** - Email and password for the initial admin user
|
|
23
|
+
5. **Install dependencies** - Optionally install packages with your preferred package manager
|
|
24
|
+
6. **Start server** - Optionally run migrations, seed the database, and start the dev server
|
|
24
25
|
|
|
25
26
|
## Setup Types
|
|
26
27
|
|
|
@@ -68,27 +69,38 @@ npm run dev
|
|
|
68
69
|
```
|
|
69
70
|
my-auth-project/
|
|
70
71
|
├── src/
|
|
71
|
-
│ ├── index.ts
|
|
72
|
-
│ ├── app.ts
|
|
73
|
-
│ └── types.ts
|
|
74
|
-
├── wrangler.toml
|
|
75
|
-
├──
|
|
72
|
+
│ ├── index.ts # Worker entry point
|
|
73
|
+
│ ├── app.ts # AuthHero configuration
|
|
74
|
+
│ └── types.ts # TypeScript types
|
|
75
|
+
├── wrangler.toml # Base Cloudflare config (safe for git)
|
|
76
|
+
├── wrangler.local.toml # Your private IDs (gitignored)
|
|
77
|
+
├── .dev.vars # Local secrets (gitignored)
|
|
78
|
+
├── .dev.vars.example # Template for .dev.vars
|
|
79
|
+
├── seed.sql # Database seeding (generated)
|
|
76
80
|
├── package.json
|
|
77
81
|
└── tsconfig.json
|
|
78
82
|
```
|
|
79
83
|
|
|
80
|
-
**Quick Start:**
|
|
84
|
+
**Quick Start (Local Development):**
|
|
81
85
|
|
|
82
86
|
```sh
|
|
83
87
|
cd my-auth-project
|
|
84
88
|
npm install
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
npm run db:migrate
|
|
88
|
-
npm run seed
|
|
89
|
+
npm run migrate
|
|
90
|
+
ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD=yourpassword npm run seed
|
|
89
91
|
npm run dev
|
|
90
92
|
```
|
|
91
93
|
|
|
94
|
+
**Remote Development (Your Cloudflare Account):**
|
|
95
|
+
|
|
96
|
+
```sh
|
|
97
|
+
# Create a D1 database
|
|
98
|
+
npx wrangler d1 create authhero-db
|
|
99
|
+
# Copy the database_id to wrangler.local.toml
|
|
100
|
+
npm run db:migrate:remote
|
|
101
|
+
npm run dev:remote
|
|
102
|
+
```
|
|
103
|
+
|
|
92
104
|
### 3. Cloudflare Multi-Tenant (Production)
|
|
93
105
|
|
|
94
106
|
**Best for:** SaaS platforms, agencies, enterprise deployments
|
|
@@ -103,13 +115,15 @@ npm run dev
|
|
|
103
115
|
```
|
|
104
116
|
my-auth-project/
|
|
105
117
|
├── src/
|
|
106
|
-
│ ├── index.ts
|
|
107
|
-
│ ├── app.ts
|
|
108
|
-
│ ├── types.ts
|
|
118
|
+
│ ├── index.ts # Worker entry point
|
|
119
|
+
│ ├── app.ts # AuthHero configuration
|
|
120
|
+
│ ├── types.ts # TypeScript types
|
|
109
121
|
│ └── database-factory.ts # Multi-tenant DB factory
|
|
110
|
-
├── wrangler.toml
|
|
111
|
-
├── .
|
|
112
|
-
├──
|
|
122
|
+
├── wrangler.toml # Base Cloudflare config (safe for git)
|
|
123
|
+
├── wrangler.local.toml # Your private IDs (gitignored)
|
|
124
|
+
├── .dev.vars # Local secrets (gitignored)
|
|
125
|
+
├── .dev.vars.example # Environment variables template
|
|
126
|
+
├── seed.sql # Database seeding (generated)
|
|
113
127
|
├── package.json
|
|
114
128
|
└── tsconfig.json
|
|
115
129
|
```
|
|
@@ -138,20 +152,51 @@ my-auth-project/
|
|
|
138
152
|
└─────────────┘ └─────────────┘ └─────────────┘
|
|
139
153
|
```
|
|
140
154
|
|
|
141
|
-
**Quick Start:**
|
|
155
|
+
**Quick Start (Local Development):**
|
|
142
156
|
|
|
143
157
|
```sh
|
|
144
158
|
cd my-auth-project
|
|
145
159
|
npm install
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
cp .dev.vars.example .dev.vars
|
|
149
|
-
# Add your Cloudflare credentials to .dev.vars
|
|
150
|
-
npm run db:migrate
|
|
151
|
-
npm run seed
|
|
160
|
+
npm run migrate
|
|
161
|
+
ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD=yourpassword npm run seed
|
|
152
162
|
npm run dev
|
|
153
163
|
```
|
|
154
164
|
|
|
165
|
+
**Remote Development (Your Cloudflare Account):**
|
|
166
|
+
|
|
167
|
+
```sh
|
|
168
|
+
# Create a D1 database
|
|
169
|
+
npx wrangler d1 create authhero-db
|
|
170
|
+
# Copy the database_id to wrangler.local.toml
|
|
171
|
+
# Optionally add CLOUDFLARE_ACCOUNT_ID to .dev.vars
|
|
172
|
+
npm run db:migrate:remote
|
|
173
|
+
npm run dev:remote
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Security & Privacy
|
|
177
|
+
|
|
178
|
+
Cloudflare projects are designed to be **open-source friendly**:
|
|
179
|
+
|
|
180
|
+
| File | Purpose | In Git? |
|
|
181
|
+
| --------------------- | ---------------------------------- | ------- |
|
|
182
|
+
| `wrangler.toml` | Base config (safe for public repo) | ✅ Yes |
|
|
183
|
+
| `wrangler.local.toml` | Your private IDs (database_id) | ❌ No |
|
|
184
|
+
| `.dev.vars` | Local secrets (API tokens, etc.) | ❌ No |
|
|
185
|
+
| `.dev.vars.example` | Template for .dev.vars | ✅ Yes |
|
|
186
|
+
|
|
187
|
+
The CLI automatically creates `wrangler.local.toml` and `.dev.vars` from the templates when you create a project. Simply update them with your Cloudflare IDs.
|
|
188
|
+
|
|
189
|
+
### GitHub Actions / CI Deployment
|
|
190
|
+
|
|
191
|
+
For automated deployments, set these GitHub Secrets:
|
|
192
|
+
|
|
193
|
+
| Secret | Description |
|
|
194
|
+
| ----------------------- | -------------------------------------------------------- |
|
|
195
|
+
| `CLOUDFLARE_API_TOKEN` | API token with Workers permissions |
|
|
196
|
+
| `CLOUDFLARE_ACCOUNT_ID` | Your Cloudflare account ID (optional, for some features) |
|
|
197
|
+
|
|
198
|
+
The wrangler GitHub Action will automatically use these secrets. No need to store IDs in your repo!
|
|
199
|
+
|
|
155
200
|
## Example
|
|
156
201
|
|
|
157
202
|
```sh
|
|
@@ -175,6 +220,40 @@ You'll be prompted to:
|
|
|
175
220
|
| Complexity | Low | Medium | High |
|
|
176
221
|
| Best For | Development | Simple Production | Enterprise/SaaS |
|
|
177
222
|
|
|
223
|
+
## GitHub CI with Semantic Versioning
|
|
224
|
+
|
|
225
|
+
For Cloudflare setups, you can optionally include GitHub Actions workflows that provide:
|
|
226
|
+
|
|
227
|
+
- **Unit Tests**: Runs on all pushes to any branch
|
|
228
|
+
- **Deploy to Dev**: Automatically deploys to dev environment on push to `main`, with semantic-release for version management
|
|
229
|
+
- **Deploy to Production**: Deploys to production when a GitHub release is published
|
|
230
|
+
|
|
231
|
+
### CI/CD Flow
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
All PRs/Pushes → Unit Tests (type-check + tests)
|
|
235
|
+
Push to main → Semantic Release + Deploy to Dev
|
|
236
|
+
GitHub Release → Deploy to Production
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Required GitHub Secrets
|
|
240
|
+
|
|
241
|
+
| Secret | Description |
|
|
242
|
+
| --------------------------- | ---------------------------------------- |
|
|
243
|
+
| `CLOUDFLARE_API_TOKEN` | API token for dev deployments |
|
|
244
|
+
| `CLOUDFLARE_ACCOUNT_ID` | Account ID (optional, for some features) |
|
|
245
|
+
| `PROD_CLOUDFLARE_API_TOKEN` | API token for production deployments |
|
|
246
|
+
|
|
247
|
+
> **Note:** You do NOT need to store `database_id` or `zone_id` in your repo or secrets. Wrangler resolves these automatically when deploying with the correct API token.
|
|
248
|
+
|
|
249
|
+
### Commit Message Conventions
|
|
250
|
+
|
|
251
|
+
Use conventional commits for automatic versioning:
|
|
252
|
+
|
|
253
|
+
- `fix:` → Patch release (1.0.0 → 1.0.1)
|
|
254
|
+
- `feat:` → Minor release (1.0.0 → 1.1.0)
|
|
255
|
+
- `BREAKING CHANGE:` → Major release (1.0.0 → 2.0.0)
|
|
256
|
+
|
|
178
257
|
## Documentation
|
|
179
258
|
|
|
180
259
|
For more information, visit [https://authhero.net/docs](https://authhero.net/docs)
|
|
@@ -28,15 +28,34 @@ A production-grade multi-tenant AuthHero authentication server using Cloudflare
|
|
|
28
28
|
└─────────────┘
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
## Security & Privacy
|
|
32
|
+
|
|
33
|
+
This project is designed to be **open-source friendly**. Sensitive Cloudflare IDs are kept out of version control:
|
|
34
|
+
|
|
35
|
+
| File | Purpose | In Git? |
|
|
36
|
+
| --------------------- | ---------------------------------- | ------- |
|
|
37
|
+
| `wrangler.toml` | Base config (safe for public repo) | ✅ Yes |
|
|
38
|
+
| `wrangler.local.toml` | Your private IDs (database_id) | ❌ No |
|
|
39
|
+
| `.dev.vars` | Local secrets (API tokens, etc.) | ❌ No |
|
|
40
|
+
| `.dev.vars.example` | Template for .dev.vars | ✅ Yes |
|
|
41
|
+
|
|
42
|
+
**For GitHub Actions / CI:**
|
|
43
|
+
|
|
44
|
+
- Set `CLOUDFLARE_API_TOKEN` as a GitHub Secret
|
|
45
|
+
- Set `CLOUDFLARE_ACCOUNT_ID` as a GitHub Secret (if needed)
|
|
46
|
+
- The wrangler action will use these automatically
|
|
47
|
+
|
|
31
48
|
## Getting Started
|
|
32
49
|
|
|
50
|
+
### Local Development (Quick Start)
|
|
51
|
+
|
|
33
52
|
1. Install dependencies:
|
|
34
53
|
|
|
35
54
|
```bash
|
|
36
55
|
npm install
|
|
37
56
|
```
|
|
38
57
|
|
|
39
|
-
2. Run local
|
|
58
|
+
2. Run database migrations (uses local SQLite-backed D1):
|
|
40
59
|
|
|
41
60
|
```bash
|
|
42
61
|
npm run migrate
|
|
@@ -55,29 +74,46 @@ A production-grade multi-tenant AuthHero authentication server using Cloudflare
|
|
|
55
74
|
|
|
56
75
|
The server will be available at `https://localhost:3000`.
|
|
57
76
|
|
|
58
|
-
|
|
77
|
+
### Remote Development (Your Cloudflare Account)
|
|
59
78
|
|
|
60
79
|
1. Create a D1 database:
|
|
61
80
|
|
|
62
81
|
```bash
|
|
63
|
-
wrangler d1 create authhero-db
|
|
82
|
+
npx wrangler d1 create authhero-db
|
|
64
83
|
```
|
|
65
84
|
|
|
66
|
-
2.
|
|
85
|
+
2. Copy the `database_id` from the output and update `wrangler.local.toml`:
|
|
86
|
+
|
|
87
|
+
```toml
|
|
88
|
+
[[d1_databases]]
|
|
89
|
+
binding = "AUTH_DB"
|
|
90
|
+
database_name = "authhero-db"
|
|
91
|
+
database_id = "paste-your-database-id-here"
|
|
92
|
+
migrations_dir = "node_modules/@authhero/drizzle/drizzle"
|
|
93
|
+
```
|
|
67
94
|
|
|
68
|
-
3. Run migrations
|
|
95
|
+
3. Run remote migrations:
|
|
69
96
|
|
|
70
97
|
```bash
|
|
71
98
|
npm run db:migrate:remote
|
|
72
99
|
```
|
|
73
100
|
|
|
74
|
-
4. Seed the
|
|
101
|
+
4. Seed the remote database:
|
|
75
102
|
|
|
76
103
|
```bash
|
|
77
104
|
ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD=yourpassword npm run seed:remote
|
|
78
105
|
```
|
|
79
106
|
|
|
80
|
-
5.
|
|
107
|
+
5. Start with remote D1:
|
|
108
|
+
```bash
|
|
109
|
+
npm run dev:remote
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Production Deployment
|
|
113
|
+
|
|
114
|
+
1. Ensure `wrangler.local.toml` has your production `database_id`.
|
|
115
|
+
|
|
116
|
+
2. Deploy:
|
|
81
117
|
```bash
|
|
82
118
|
npm run deploy
|
|
83
119
|
```
|
|
@@ -154,6 +190,83 @@ curl https://your-worker.workers.dev/management/tenants \
|
|
|
154
190
|
|
|
155
191
|
For more information, visit [https://authhero.net/docs](https://authhero.net/docs).
|
|
156
192
|
|
|
193
|
+
## CI/CD with GitHub Actions
|
|
194
|
+
|
|
195
|
+
If you selected GitHub CI during setup, your project includes automated workflows for continuous integration and deployment.
|
|
196
|
+
|
|
197
|
+
### Workflow Overview
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
201
|
+
│ GitHub Actions │
|
|
202
|
+
├─────────────────────────────────────────────────────────────────────────┤
|
|
203
|
+
│ │
|
|
204
|
+
│ ┌──────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
|
|
205
|
+
│ │ Any Push │ │ Push to main │ │ GitHub Release │ │
|
|
206
|
+
│ │ │ │ │ │ (released) │ │
|
|
207
|
+
│ └──────┬───────┘ └────────┬─────────┘ └────────┬─────────┘ │
|
|
208
|
+
│ │ │ │ │
|
|
209
|
+
│ ▼ ▼ ▼ │
|
|
210
|
+
│ ┌──────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
|
|
211
|
+
│ │ Unit Tests │ │ Semantic Release │ │ Deploy to │ │
|
|
212
|
+
│ │ Type Check │ │ + Deploy Dev │ │ Production │ │
|
|
213
|
+
│ └──────────────┘ └──────────────────┘ └──────────────────┘ │
|
|
214
|
+
│ │
|
|
215
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Workflows
|
|
219
|
+
|
|
220
|
+
1. **Unit Tests** (`.github/workflows/unit-tests.yml`)
|
|
221
|
+
- **Trigger**: All pushes to any branch
|
|
222
|
+
- **Actions**: Runs type checking and tests
|
|
223
|
+
|
|
224
|
+
2. **Deploy to Dev** (`.github/workflows/deploy-dev.yml`)
|
|
225
|
+
- **Trigger**: Push to `main` branch
|
|
226
|
+
- **Actions**:
|
|
227
|
+
- Runs semantic-release to create version tags
|
|
228
|
+
- Deploys to Cloudflare Workers (dev environment)
|
|
229
|
+
|
|
230
|
+
3. **Deploy to Production** (`.github/workflows/release.yml`)
|
|
231
|
+
- **Trigger**: GitHub Release (when "released")
|
|
232
|
+
- **Actions**: Deploys to Cloudflare Workers (production environment)
|
|
233
|
+
|
|
234
|
+
### Required Secrets
|
|
235
|
+
|
|
236
|
+
Configure these secrets in your GitHub repository settings:
|
|
237
|
+
|
|
238
|
+
| Secret | Description |
|
|
239
|
+
| --------------------------- | ----------------------------------------------- |
|
|
240
|
+
| `CLOUDFLARE_API_TOKEN` | Cloudflare API token for dev deployments |
|
|
241
|
+
| `PROD_CLOUDFLARE_API_TOKEN` | Cloudflare API token for production deployments |
|
|
242
|
+
|
|
243
|
+
### Semantic Versioning
|
|
244
|
+
|
|
245
|
+
Commits to `main` are analyzed to determine version bumps:
|
|
246
|
+
|
|
247
|
+
- `fix:` - Patch release (1.0.0 → 1.0.1)
|
|
248
|
+
- `feat:` - Minor release (1.0.0 → 1.1.0)
|
|
249
|
+
- `BREAKING CHANGE:` - Major release (1.0.0 → 2.0.0)
|
|
250
|
+
|
|
251
|
+
### Production Deployment
|
|
252
|
+
|
|
253
|
+
To deploy to production:
|
|
254
|
+
|
|
255
|
+
1. Go to GitHub → Releases → "Draft a new release"
|
|
256
|
+
2. Create a new tag (e.g., `v1.0.0`)
|
|
257
|
+
3. Click "Publish release"
|
|
258
|
+
4. The `release.yml` workflow will deploy to production
|
|
259
|
+
|
|
260
|
+
### Wrangler Configuration
|
|
261
|
+
|
|
262
|
+
For production deployments, add an environment to `wrangler.toml`:
|
|
263
|
+
|
|
264
|
+
```toml
|
|
265
|
+
[env.production]
|
|
266
|
+
name = "your-worker-production"
|
|
267
|
+
# Add production-specific settings here
|
|
268
|
+
```
|
|
269
|
+
|
|
157
270
|
## Optional Features
|
|
158
271
|
|
|
159
272
|
### Analytics Engine (Centralized Logging)
|
|
@@ -7,19 +7,21 @@ import {
|
|
|
7
7
|
DataAdapters,
|
|
8
8
|
} from "@authhero/multi-tenancy";
|
|
9
9
|
|
|
10
|
-
//
|
|
11
|
-
const
|
|
10
|
+
// Control plane tenant ID - the tenant that manages all other tenants
|
|
11
|
+
const CONTROL_PLANE_TENANT_ID = "control_plane";
|
|
12
12
|
|
|
13
13
|
export default function createApp(
|
|
14
|
-
config: Omit<MultiTenantAuthHeroConfig, "
|
|
14
|
+
config: Omit<MultiTenantAuthHeroConfig, "controlPlaneTenantId"> & {
|
|
15
15
|
dataAdapter: DataAdapters;
|
|
16
16
|
},
|
|
17
17
|
) {
|
|
18
18
|
const { app } = init({
|
|
19
19
|
...config,
|
|
20
|
-
|
|
21
|
-
// Sync resource servers from
|
|
20
|
+
controlPlaneTenantId: CONTROL_PLANE_TENANT_ID,
|
|
21
|
+
// Sync resource servers from control plane tenant to all child tenants
|
|
22
22
|
syncResourceServers: true,
|
|
23
|
+
// Sync roles from control plane tenant to all child tenants
|
|
24
|
+
syncRoles: true,
|
|
23
25
|
});
|
|
24
26
|
|
|
25
27
|
app
|
|
@@ -36,7 +38,7 @@ export default function createApp(
|
|
|
36
38
|
version: "1.0.0",
|
|
37
39
|
status: "running",
|
|
38
40
|
docs: "/docs",
|
|
39
|
-
|
|
41
|
+
controlPlaneTenant: CONTROL_PLANE_TENANT_ID,
|
|
40
42
|
});
|
|
41
43
|
})
|
|
42
44
|
.get("/docs", swaggerUI({ url: "/api/v2/spec" }));
|
|
@@ -1,23 +1,46 @@
|
|
|
1
|
+
# ════════════════════════════════════════════════════════════════════════════
|
|
2
|
+
# AuthHero Multi-Tenant Cloudflare Worker Configuration
|
|
3
|
+
# ════════════════════════════════════════════════════════════════════════════
|
|
4
|
+
# This file is safe for version control. Sensitive IDs should go in:
|
|
5
|
+
# - wrangler.local.toml (local development - gitignored)
|
|
6
|
+
# - GitHub Secrets / Cloudflare Dashboard (production)
|
|
7
|
+
#
|
|
8
|
+
# For local development with your own Cloudflare resources:
|
|
9
|
+
# cp wrangler.toml wrangler.local.toml
|
|
10
|
+
# # Add your database_id to wrangler.local.toml
|
|
11
|
+
# npm run dev # Uses wrangler.local.toml automatically
|
|
12
|
+
# ════════════════════════════════════════════════════════════════════════════
|
|
13
|
+
|
|
1
14
|
name = "authhero-multitenant"
|
|
2
15
|
main = "src/index.ts"
|
|
3
16
|
compatibility_date = "2024-11-20"
|
|
4
17
|
|
|
18
|
+
# ════════════════════════════════════════════════════════════════════════════
|
|
5
19
|
# D1 Database binding
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
|
|
14
|
-
#
|
|
20
|
+
# ════════════════════════════════════════════════════════════════════════════
|
|
21
|
+
# This configuration uses a local D1 database by default.
|
|
22
|
+
# For remote development/production, copy to wrangler.local.toml and update.
|
|
23
|
+
#
|
|
24
|
+
# Setup:
|
|
25
|
+
# 1. Create database: npx wrangler d1 create authhero-db
|
|
26
|
+
# 2. Copy to local config: cp wrangler.toml wrangler.local.toml
|
|
27
|
+
# 3. Update database_id in wrangler.local.toml with the ID from step 1
|
|
28
|
+
#
|
|
15
29
|
[[d1_databases]]
|
|
16
30
|
binding = "AUTH_DB"
|
|
17
31
|
database_name = "authhero-db"
|
|
18
|
-
database_id = "local"
|
|
32
|
+
database_id = "local" # Use "local" for local dev, or your actual ID in wrangler.local.toml
|
|
19
33
|
migrations_dir = "node_modules/@authhero/drizzle/drizzle"
|
|
20
34
|
|
|
35
|
+
# ════════════════════════════════════════════════════════════════════════════
|
|
36
|
+
# OPTIONAL: Custom Domain
|
|
37
|
+
# ════════════════════════════════════════════════════════════════════════════
|
|
38
|
+
# To route your worker to a custom domain, use zone_name (not zone_id):
|
|
39
|
+
#
|
|
40
|
+
# [[routes]]
|
|
41
|
+
# pattern = "auth.yourdomain.com/*"
|
|
42
|
+
# zone_name = "yourdomain.com"
|
|
43
|
+
|
|
21
44
|
# ════════════════════════════════════════════════════════════════════════════
|
|
22
45
|
# OPTIONAL: Analytics Engine for centralized logging
|
|
23
46
|
# ════════════════════════════════════════════════════════════════════════════
|
|
@@ -7,90 +7,117 @@ A single-tenant AuthHero authentication server using Cloudflare Workers and D1.
|
|
|
7
7
|
- [Cloudflare Account](https://dash.cloudflare.com/sign-up)
|
|
8
8
|
- [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/install-and-update/)
|
|
9
9
|
|
|
10
|
+
## Security & Privacy
|
|
11
|
+
|
|
12
|
+
This project is designed to be **open-source friendly**. Sensitive Cloudflare IDs are kept out of version control:
|
|
13
|
+
|
|
14
|
+
| File | Purpose | In Git? |
|
|
15
|
+
| --------------------- | ---------------------------------- | ------- |
|
|
16
|
+
| `wrangler.toml` | Base config (safe for public repo) | ✅ Yes |
|
|
17
|
+
| `wrangler.local.toml` | Your private IDs (database_id) | ❌ No |
|
|
18
|
+
| `.dev.vars` | Local secrets (API tokens, etc.) | ❌ No |
|
|
19
|
+
| `.dev.vars.example` | Template for .dev.vars | ✅ Yes |
|
|
20
|
+
|
|
21
|
+
**For GitHub Actions / CI:**
|
|
22
|
+
|
|
23
|
+
- Set `CLOUDFLARE_API_TOKEN` as a GitHub Secret
|
|
24
|
+
- Set `CLOUDFLARE_ACCOUNT_ID` as a GitHub Secret (if needed)
|
|
25
|
+
- The wrangler action will use these automatically
|
|
26
|
+
|
|
10
27
|
## Getting Started
|
|
11
28
|
|
|
29
|
+
### Local Development (Quick Start)
|
|
30
|
+
|
|
12
31
|
1. Install dependencies:
|
|
13
32
|
|
|
14
33
|
```bash
|
|
15
34
|
npm install
|
|
16
35
|
```
|
|
17
36
|
|
|
18
|
-
2.
|
|
37
|
+
2. Run database migrations (uses local SQLite-backed D1):
|
|
19
38
|
|
|
20
39
|
```bash
|
|
21
|
-
|
|
40
|
+
npm run migrate
|
|
22
41
|
```
|
|
23
42
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
3. Run database migrations:
|
|
27
|
-
|
|
28
|
-
**For local development:**
|
|
43
|
+
3. Seed the database with an admin user:
|
|
29
44
|
|
|
30
45
|
```bash
|
|
31
|
-
npm run
|
|
46
|
+
ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD=yourpassword npm run seed
|
|
32
47
|
```
|
|
33
48
|
|
|
34
|
-
|
|
49
|
+
4. Start the development server:
|
|
35
50
|
|
|
36
51
|
```bash
|
|
37
|
-
npm run
|
|
52
|
+
npm run dev
|
|
38
53
|
```
|
|
39
54
|
|
|
40
|
-
|
|
55
|
+
The server will be available at `https://localhost:3000`.
|
|
56
|
+
|
|
57
|
+
### Remote Development (Your Cloudflare Account)
|
|
41
58
|
|
|
42
|
-
|
|
59
|
+
1. Create a D1 database:
|
|
43
60
|
|
|
44
61
|
```bash
|
|
45
|
-
|
|
62
|
+
npx wrangler d1 create authhero-db
|
|
46
63
|
```
|
|
47
64
|
|
|
48
|
-
|
|
65
|
+
2. Copy the `database_id` from the output and update `wrangler.local.toml`:
|
|
49
66
|
|
|
50
|
-
```
|
|
51
|
-
|
|
67
|
+
```toml
|
|
68
|
+
[[d1_databases]]
|
|
69
|
+
binding = "AUTH_DB"
|
|
70
|
+
database_name = "authhero-db"
|
|
71
|
+
database_id = "paste-your-database-id-here"
|
|
72
|
+
migrations_dir = "node_modules/@authhero/drizzle/drizzle"
|
|
52
73
|
```
|
|
53
74
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
**For local mode (local D1 database):**
|
|
75
|
+
3. Run remote migrations:
|
|
57
76
|
|
|
58
77
|
```bash
|
|
59
|
-
npm run
|
|
78
|
+
npm run db:migrate:remote
|
|
60
79
|
```
|
|
61
80
|
|
|
62
|
-
|
|
81
|
+
4. Seed the remote database:
|
|
63
82
|
|
|
83
|
+
```bash
|
|
84
|
+
ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD=yourpassword npm run seed:remote
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
5. Start with remote D1:
|
|
64
88
|
```bash
|
|
65
89
|
npm run dev:remote
|
|
66
90
|
```
|
|
67
91
|
|
|
68
92
|
## Available Scripts
|
|
69
93
|
|
|
70
|
-
- `npm run dev
|
|
71
|
-
- `npm run dev:remote` - Start
|
|
94
|
+
- `npm run dev` - Start development server with local D1 database
|
|
95
|
+
- `npm run dev:remote` - Start with remote D1 database
|
|
72
96
|
- `npm run deploy` - Deploy to Cloudflare Workers
|
|
73
97
|
- `npm run migrate` - Run migrations on local database
|
|
74
98
|
- `npm run db:migrate:local` - Run migrations on local database (alias)
|
|
75
99
|
- `npm run db:migrate:remote` - Run migrations on remote database
|
|
76
|
-
- `npm run seed
|
|
100
|
+
- `npm run seed` - Seed local database with admin user
|
|
77
101
|
- `npm run seed:remote` - Seed remote database with admin user
|
|
102
|
+
- `npm run setup` - Create wrangler.local.toml and .dev.vars from templates
|
|
78
103
|
|
|
79
104
|
## Deployment
|
|
80
105
|
|
|
81
|
-
1.
|
|
106
|
+
1. Ensure `wrangler.local.toml` has your production `database_id`.
|
|
107
|
+
|
|
108
|
+
2. Deploy to Cloudflare:
|
|
82
109
|
|
|
83
110
|
```bash
|
|
84
111
|
npm run deploy
|
|
85
112
|
```
|
|
86
113
|
|
|
87
|
-
|
|
114
|
+
3. Run production migrations:
|
|
88
115
|
|
|
89
116
|
```bash
|
|
90
117
|
npm run db:migrate:remote
|
|
91
118
|
```
|
|
92
119
|
|
|
93
|
-
|
|
120
|
+
4. Seed the production database:
|
|
94
121
|
```bash
|
|
95
122
|
ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD=yourpassword npm run seed:remote
|
|
96
123
|
```
|
|
@@ -136,6 +163,83 @@ routes = [
|
|
|
136
163
|
|
|
137
164
|
For more information, visit [https://authhero.net/docs](https://authhero.net/docs).
|
|
138
165
|
|
|
166
|
+
## CI/CD with GitHub Actions
|
|
167
|
+
|
|
168
|
+
If you selected GitHub CI during setup, your project includes automated workflows for continuous integration and deployment.
|
|
169
|
+
|
|
170
|
+
### Workflow Overview
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
174
|
+
│ GitHub Actions │
|
|
175
|
+
├─────────────────────────────────────────────────────────────────────────┤
|
|
176
|
+
│ │
|
|
177
|
+
│ ┌──────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
|
|
178
|
+
│ │ Any Push │ │ Push to main │ │ GitHub Release │ │
|
|
179
|
+
│ │ │ │ │ │ (released) │ │
|
|
180
|
+
│ └──────┬───────┘ └────────┬─────────┘ └────────┬─────────┘ │
|
|
181
|
+
│ │ │ │ │
|
|
182
|
+
│ ▼ ▼ ▼ │
|
|
183
|
+
│ ┌──────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
|
|
184
|
+
│ │ Unit Tests │ │ Semantic Release │ │ Deploy to │ │
|
|
185
|
+
│ │ Type Check │ │ + Deploy Dev │ │ Production │ │
|
|
186
|
+
│ └──────────────┘ └──────────────────┘ └──────────────────┘ │
|
|
187
|
+
│ │
|
|
188
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Workflows
|
|
192
|
+
|
|
193
|
+
1. **Unit Tests** (`.github/workflows/unit-tests.yml`)
|
|
194
|
+
- **Trigger**: All pushes to any branch
|
|
195
|
+
- **Actions**: Runs type checking and tests
|
|
196
|
+
|
|
197
|
+
2. **Deploy to Dev** (`.github/workflows/deploy-dev.yml`)
|
|
198
|
+
- **Trigger**: Push to `main` branch
|
|
199
|
+
- **Actions**:
|
|
200
|
+
- Runs semantic-release to create version tags
|
|
201
|
+
- Deploys to Cloudflare Workers (dev environment)
|
|
202
|
+
|
|
203
|
+
3. **Deploy to Production** (`.github/workflows/release.yml`)
|
|
204
|
+
- **Trigger**: GitHub Release (when "released")
|
|
205
|
+
- **Actions**: Deploys to Cloudflare Workers (production environment)
|
|
206
|
+
|
|
207
|
+
### Required Secrets
|
|
208
|
+
|
|
209
|
+
Configure these secrets in your GitHub repository settings:
|
|
210
|
+
|
|
211
|
+
| Secret | Description |
|
|
212
|
+
| --------------------------- | ----------------------------------------------- |
|
|
213
|
+
| `CLOUDFLARE_API_TOKEN` | Cloudflare API token for dev deployments |
|
|
214
|
+
| `PROD_CLOUDFLARE_API_TOKEN` | Cloudflare API token for production deployments |
|
|
215
|
+
|
|
216
|
+
### Semantic Versioning
|
|
217
|
+
|
|
218
|
+
Commits to `main` are analyzed to determine version bumps:
|
|
219
|
+
|
|
220
|
+
- `fix:` - Patch release (1.0.0 → 1.0.1)
|
|
221
|
+
- `feat:` - Minor release (1.0.0 → 1.1.0)
|
|
222
|
+
- `BREAKING CHANGE:` - Major release (1.0.0 → 2.0.0)
|
|
223
|
+
|
|
224
|
+
### Production Deployment
|
|
225
|
+
|
|
226
|
+
To deploy to production:
|
|
227
|
+
|
|
228
|
+
1. Go to GitHub → Releases → "Draft a new release"
|
|
229
|
+
2. Create a new tag (e.g., `v1.0.0`)
|
|
230
|
+
3. Click "Publish release"
|
|
231
|
+
4. The `release.yml` workflow will deploy to production
|
|
232
|
+
|
|
233
|
+
### Wrangler Configuration
|
|
234
|
+
|
|
235
|
+
For production deployments, add an environment to `wrangler.toml`:
|
|
236
|
+
|
|
237
|
+
```toml
|
|
238
|
+
[env.production]
|
|
239
|
+
name = "your-worker-production"
|
|
240
|
+
# Add production-specific settings here
|
|
241
|
+
```
|
|
242
|
+
|
|
139
243
|
## Optional Features
|
|
140
244
|
|
|
141
245
|
### Analytics Engine (Centralized Logging)
|
|
@@ -1,23 +1,46 @@
|
|
|
1
|
+
# ════════════════════════════════════════════════════════════════════════════
|
|
2
|
+
# AuthHero Cloudflare Worker Configuration
|
|
3
|
+
# ════════════════════════════════════════════════════════════════════════════
|
|
4
|
+
# This file is safe for version control. Sensitive IDs should go in:
|
|
5
|
+
# - wrangler.local.toml (local development - gitignored)
|
|
6
|
+
# - GitHub Secrets / Cloudflare Dashboard (production)
|
|
7
|
+
#
|
|
8
|
+
# For local development with your own Cloudflare resources:
|
|
9
|
+
# cp wrangler.toml wrangler.local.toml
|
|
10
|
+
# # Add your database_id to wrangler.local.toml
|
|
11
|
+
# npm run dev # Uses wrangler.local.toml automatically
|
|
12
|
+
# ════════════════════════════════════════════════════════════════════════════
|
|
13
|
+
|
|
1
14
|
name = "authhero-server"
|
|
2
15
|
main = "src/index.ts"
|
|
3
16
|
compatibility_date = "2024-11-20"
|
|
4
17
|
|
|
18
|
+
# ════════════════════════════════════════════════════════════════════════════
|
|
5
19
|
# D1 Database binding
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
|
|
14
|
-
#
|
|
20
|
+
# ════════════════════════════════════════════════════════════════════════════
|
|
21
|
+
# This configuration uses a local D1 database by default.
|
|
22
|
+
# For remote development/production, copy to wrangler.local.toml and update.
|
|
23
|
+
#
|
|
24
|
+
# Setup:
|
|
25
|
+
# 1. Create database: npx wrangler d1 create authhero-db
|
|
26
|
+
# 2. Copy to local config: cp wrangler.toml wrangler.local.toml
|
|
27
|
+
# 3. Update database_id in wrangler.local.toml with the ID from step 1
|
|
28
|
+
#
|
|
15
29
|
[[d1_databases]]
|
|
16
30
|
binding = "AUTH_DB"
|
|
17
31
|
database_name = "authhero-db"
|
|
18
|
-
database_id = "local"
|
|
32
|
+
database_id = "local" # Use "local" for local dev, or your actual ID in wrangler.local.toml
|
|
19
33
|
migrations_dir = "node_modules/@authhero/drizzle/drizzle"
|
|
20
34
|
|
|
35
|
+
# ════════════════════════════════════════════════════════════════════════════
|
|
36
|
+
# OPTIONAL: Custom Domain
|
|
37
|
+
# ════════════════════════════════════════════════════════════════════════════
|
|
38
|
+
# To route your worker to a custom domain, use zone_name (not zone_id):
|
|
39
|
+
#
|
|
40
|
+
# [[routes]]
|
|
41
|
+
# pattern = "auth.yourdomain.com/*"
|
|
42
|
+
# zone_name = "yourdomain.com"
|
|
43
|
+
|
|
21
44
|
# ════════════════════════════════════════════════════════════════════════════
|
|
22
45
|
# OPTIONAL: Analytics Engine for centralized logging
|
|
23
46
|
# ════════════════════════════════════════════════════════════════════════════
|
package/dist/create-authhero.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { Command as
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import { spawn as
|
|
7
|
-
const
|
|
2
|
+
import { Command as j } from "commander";
|
|
3
|
+
import m from "inquirer";
|
|
4
|
+
import o from "fs";
|
|
5
|
+
import i from "path";
|
|
6
|
+
import { spawn as b } from "child_process";
|
|
7
|
+
const D = new j(), c = {
|
|
8
8
|
local: {
|
|
9
9
|
name: "Local (SQLite)",
|
|
10
10
|
description: "Local development setup with SQLite database - great for getting started",
|
|
11
11
|
templateDir: "local",
|
|
12
|
-
packageJson: (
|
|
13
|
-
name:
|
|
12
|
+
packageJson: (s) => ({
|
|
13
|
+
name: s,
|
|
14
14
|
version: "1.0.0",
|
|
15
15
|
type: "module",
|
|
16
16
|
scripts: {
|
|
@@ -42,21 +42,21 @@ const b = new x(), n = {
|
|
|
42
42
|
name: "Cloudflare Simple (Single Tenant)",
|
|
43
43
|
description: "Single-tenant Cloudflare Workers setup with D1 database",
|
|
44
44
|
templateDir: "cloudflare-simple",
|
|
45
|
-
packageJson: (
|
|
46
|
-
name:
|
|
45
|
+
packageJson: (s) => ({
|
|
46
|
+
name: s,
|
|
47
47
|
version: "1.0.0",
|
|
48
48
|
type: "module",
|
|
49
49
|
scripts: {
|
|
50
|
-
"dev:local": "wrangler dev --port 3000 --local-protocol https",
|
|
51
|
-
"dev:remote": "wrangler dev --port 3000 --local-protocol https --remote",
|
|
52
50
|
dev: "wrangler dev --port 3000 --local-protocol https",
|
|
53
|
-
|
|
51
|
+
"dev:remote": "wrangler dev --port 3000 --local-protocol https --remote --config wrangler.local.toml",
|
|
52
|
+
deploy: "wrangler deploy --config wrangler.local.toml",
|
|
54
53
|
"db:migrate:local": "wrangler d1 migrations apply AUTH_DB --local",
|
|
55
|
-
"db:migrate:remote": "wrangler d1 migrations apply AUTH_DB --remote",
|
|
54
|
+
"db:migrate:remote": "wrangler d1 migrations apply AUTH_DB --remote --config wrangler.local.toml",
|
|
56
55
|
migrate: "wrangler d1 migrations apply AUTH_DB --local",
|
|
57
56
|
"seed:local": "node seed-helper.js",
|
|
58
57
|
"seed:remote": "node seed-helper.js '' '' remote",
|
|
59
|
-
seed: "node seed-helper.js"
|
|
58
|
+
seed: "node seed-helper.js",
|
|
59
|
+
setup: "cp wrangler.toml wrangler.local.toml && cp .dev.vars.example .dev.vars && echo '✅ Created wrangler.local.toml and .dev.vars - update with your IDs'"
|
|
60
60
|
},
|
|
61
61
|
dependencies: {
|
|
62
62
|
"@authhero/drizzle": "latest",
|
|
@@ -82,21 +82,21 @@ const b = new x(), n = {
|
|
|
82
82
|
name: "Cloudflare Multi-Tenant (Production)",
|
|
83
83
|
description: "Production-grade multi-tenant setup with D1 database and tenant management",
|
|
84
84
|
templateDir: "cloudflare-multitenant",
|
|
85
|
-
packageJson: (
|
|
86
|
-
name:
|
|
85
|
+
packageJson: (s) => ({
|
|
86
|
+
name: s,
|
|
87
87
|
version: "1.0.0",
|
|
88
88
|
type: "module",
|
|
89
89
|
scripts: {
|
|
90
|
-
"dev:local": "wrangler dev --port 3000 --local-protocol https",
|
|
91
|
-
"dev:remote": "wrangler dev --port 3000 --local-protocol https --remote",
|
|
92
90
|
dev: "wrangler dev --port 3000 --local-protocol https",
|
|
93
|
-
|
|
91
|
+
"dev:remote": "wrangler dev --port 3000 --local-protocol https --remote --config wrangler.local.toml",
|
|
92
|
+
deploy: "wrangler deploy --config wrangler.local.toml",
|
|
94
93
|
"db:migrate:local": "wrangler d1 migrations apply AUTH_DB --local",
|
|
95
|
-
"db:migrate:remote": "wrangler d1 migrations apply AUTH_DB --remote",
|
|
94
|
+
"db:migrate:remote": "wrangler d1 migrations apply AUTH_DB --remote --config wrangler.local.toml",
|
|
96
95
|
migrate: "wrangler d1 migrations apply AUTH_DB --local",
|
|
97
96
|
"seed:local": "node seed-helper.js",
|
|
98
97
|
"seed:remote": "node seed-helper.js '' '' remote",
|
|
99
|
-
seed: "node seed-helper.js"
|
|
98
|
+
seed: "node seed-helper.js",
|
|
99
|
+
setup: "cp wrangler.toml wrangler.local.toml && cp .dev.vars.example .dev.vars && echo '✅ Created wrangler.local.toml and .dev.vars - update with your IDs'"
|
|
100
100
|
},
|
|
101
101
|
dependencies: {
|
|
102
102
|
"@authhero/drizzle": "latest",
|
|
@@ -119,13 +119,13 @@ const b = new x(), n = {
|
|
|
119
119
|
seedFile: "seed.ts"
|
|
120
120
|
}
|
|
121
121
|
};
|
|
122
|
-
function A(
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
|
|
122
|
+
function A(s, e) {
|
|
123
|
+
o.readdirSync(s).forEach((n) => {
|
|
124
|
+
const a = i.join(s, n), t = i.join(e, n);
|
|
125
|
+
o.lstatSync(a).isDirectory() ? (o.mkdirSync(t, { recursive: !0 }), A(a, t)) : o.copyFileSync(a, t);
|
|
126
126
|
});
|
|
127
127
|
}
|
|
128
|
-
function
|
|
128
|
+
function I() {
|
|
129
129
|
return `import { SqliteDialect, Kysely } from "kysely";
|
|
130
130
|
import Database from "better-sqlite3";
|
|
131
131
|
import createAdapters from "@authhero/kysely-adapter";
|
|
@@ -159,106 +159,236 @@ async function main() {
|
|
|
159
159
|
main().catch(console.error);
|
|
160
160
|
`;
|
|
161
161
|
}
|
|
162
|
-
function
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
162
|
+
function x(s) {
|
|
163
|
+
const e = i.join(s, ".github", "workflows");
|
|
164
|
+
o.mkdirSync(e, { recursive: !0 });
|
|
165
|
+
const r = `name: Unit tests
|
|
166
|
+
|
|
167
|
+
on: push
|
|
168
|
+
|
|
169
|
+
jobs:
|
|
170
|
+
test:
|
|
171
|
+
runs-on: ubuntu-latest
|
|
172
|
+
steps:
|
|
173
|
+
- uses: actions/checkout@v4
|
|
174
|
+
|
|
175
|
+
- name: Setup Node.js
|
|
176
|
+
uses: actions/setup-node@v4
|
|
177
|
+
with:
|
|
178
|
+
node-version: "22"
|
|
179
|
+
cache: "npm"
|
|
180
|
+
|
|
181
|
+
- name: Install dependencies
|
|
182
|
+
run: npm ci
|
|
183
|
+
|
|
184
|
+
- run: npm run type-check
|
|
185
|
+
- run: npm test
|
|
186
|
+
`, n = `name: Deploy to Dev
|
|
187
|
+
|
|
188
|
+
on:
|
|
189
|
+
push:
|
|
190
|
+
branches:
|
|
191
|
+
- main
|
|
192
|
+
|
|
193
|
+
jobs:
|
|
194
|
+
release:
|
|
195
|
+
name: Release and Deploy
|
|
196
|
+
runs-on: ubuntu-latest
|
|
197
|
+
steps:
|
|
198
|
+
- name: Checkout
|
|
199
|
+
uses: actions/checkout@v4
|
|
200
|
+
with:
|
|
201
|
+
fetch-depth: 0
|
|
202
|
+
|
|
203
|
+
- name: Setup Node.js
|
|
204
|
+
uses: actions/setup-node@v4
|
|
205
|
+
with:
|
|
206
|
+
node-version: "22"
|
|
207
|
+
cache: "npm"
|
|
208
|
+
|
|
209
|
+
- name: Install dependencies
|
|
210
|
+
run: npm ci
|
|
211
|
+
|
|
212
|
+
- name: Release
|
|
213
|
+
env:
|
|
214
|
+
GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
|
|
215
|
+
run: npx semantic-release
|
|
216
|
+
|
|
217
|
+
- name: Deploy to Cloudflare (Dev)
|
|
218
|
+
uses: cloudflare/wrangler-action@v3
|
|
219
|
+
with:
|
|
220
|
+
apiToken: \${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
221
|
+
command: deploy
|
|
222
|
+
`, a = `name: Deploy to Production
|
|
223
|
+
|
|
224
|
+
on:
|
|
225
|
+
release:
|
|
226
|
+
types: ["released"]
|
|
227
|
+
|
|
228
|
+
jobs:
|
|
229
|
+
deploy:
|
|
230
|
+
name: Deploy to Production
|
|
231
|
+
runs-on: ubuntu-latest
|
|
232
|
+
steps:
|
|
233
|
+
- name: Checkout
|
|
234
|
+
uses: actions/checkout@v4
|
|
235
|
+
|
|
236
|
+
- name: Setup Node.js
|
|
237
|
+
uses: actions/setup-node@v4
|
|
238
|
+
with:
|
|
239
|
+
node-version: "22"
|
|
240
|
+
cache: "npm"
|
|
241
|
+
|
|
242
|
+
- name: Install dependencies
|
|
243
|
+
run: npm ci
|
|
244
|
+
|
|
245
|
+
- name: Deploy to Cloudflare (Production)
|
|
246
|
+
uses: cloudflare/wrangler-action@v3
|
|
247
|
+
with:
|
|
248
|
+
apiToken: \${{ secrets.PROD_CLOUDFLARE_API_TOKEN }}
|
|
249
|
+
command: deploy --env production
|
|
250
|
+
`;
|
|
251
|
+
o.writeFileSync(i.join(e, "unit-tests.yml"), r), o.writeFileSync(i.join(e, "deploy-dev.yml"), n), o.writeFileSync(i.join(e, "release.yml"), a), console.log("\\n📦 GitHub CI workflows created!");
|
|
252
|
+
}
|
|
253
|
+
function C(s) {
|
|
254
|
+
const e = {
|
|
255
|
+
branches: ["main"],
|
|
256
|
+
plugins: [
|
|
257
|
+
"@semantic-release/commit-analyzer",
|
|
258
|
+
"@semantic-release/release-notes-generator",
|
|
259
|
+
"@semantic-release/github"
|
|
260
|
+
]
|
|
261
|
+
};
|
|
262
|
+
o.writeFileSync(
|
|
263
|
+
i.join(s, ".releaserc.json"),
|
|
264
|
+
JSON.stringify(e, null, 2)
|
|
265
|
+
);
|
|
266
|
+
const r = i.join(s, "package.json"), n = JSON.parse(o.readFileSync(r, "utf-8"));
|
|
267
|
+
n.devDependencies = {
|
|
268
|
+
...n.devDependencies,
|
|
269
|
+
"semantic-release": "^24.0.0"
|
|
270
|
+
}, n.scripts = {
|
|
271
|
+
...n.scripts,
|
|
272
|
+
test: 'echo "No tests yet"',
|
|
273
|
+
"type-check": "tsc --noEmit"
|
|
274
|
+
}, o.writeFileSync(r, JSON.stringify(n, null, 2));
|
|
275
|
+
}
|
|
276
|
+
function v(s, e) {
|
|
277
|
+
return new Promise((r, n) => {
|
|
278
|
+
const a = b(s, [], {
|
|
279
|
+
cwd: e,
|
|
166
280
|
shell: !0,
|
|
167
281
|
stdio: "inherit"
|
|
168
282
|
});
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}),
|
|
283
|
+
a.on("close", (t) => {
|
|
284
|
+
t === 0 ? r() : n(new Error(`Command failed with exit code ${t}`));
|
|
285
|
+
}), a.on("error", n);
|
|
172
286
|
});
|
|
173
287
|
}
|
|
174
|
-
function S(
|
|
175
|
-
return new Promise((
|
|
176
|
-
const
|
|
177
|
-
cwd:
|
|
288
|
+
function S(s, e, r) {
|
|
289
|
+
return new Promise((n, a) => {
|
|
290
|
+
const t = b(s, [], {
|
|
291
|
+
cwd: e,
|
|
178
292
|
shell: !0,
|
|
179
293
|
stdio: "inherit",
|
|
180
|
-
env: { ...process.env, ...
|
|
294
|
+
env: { ...process.env, ...r }
|
|
181
295
|
});
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}),
|
|
296
|
+
t.on("close", (f) => {
|
|
297
|
+
f === 0 ? n() : a(new Error(`Command failed with exit code ${f}`));
|
|
298
|
+
}), t.on("error", a);
|
|
185
299
|
});
|
|
186
300
|
}
|
|
187
|
-
|
|
301
|
+
D.version("1.0.0").description("Create a new AuthHero project").argument("[project-name]", "name of the project").option(
|
|
188
302
|
"-t, --template <type>",
|
|
189
303
|
"template type: local, cloudflare-simple, or cloudflare-multitenant"
|
|
190
304
|
).option("-e, --email <email>", "admin email address").option("-p, --password <password>", "admin password (min 8 characters)").option(
|
|
191
305
|
"--package-manager <pm>",
|
|
192
306
|
"package manager to use: npm, yarn, pnpm, or bun"
|
|
193
|
-
).option("--skip-install", "skip installing dependencies").option("--skip-migrate", "skip running database migrations").option("--skip-seed", "skip seeding the database").option("--skip-start", "skip starting the development server").option("--
|
|
194
|
-
const
|
|
307
|
+
).option("--skip-install", "skip installing dependencies").option("--skip-migrate", "skip running database migrations").option("--skip-seed", "skip seeding the database").option("--skip-start", "skip starting the development server").option("--github-ci", "include GitHub CI workflows with semantic versioning").option("-y, --yes", "skip all prompts and use defaults/provided options").action(async (s, e) => {
|
|
308
|
+
const r = e.yes === !0;
|
|
195
309
|
console.log(`
|
|
196
310
|
🔐 Welcome to AuthHero!
|
|
197
311
|
`);
|
|
198
|
-
let
|
|
199
|
-
|
|
312
|
+
let n = s;
|
|
313
|
+
n || (r ? (n = "auth-server", console.log(`Using default project name: ${n}`)) : n = (await m.prompt([
|
|
200
314
|
{
|
|
201
315
|
type: "input",
|
|
202
316
|
name: "projectName",
|
|
203
317
|
message: "Project name:",
|
|
204
318
|
default: "auth-server",
|
|
205
|
-
validate: (
|
|
319
|
+
validate: (d) => d !== "" || "Project name cannot be empty"
|
|
206
320
|
}
|
|
207
321
|
])).projectName);
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
let
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
) || (console.error(`❌ Invalid template: ${
|
|
322
|
+
const a = i.join(process.cwd(), n);
|
|
323
|
+
o.existsSync(a) && (console.error(`❌ Project "${n}" already exists.`), process.exit(1));
|
|
324
|
+
let t;
|
|
325
|
+
e.template ? (["local", "cloudflare-simple", "cloudflare-multitenant"].includes(
|
|
326
|
+
e.template
|
|
327
|
+
) || (console.error(`❌ Invalid template: ${e.template}`), console.error(
|
|
214
328
|
"Valid options: local, cloudflare-simple, cloudflare-multitenant"
|
|
215
|
-
), process.exit(1)),
|
|
329
|
+
), process.exit(1)), t = e.template, console.log(`Using template: ${c[t].name}`)) : t = (await m.prompt([
|
|
216
330
|
{
|
|
217
331
|
type: "list",
|
|
218
332
|
name: "setupType",
|
|
219
333
|
message: "Select your setup type:",
|
|
220
334
|
choices: [
|
|
221
335
|
{
|
|
222
|
-
name: `${
|
|
223
|
-
${
|
|
336
|
+
name: `${c.local.name}
|
|
337
|
+
${c.local.description}`,
|
|
224
338
|
value: "local",
|
|
225
|
-
short:
|
|
339
|
+
short: c.local.name
|
|
226
340
|
},
|
|
227
341
|
{
|
|
228
|
-
name: `${
|
|
229
|
-
${
|
|
342
|
+
name: `${c["cloudflare-simple"].name}
|
|
343
|
+
${c["cloudflare-simple"].description}`,
|
|
230
344
|
value: "cloudflare-simple",
|
|
231
|
-
short:
|
|
345
|
+
short: c["cloudflare-simple"].name
|
|
232
346
|
},
|
|
233
347
|
{
|
|
234
|
-
name: `${
|
|
235
|
-
${
|
|
348
|
+
name: `${c["cloudflare-multitenant"].name}
|
|
349
|
+
${c["cloudflare-multitenant"].description}`,
|
|
236
350
|
value: "cloudflare-multitenant",
|
|
237
|
-
short:
|
|
351
|
+
short: c["cloudflare-multitenant"].name
|
|
238
352
|
}
|
|
239
353
|
]
|
|
240
354
|
}
|
|
241
355
|
])).setupType;
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
JSON.stringify(
|
|
356
|
+
const f = c[t];
|
|
357
|
+
o.mkdirSync(a, { recursive: !0 }), o.writeFileSync(
|
|
358
|
+
i.join(a, "package.json"),
|
|
359
|
+
JSON.stringify(f.packageJson(n), null, 2)
|
|
246
360
|
);
|
|
247
|
-
const k =
|
|
361
|
+
const k = i.join(
|
|
248
362
|
import.meta.url.replace("file://", "").replace("/create-authhero.js", ""),
|
|
249
|
-
|
|
363
|
+
f.templateDir
|
|
250
364
|
);
|
|
251
|
-
if (
|
|
252
|
-
const l =
|
|
253
|
-
|
|
365
|
+
if (o.existsSync(k) ? A(k, a) : (console.error(`❌ Template directory not found: ${k}`), process.exit(1)), t === "cloudflare-simple" || t === "cloudflare-multitenant") {
|
|
366
|
+
const l = i.join(a, "wrangler.toml"), d = i.join(a, "wrangler.local.toml");
|
|
367
|
+
o.existsSync(l) && o.copyFileSync(l, d);
|
|
368
|
+
const p = i.join(a, ".dev.vars.example"), u = i.join(a, ".dev.vars");
|
|
369
|
+
o.existsSync(p) && o.copyFileSync(p, u), console.log(
|
|
370
|
+
"📁 Created wrangler.local.toml and .dev.vars for local development"
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
let w = !1;
|
|
374
|
+
if ((t === "cloudflare-simple" || t === "cloudflare-multitenant") && (e.githubCi !== void 0 ? (w = e.githubCi, w && console.log("Including GitHub CI workflows with semantic versioning")) : r || (w = (await m.prompt([
|
|
375
|
+
{
|
|
376
|
+
type: "confirm",
|
|
377
|
+
name: "includeGithubCi",
|
|
378
|
+
message: "Would you like to include GitHub CI with semantic versioning?",
|
|
379
|
+
default: !1
|
|
380
|
+
}
|
|
381
|
+
])).includeGithubCi), w && (x(a), C(a))), t === "local") {
|
|
382
|
+
const l = I();
|
|
383
|
+
o.writeFileSync(i.join(a, "src/seed.ts"), l);
|
|
254
384
|
}
|
|
255
385
|
console.log(
|
|
256
386
|
`
|
|
257
|
-
✅ Project "${
|
|
387
|
+
✅ Project "${n}" has been created with ${f.name} setup!
|
|
258
388
|
`
|
|
259
389
|
);
|
|
260
390
|
let h;
|
|
261
|
-
if (
|
|
391
|
+
if (e.skipInstall ? h = !1 : r ? h = !0 : h = (await m.prompt([
|
|
262
392
|
{
|
|
263
393
|
type: "confirm",
|
|
264
394
|
name: "shouldInstall",
|
|
@@ -267,9 +397,9 @@ b.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
267
397
|
}
|
|
268
398
|
])).shouldInstall, h) {
|
|
269
399
|
let l;
|
|
270
|
-
|
|
271
|
-
`❌ Invalid package manager: ${
|
|
272
|
-
), console.error("Valid options: npm, yarn, pnpm, bun"), process.exit(1)), l =
|
|
400
|
+
e.packageManager ? (["npm", "yarn", "pnpm", "bun"].includes(e.packageManager) || (console.error(
|
|
401
|
+
`❌ Invalid package manager: ${e.packageManager}`
|
|
402
|
+
), console.error("Valid options: npm, yarn, pnpm, bun"), process.exit(1)), l = e.packageManager) : r ? l = "pnpm" : l = (await m.prompt([
|
|
273
403
|
{
|
|
274
404
|
type: "list",
|
|
275
405
|
name: "packageManager",
|
|
@@ -286,36 +416,14 @@ b.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
286
416
|
📦 Installing dependencies with ${l}...
|
|
287
417
|
`);
|
|
288
418
|
try {
|
|
289
|
-
const
|
|
290
|
-
await v(
|
|
419
|
+
const d = l === "pnpm" ? "pnpm install --ignore-workspace" : `${l} install`;
|
|
420
|
+
if (await v(d, a), t === "local" && (console.log(`
|
|
291
421
|
🔧 Building native modules...
|
|
292
|
-
`), await v("npm rebuild better-sqlite3",
|
|
422
|
+
`), await v("npm rebuild better-sqlite3", a)), console.log(`
|
|
293
423
|
✅ Dependencies installed successfully!
|
|
294
|
-
`)
|
|
295
|
-
let y = a.remote ? "remote" : "local";
|
|
296
|
-
if (e === "local" || e === "cloudflare-simple" || e === "cloudflare-multitenant") {
|
|
297
|
-
(e === "cloudflare-simple" || e === "cloudflare-multitenant") && !s && !a.remote && (y = (await c.prompt([
|
|
298
|
-
{
|
|
299
|
-
type: "list",
|
|
300
|
-
name: "mode",
|
|
301
|
-
message: "Would you like to run in local mode or remote mode?",
|
|
302
|
-
choices: [
|
|
303
|
-
{
|
|
304
|
-
name: "Local (using local D1 database)",
|
|
305
|
-
value: "local",
|
|
306
|
-
short: "Local"
|
|
307
|
-
},
|
|
308
|
-
{
|
|
309
|
-
name: "Remote (using production D1 database)",
|
|
310
|
-
value: "remote",
|
|
311
|
-
short: "Remote"
|
|
312
|
-
}
|
|
313
|
-
],
|
|
314
|
-
default: "local"
|
|
315
|
-
}
|
|
316
|
-
])).mode);
|
|
424
|
+
`), t === "local" || t === "cloudflare-simple" || t === "cloudflare-multitenant") {
|
|
317
425
|
let u;
|
|
318
|
-
if (
|
|
426
|
+
if (e.skipMigrate && e.skipSeed ? u = !1 : r ? u = !e.skipMigrate || !e.skipSeed : u = (await m.prompt([
|
|
319
427
|
{
|
|
320
428
|
type: "confirm",
|
|
321
429
|
name: "shouldSetup",
|
|
@@ -323,84 +431,72 @@ b.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
323
431
|
default: !0
|
|
324
432
|
}
|
|
325
433
|
])).shouldSetup, u) {
|
|
326
|
-
let
|
|
327
|
-
|
|
328
|
-
username:
|
|
329
|
-
password:
|
|
330
|
-
}, console.log(`Using admin email: ${
|
|
434
|
+
let g;
|
|
435
|
+
e.email && e.password ? (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e.email) || (console.error("❌ Invalid email address provided"), process.exit(1)), e.password.length < 8 && (console.error("❌ Password must be at least 8 characters"), process.exit(1)), g = {
|
|
436
|
+
username: e.email,
|
|
437
|
+
password: e.password
|
|
438
|
+
}, console.log(`Using admin email: ${e.email}`)) : g = await m.prompt([
|
|
331
439
|
{
|
|
332
440
|
type: "input",
|
|
333
441
|
name: "username",
|
|
334
442
|
message: "Admin email:",
|
|
335
443
|
default: "admin@example.com",
|
|
336
|
-
validate: (
|
|
444
|
+
validate: (y) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(y) || "Please enter a valid email address"
|
|
337
445
|
},
|
|
338
446
|
{
|
|
339
447
|
type: "password",
|
|
340
448
|
name: "password",
|
|
341
449
|
message: "Admin password:",
|
|
342
450
|
mask: "*",
|
|
343
|
-
validate: (
|
|
451
|
+
validate: (y) => y.length < 8 ? "Password must be at least 8 characters" : !0
|
|
344
452
|
}
|
|
345
|
-
]),
|
|
346
|
-
console.log(`
|
|
453
|
+
]), e.skipMigrate || (console.log(`
|
|
347
454
|
🔄 Running migrations...
|
|
348
|
-
`)
|
|
349
|
-
const m = (e === "cloudflare-simple" || e === "cloudflare-multitenant") && y === "remote" ? `${l} run db:migrate:remote` : `${l} run migrate`;
|
|
350
|
-
await v(m, t);
|
|
351
|
-
}
|
|
352
|
-
if (!a.skipSeed)
|
|
353
|
-
if (console.log(`
|
|
455
|
+
`), await v(`${l} run migrate`, a)), e.skipSeed || (console.log(`
|
|
354
456
|
🌱 Seeding database...
|
|
355
|
-
`),
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
ADMIN_PASSWORD: d.password
|
|
362
|
-
}
|
|
363
|
-
);
|
|
364
|
-
else {
|
|
365
|
-
const m = y === "remote" ? `${l} run seed:remote` : `${l} run seed:local`;
|
|
366
|
-
await S(m, t, {
|
|
367
|
-
ADMIN_EMAIL: d.username,
|
|
368
|
-
ADMIN_PASSWORD: d.password
|
|
369
|
-
});
|
|
457
|
+
`), t === "local" ? await S(
|
|
458
|
+
`${l} run seed`,
|
|
459
|
+
a,
|
|
460
|
+
{
|
|
461
|
+
ADMIN_EMAIL: g.username,
|
|
462
|
+
ADMIN_PASSWORD: g.password
|
|
370
463
|
}
|
|
464
|
+
) : await S(
|
|
465
|
+
`${l} run seed:local`,
|
|
466
|
+
a,
|
|
467
|
+
{
|
|
468
|
+
ADMIN_EMAIL: g.username,
|
|
469
|
+
ADMIN_PASSWORD: g.password
|
|
470
|
+
}
|
|
471
|
+
));
|
|
371
472
|
}
|
|
372
473
|
}
|
|
373
|
-
let
|
|
374
|
-
|
|
474
|
+
let p;
|
|
475
|
+
e.skipStart || r ? p = !1 : p = (await m.prompt([
|
|
375
476
|
{
|
|
376
477
|
type: "confirm",
|
|
377
478
|
name: "shouldStart",
|
|
378
479
|
message: "Would you like to start the development server?",
|
|
379
480
|
default: !0
|
|
380
481
|
}
|
|
381
|
-
])).shouldStart,
|
|
382
|
-
|
|
383
|
-
`
|
|
482
|
+
])).shouldStart, p && (console.log(
|
|
483
|
+
`
|
|
384
484
|
🚀 Starting development server on https://localhost:3000 ...
|
|
385
485
|
`
|
|
386
|
-
|
|
387
|
-
const u = (e === "cloudflare-simple" || e === "cloudflare-multitenant") && y === "remote" ? `${l} run dev:remote` : `${l} run dev`;
|
|
388
|
-
await v(u, t);
|
|
389
|
-
}
|
|
390
|
-
s && !w && (console.log(`
|
|
486
|
+
), await v(`${l} run dev`, a)), r && !p && (console.log(`
|
|
391
487
|
✅ Setup complete!`), console.log(`
|
|
392
|
-
To start the development server:`), console.log(` cd ${
|
|
488
|
+
To start the development server:`), console.log(` cd ${n}`), console.log(" npm run dev"), (t === "cloudflare-simple" || t === "cloudflare-multitenant") && console.log(
|
|
393
489
|
`
|
|
394
490
|
Server will be available at: https://localhost:3000`
|
|
395
491
|
));
|
|
396
|
-
} catch (
|
|
492
|
+
} catch (d) {
|
|
397
493
|
console.error(`
|
|
398
|
-
❌ An error occurred:`,
|
|
494
|
+
❌ An error occurred:`, d), process.exit(1);
|
|
399
495
|
}
|
|
400
496
|
}
|
|
401
|
-
h || (console.log("Next steps:"), console.log(` cd ${
|
|
497
|
+
h || (console.log("Next steps:"), console.log(` cd ${n}`), t === "local" ? (console.log(" npm install"), console.log(" npm run migrate"), console.log(
|
|
402
498
|
" ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD=yourpassword npm run seed"
|
|
403
|
-
), console.log(" npm run dev")) : (
|
|
499
|
+
), console.log(" npm run dev")) : (t === "cloudflare-simple" || t === "cloudflare-multitenant") && (console.log(" npm install"), console.log(
|
|
404
500
|
" npm run migrate # or npm run db:migrate:remote for production"
|
|
405
501
|
), console.log(
|
|
406
502
|
" ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD=yourpassword npm run seed"
|
|
@@ -409,4 +505,4 @@ Server will be available at: https://localhost:3000`)), console.log(`
|
|
|
409
505
|
For more information, visit: https://authhero.net/docs
|
|
410
506
|
`));
|
|
411
507
|
});
|
|
412
|
-
|
|
508
|
+
D.parse(process.argv);
|