@donotdev/cli 0.0.15 → 0.0.17
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/dependencies-matrix.json +67 -147
- package/dist/bin/commands/build.js +69 -52
- package/dist/bin/commands/bump.js +15 -14
- package/dist/bin/commands/create-app.js +258 -55
- package/dist/bin/commands/create-project.js +290 -161
- package/dist/bin/commands/deploy.js +146 -63
- package/dist/bin/commands/dev.js +43 -24
- package/dist/bin/commands/doctor.d.ts +6 -0
- package/dist/bin/commands/doctor.d.ts.map +1 -0
- package/dist/bin/commands/{lint.js → doctor.js} +1370 -146
- package/dist/bin/commands/doctor.js.map +1 -0
- package/dist/bin/commands/emu.js +295 -107
- package/dist/bin/commands/make-admin.js +77519 -11
- package/dist/bin/commands/preview.js +44 -25
- package/dist/bin/commands/setup.d.ts +6 -0
- package/dist/bin/commands/setup.d.ts.map +1 -0
- package/dist/bin/commands/setup.js +12123 -0
- package/dist/bin/commands/setup.js.map +1 -0
- package/dist/bin/commands/type-check.d.ts.map +1 -1
- package/dist/bin/commands/type-check.js +2022 -283
- package/dist/bin/commands/type-check.js.map +1 -1
- package/dist/bin/dndev.js +54 -58
- package/dist/bin/donotdev.js +54 -58
- package/dist/index.js +860 -459
- package/package.json +2 -2
- package/templates/app-expo/.env.example +2 -22
- package/templates/app-expo/README.md.example +1 -1
- package/templates/app-expo/assets/adaptive-icon.png +0 -0
- package/templates/app-expo/assets/favicon.png +0 -0
- package/templates/app-expo/assets/icon.png +0 -0
- package/templates/app-expo/assets/splash.png +0 -0
- package/templates/app-expo/src/config/app.ts.example +46 -0
- package/templates/app-expo/src/config/providers.ts.example +7 -0
- package/templates/app-next/src/config/providers.ts.example +7 -0
- package/templates/app-vite/src/config/providers.ts.example +7 -0
- package/templates/app-vite/src/pages/HomePage.tsx.example +1 -1
- package/templates/functions-firebase/README.md.example +1 -1
- package/templates/functions-firebase/functions-firebase/.env.example.example +1 -1
- package/templates/functions-firebase/functions-firebase/README.md.example +1 -1
- package/templates/functions-firebase/functions-firebase/tsconfig.json.example +1 -1
- package/templates/functions-firebase/functions.config.js.example +1 -1
- package/templates/functions-supabase/supabase/config.toml.example +59 -0
- package/templates/functions-supabase/supabase/functions/.env.example +13 -0
- package/templates/functions-supabase/supabase/functions/deno.json.example +8 -0
- package/templates/overlay-firebase/env.fragment.example +1 -1
- package/templates/overlay-firebase/env.fragment.expo.example +1 -1
- package/templates/overlay-firebase/env.fragment.nextjs.example +1 -1
- package/templates/overlay-supabase/env.fragment.example +8 -3
- package/templates/overlay-supabase/env.fragment.expo.example +8 -3
- package/templates/overlay-supabase/env.fragment.nextjs.example +8 -3
- package/templates/overlay-vercel/env.fragment.example +1 -1
- package/templates/overlay-vercel/env.fragment.nextjs.example +1 -1
- package/templates/root-consumer/AI.md.example +15 -0
- package/templates/root-consumer/guides/dndev/AGENT_START_HERE.md.example +2 -2
- package/templates/root-consumer/guides/dndev/ENV_SETUP.md.example +12 -12
- package/templates/root-consumer/guides/dndev/INDEX.md.example +3 -3
- package/templates/root-consumer/guides/dndev/SETUP_APP_CONFIG.md.example +3 -3
- package/templates/root-consumer/guides/dndev/SETUP_AUTH.md.example +13 -6
- package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +149 -988
- package/templates/root-consumer/guides/dndev/SETUP_FIREBASE.md.example +72 -20
- package/templates/root-consumer/guides/dndev/SETUP_FUNCTIONS.md.example +6 -111
- package/templates/root-consumer/guides/dndev/SETUP_OAUTH_PROVIDERS.md.example +60 -0
- package/templates/root-consumer/guides/dndev/SETUP_STRIPE.md.example +62 -0
- package/templates/root-consumer/guides/dndev/SETUP_SUPABASE.md.example +124 -33
- package/templates/root-consumer/guides/dndev/SETUP_VERCEL.md.example +108 -91
- package/templates/root-consumer/guides/dndev/advanced/EMULATORS.md.example +2 -2
- package/templates/root-consumer/guides/wai-way/WAI_WAY_CLI.md.example +7 -8
- package/templates/root-consumer/guides/wai-way/blueprints/1_scaffold.md.example +9 -5
- package/dist/bin/commands/firebase-setup.d.ts +0 -6
- package/dist/bin/commands/firebase-setup.d.ts.map +0 -1
- package/dist/bin/commands/firebase-setup.js +0 -7
- package/dist/bin/commands/firebase-setup.js.map +0 -1
- package/dist/bin/commands/lint.d.ts +0 -11
- package/dist/bin/commands/lint.d.ts.map +0 -1
- package/dist/bin/commands/lint.js.map +0 -1
- package/dist/bin/commands/staging.d.ts +0 -11
- package/dist/bin/commands/staging.d.ts.map +0 -1
- package/dist/bin/commands/staging.js +0 -12
- package/dist/bin/commands/staging.js.map +0 -1
- package/dist/bin/commands/supabase-setup.d.ts +0 -6
- package/dist/bin/commands/supabase-setup.d.ts.map +0 -1
- package/dist/bin/commands/supabase-setup.js +0 -7
- package/dist/bin/commands/supabase-setup.js.map +0 -1
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# Setup: Firebase
|
|
2
2
|
|
|
3
|
-
**From zero to deployed in 5 steps.**
|
|
3
|
+
**From zero to deployed in 5 steps. Covers Auth, Firestore, Storage, Cloud Functions, and Hosting.**
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## Step 1: Run Firebase Setup
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
dndev firebase
|
|
10
|
+
dndev setup firebase
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
This command:
|
|
@@ -60,7 +60,7 @@ dndev emu start
|
|
|
60
60
|
|
|
61
61
|
Select services: Auth + Firestore + Functions. This starts Firebase emulators so you can develop without touching production.
|
|
62
62
|
|
|
63
|
-
**Verify:** Open the app (`
|
|
63
|
+
**Verify:** Open the app (`dndev dev`), sign up, create some data. Everything runs locally.
|
|
64
64
|
|
|
65
65
|
---
|
|
66
66
|
|
|
@@ -81,6 +81,66 @@ This handles everything:
|
|
|
81
81
|
|
|
82
82
|
---
|
|
83
83
|
|
|
84
|
+
## Cloud Functions
|
|
85
|
+
|
|
86
|
+
Functions are scaffolded in `functions/` and auto-deployed with `dndev deploy`.
|
|
87
|
+
|
|
88
|
+
**Server secrets** (Stripe, OAuth, etc.) go in `functions/.env` — see the [Secrets](#secrets-stripe-oauth-etc) section below.
|
|
89
|
+
|
|
90
|
+
### CRUD Functions (one line)
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
// functions/src/index.ts
|
|
94
|
+
import { initializeApp } from 'firebase-admin/app';
|
|
95
|
+
import { createCrudFunctions } from '@donotdev/functions/firebase';
|
|
96
|
+
import * as entities from 'entities';
|
|
97
|
+
|
|
98
|
+
initializeApp();
|
|
99
|
+
export const crud = createCrudFunctions(entities);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Result:** Auto-generates CRUD handlers for each entity. The export name (camelCase, e.g. `crud`) is what you use client-side: `httpsCallable(functions, 'crud')`. The operation ID (snake_case, e.g. `create_products`) is used internally for logging and rate limiting.
|
|
103
|
+
|
|
104
|
+
### Custom Functions
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import * as v from 'valibot';
|
|
108
|
+
import { createFunction } from '@donotdev/functions/firebase';
|
|
109
|
+
import { getFirebaseAdminFirestore } from '@donotdev/firebase/server';
|
|
110
|
+
|
|
111
|
+
const schema = v.object({ productId: v.string() });
|
|
112
|
+
|
|
113
|
+
export const getProductDetails = createFunction(schema, 'get_product_details', async (data, { uid }) => {
|
|
114
|
+
const db = getFirebaseAdminFirestore();
|
|
115
|
+
const doc = await db.collection('products').doc(data.productId).get();
|
|
116
|
+
return doc.data();
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Rate limiting, metrics, auth, schema validation — all included by default.
|
|
121
|
+
|
|
122
|
+
### Import Rules
|
|
123
|
+
|
|
124
|
+
**Functions run on Node.js — you MUST use `/server` imports:**
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
// ✅ CORRECT
|
|
128
|
+
import { getFirebaseAdminFirestore } from '@donotdev/firebase/server';
|
|
129
|
+
import { handleError } from '@donotdev/core/server';
|
|
130
|
+
|
|
131
|
+
// ❌ WRONG - crashes on deploy
|
|
132
|
+
import { getFirestore } from '@donotdev/firebase';
|
|
133
|
+
import { handleError } from '@donotdev/core';
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Cloud Run IAM (Technical Detail)
|
|
137
|
+
|
|
138
|
+
Firebase 2nd gen Cloud Functions run on Cloud Run. By default, Cloud Run blocks unauthenticated OPTIONS requests (CORS preflight). `dndev deploy` automatically runs `gcloud run services update --no-invoker-iam-check` on all deployed functions.
|
|
139
|
+
|
|
140
|
+
If you deploy manually with `firebase deploy`, you'll see `403 Forbidden` on CORS preflight. Use `dndev deploy` instead.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
84
144
|
## Environment Variables
|
|
85
145
|
|
|
86
146
|
**Vite loads `.env` from the app directory only. NOT from the repo root.**
|
|
@@ -94,17 +154,20 @@ This handles everything:
|
|
|
94
154
|
| `functions/.env` | Server secrets: STRIPE_SECRET_KEY, OAuth secrets | Cloud Functions runtime |
|
|
95
155
|
| Root `.env` | **Not read by Vite.** Reference only. | Nothing |
|
|
96
156
|
|
|
97
|
-
**`dndev firebase
|
|
157
|
+
**`dndev setup firebase` writes Firebase vars to `apps/<app>/.env` automatically.**
|
|
98
158
|
|
|
99
|
-
**Custom domains:** Framework uses `APP_URL` hostname as `authDomain` in production (not Firebase's `projectId.firebaseapp.com`).
|
|
159
|
+
**Custom domains:** Framework uses `APP_URL` hostname as `authDomain` in production (not Firebase's `projectId.firebaseapp.com`). Set your `FIREBASE_AUTH_DOMAIN` env var (with the appropriate `VITE_` or `NEXT_PUBLIC_` prefix) in `.env.local` and `.env.production` — framework handles the rest.
|
|
100
160
|
|
|
101
161
|
---
|
|
102
162
|
|
|
103
163
|
## Staging Environment (Optional)
|
|
104
164
|
|
|
105
165
|
1. Create a second Firebase project (e.g., `my-app-staging`)
|
|
106
|
-
2. Run `dndev firebase
|
|
107
|
-
3.
|
|
166
|
+
2. Run `dndev setup firebase` again and select the staging project
|
|
167
|
+
3. The wizard updates `.firebaserc` — add a `"staging"` alias manually if the wizard only sets `"default"`:
|
|
168
|
+
```json
|
|
169
|
+
{ "projects": { "default": "my-app-prod", "staging": "my-app-staging" } }
|
|
170
|
+
```
|
|
108
171
|
4. Create `service-account-key.staging.json` (same steps as production)
|
|
109
172
|
5. Deploy: `dndev staging`
|
|
110
173
|
|
|
@@ -120,14 +183,13 @@ Server-side secrets go in `functions/.env`, not the app `.env`.
|
|
|
120
183
|
# functions/.env
|
|
121
184
|
STRIPE_SECRET_KEY=sk_live_...
|
|
122
185
|
STRIPE_WEBHOOK_SECRET=whsec_...
|
|
123
|
-
SUPABASE_SERVICE_ROLE_KEY=eyJ... # if using Supabase
|
|
124
186
|
GITHUB_CLIENT_SECRET=...
|
|
125
187
|
```
|
|
126
188
|
|
|
127
189
|
Then sync to your runtime and CI/CD:
|
|
128
190
|
|
|
129
191
|
```bash
|
|
130
|
-
dndev sync-secrets # Firebase Secret Manager
|
|
192
|
+
dndev sync-secrets # Firebase Secret Manager
|
|
131
193
|
dndev sync-secrets --target github # GitHub Secrets (for CI/CD workflows)
|
|
132
194
|
```
|
|
133
195
|
|
|
@@ -137,16 +199,6 @@ See [ENV_SETUP.md → Secrets Philosophy](./ENV_SETUP.md#secrets-philosophy) for
|
|
|
137
199
|
|
|
138
200
|
---
|
|
139
201
|
|
|
140
|
-
## Cloud Run IAM (Technical Detail)
|
|
141
|
-
|
|
142
|
-
Firebase 2nd gen Cloud Functions run on Cloud Run. By default, Cloud Run blocks unauthenticated OPTIONS requests (CORS preflight). This breaks browser calls to your functions.
|
|
143
|
-
|
|
144
|
-
`dndev deploy` automatically runs `gcloud run services update --no-invoker-iam-check` on all deployed functions. Your functions still validate Firebase Auth in code — this only allows the CORS preflight to pass.
|
|
145
|
-
|
|
146
|
-
If you deploy manually with `firebase deploy`, you'll need to run this yourself. Use `dndev deploy` instead.
|
|
147
|
-
|
|
148
|
-
---
|
|
149
|
-
|
|
150
202
|
## Troubleshooting
|
|
151
203
|
|
|
152
204
|
**"Service account key not found"**
|
|
@@ -171,4 +223,4 @@ If you deploy manually with `firebase deploy`, you'll need to run this yourself.
|
|
|
171
223
|
|
|
172
224
|
---
|
|
173
225
|
|
|
174
|
-
**`dndev firebase
|
|
226
|
+
**`dndev setup firebase` → download service account key → enable Auth + Firestore → `dndev emu start` → `dndev deploy`. That's it.**
|
|
@@ -1,114 +1,9 @@
|
|
|
1
|
-
# Setup:
|
|
1
|
+
# Setup: Functions
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**This guide has been merged into provider-specific guides.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- **Firebase Cloud Functions** → See [SETUP_FIREBASE.md § Cloud Functions](./SETUP_FIREBASE.md#cloud-functions)
|
|
6
|
+
- **Supabase Edge Functions** → See [SETUP_SUPABASE.md § Edge Functions](./SETUP_SUPABASE.md#edge-functions)
|
|
7
|
+
- **Vercel Functions** → See [SETUP_VERCEL.md § Vercel Functions](./SETUP_VERCEL.md#vercel-functions-optional)
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
**Functions run on Node.js - you MUST use `/server` imports or your function will crash.**
|
|
10
|
-
|
|
11
|
-
```typescript
|
|
12
|
-
// ✅ CORRECT
|
|
13
|
-
import { getFirebaseAdminFirestore } from '@donotdev/firebase/server';
|
|
14
|
-
import { handleError } from '@donotdev/core/server';
|
|
15
|
-
|
|
16
|
-
// ❌ WRONG - crashes on deploy
|
|
17
|
-
import { getFirestore } from '@donotdev/firebase';
|
|
18
|
-
import { handleError } from '@donotdev/core';
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
**Rule:** Always use `/server` suffix for:
|
|
22
|
-
- `@donotdev/firebase/server`
|
|
23
|
-
- `@donotdev/core/server`
|
|
24
|
-
- `@donotdev/utils/server`
|
|
25
|
-
|
|
26
|
-
---
|
|
27
|
-
|
|
28
|
-
## CRUD Functions Setup
|
|
29
|
-
|
|
30
|
-
**One line to register all entity functions:**
|
|
31
|
-
|
|
32
|
-
```typescript
|
|
33
|
-
// functions/src/index.ts
|
|
34
|
-
import { initializeApp } from 'firebase-admin/app';
|
|
35
|
-
import { createCrudFunctions } from '@donotdev/functions/firebase';
|
|
36
|
-
import * as entities from 'entities';
|
|
37
|
-
|
|
38
|
-
initializeApp();
|
|
39
|
-
export const crud = createCrudFunctions(entities);
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
**Result:** Auto-generates `create_products`, `get_products`, `list_products`, `update_products`, `delete_products` for each entity.
|
|
43
|
-
|
|
44
|
-
**Access control:** Configured via `entity.access` (see SETUP_CRUD.md). All functions use `invoker: 'public'` - security enforced via role-based access in code.
|
|
45
|
-
|
|
46
|
-
---
|
|
47
|
-
|
|
48
|
-
## Custom Functions
|
|
49
|
-
|
|
50
|
-
**Use `createFunction` — handles config, validation, auth, rate limiting, metrics automatically:**
|
|
51
|
-
|
|
52
|
-
```typescript
|
|
53
|
-
import * as v from 'valibot';
|
|
54
|
-
import { createFunction } from '@donotdev/functions/firebase';
|
|
55
|
-
import { getFirebaseAdminFirestore } from '@donotdev/firebase/server';
|
|
56
|
-
|
|
57
|
-
const schema = v.object({ productId: v.string() });
|
|
58
|
-
|
|
59
|
-
export const getProductDetails = createFunction(schema, 'get_product_details', async (data, { uid }) => {
|
|
60
|
-
const db = getFirebaseAdminFirestore();
|
|
61
|
-
const doc = await db.collection('products').doc(data.productId).get();
|
|
62
|
-
return doc.data();
|
|
63
|
-
});
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
**That's it.** Rate limiting, metrics, auth, schema validation — all included by default. No config needed.
|
|
67
|
-
|
|
68
|
-
**Advanced:** Use `createBaseFunction` if you need custom config (memory, timeout, region override).
|
|
69
|
-
|
|
70
|
-
---
|
|
71
|
-
|
|
72
|
-
## Post-Deployment: Cloud Run IAM
|
|
73
|
-
|
|
74
|
-
**`dndev deploy` handles this automatically.** It runs `gcloud run services update --no-invoker-iam-check` on all deployed functions after each deploy.
|
|
75
|
-
|
|
76
|
-
If you deploy manually with `firebase deploy` (not recommended), you'll see `403 Forbidden` on CORS preflight. Fix by using `dndev deploy` instead, or run manually:
|
|
77
|
-
|
|
78
|
-
```bash
|
|
79
|
-
gcloud run services update <function-name> --region=<region> --no-invoker-iam-check --project=<project-id>
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
**Why?** Cloud Run blocks unauthenticated OPTIONS (CORS preflight) by default. Your function still validates Firebase Auth in code — this only allows the preflight to pass.
|
|
83
|
-
|
|
84
|
-
---
|
|
85
|
-
|
|
86
|
-
## Function Naming
|
|
87
|
-
|
|
88
|
-
**Export name (camelCase):**
|
|
89
|
-
```typescript
|
|
90
|
-
export const getDashboardMetrics = onCall(...);
|
|
91
|
-
// Client: httpsCallable(functions, 'getDashboardMetrics')
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
**Operation ID (snake_case):**
|
|
95
|
-
```typescript
|
|
96
|
-
createBaseFunction(config, handler, 'get_dashboard_metrics')
|
|
97
|
-
// Used for logging, rate limiting
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
---
|
|
101
|
-
|
|
102
|
-
## Environment
|
|
103
|
-
|
|
104
|
-
```bash
|
|
105
|
-
# .env.local (local)
|
|
106
|
-
STRIPE_SECRET_KEY=sk_test_...
|
|
107
|
-
|
|
108
|
-
# .env (production, synced via dndev sync-secrets)
|
|
109
|
-
STRIPE_SECRET_KEY=sk_live_...
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
---
|
|
113
|
-
|
|
114
|
-
**Add functions, get backend. Framework handles the rest.**
|
|
9
|
+
Each provider guide covers function setup, CRUD registration, custom functions, import rules, and deployment for that stack.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# OAuth Provider Setup Guide
|
|
2
|
+
|
|
3
|
+
Each OAuth provider requires manual registration in their developer console.
|
|
4
|
+
`dndev setup oauth` computes the correct redirect URI for you.
|
|
5
|
+
|
|
6
|
+
## Redirect URIs
|
|
7
|
+
|
|
8
|
+
### Firebase
|
|
9
|
+
```
|
|
10
|
+
https://{{PROJECT_ID}}.firebaseapp.com/__/auth/handler
|
|
11
|
+
```
|
|
12
|
+
> If you use a custom auth domain, replace `{{PROJECT_ID}}.firebaseapp.com` with your custom domain.
|
|
13
|
+
|
|
14
|
+
### Supabase
|
|
15
|
+
```
|
|
16
|
+
https://{{PROJECT_ID}}.supabase.co/auth/v1/callback
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Google OAuth
|
|
22
|
+
|
|
23
|
+
1. Go to https://console.cloud.google.com/apis/credentials
|
|
24
|
+
2. Create **OAuth 2.0 Client ID** (type: Web application)
|
|
25
|
+
3. Add **Authorized redirect URI** (see above)
|
|
26
|
+
4. Copy **Client ID** and **Client Secret**
|
|
27
|
+
5. Enable in your backend:
|
|
28
|
+
- Firebase: Console > Authentication > Sign-in method > Google
|
|
29
|
+
- Supabase: Dashboard > Authentication > Providers > Google
|
|
30
|
+
|
|
31
|
+
## GitHub OAuth
|
|
32
|
+
|
|
33
|
+
1. Go to https://github.com/settings/developers
|
|
34
|
+
2. Click **New OAuth App**
|
|
35
|
+
3. Set **Authorization callback URL** (see redirect URI above)
|
|
36
|
+
4. Copy **Client ID** and **Client Secret**
|
|
37
|
+
5. Enable in your backend:
|
|
38
|
+
- Firebase: Console > Authentication > Sign-in method > GitHub
|
|
39
|
+
- Supabase: Dashboard > Authentication > Providers > GitHub
|
|
40
|
+
|
|
41
|
+
## Apple Sign In
|
|
42
|
+
|
|
43
|
+
1. Go to https://developer.apple.com/account/resources/identifiers/list/serviceId
|
|
44
|
+
2. Create a **Services ID**
|
|
45
|
+
3. Enable **Sign In with Apple**
|
|
46
|
+
4. Add **Redirect URL** (see above)
|
|
47
|
+
5. Create a **Key** for Sign In with Apple
|
|
48
|
+
6. Enable in your backend:
|
|
49
|
+
- Firebase: Console > Authentication > Sign-in method > Apple
|
|
50
|
+
- Supabase: Dashboard > Authentication > Providers > Apple
|
|
51
|
+
|
|
52
|
+
> **Note:** Apple requires an Apple Developer Program membership ($99/year).
|
|
53
|
+
|
|
54
|
+
## Verify
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
dndev doctor
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Check that auth providers are detected and backend configuration is referenced.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Stripe Setup Guide
|
|
2
|
+
|
|
3
|
+
## Prerequisites
|
|
4
|
+
- Stripe account (https://dashboard.stripe.com)
|
|
5
|
+
- `@donotdev/billing` installed
|
|
6
|
+
|
|
7
|
+
## 1. Get API Keys
|
|
8
|
+
|
|
9
|
+
1. Go to https://dashboard.stripe.com/apikeys
|
|
10
|
+
2. Copy your **Publishable key** (`pk_test_...`) → `.env`
|
|
11
|
+
3. Copy your **Secret key** (`sk_test_...`) → `supabase/functions/.env` or `functions/.env`
|
|
12
|
+
|
|
13
|
+
> **Security:** Never commit secret keys. Stripe also supports restricted keys (`rk_test_...`, `rk_live_...`) for tighter permissions.
|
|
14
|
+
|
|
15
|
+
## 2. Configure Webhook
|
|
16
|
+
|
|
17
|
+
1. Go to https://dashboard.stripe.com/webhooks
|
|
18
|
+
2. Click **Add endpoint**
|
|
19
|
+
3. Set endpoint URL:
|
|
20
|
+
- Supabase: `https://{{PROJECT_ID}}.supabase.co/functions/v1/stripe-webhook`
|
|
21
|
+
- Firebase: `https://europe-west1-{{PROJECT_ID}}.cloudfunctions.net/stripeWebhook` (replace `europe-west1` with your region if different)
|
|
22
|
+
4. Select events:
|
|
23
|
+
- `checkout.session.completed`
|
|
24
|
+
- `customer.subscription.created`
|
|
25
|
+
- `customer.subscription.updated`
|
|
26
|
+
- `customer.subscription.deleted`
|
|
27
|
+
- `invoice.payment_succeeded`
|
|
28
|
+
- `invoice.payment_failed`
|
|
29
|
+
5. Copy the **Signing secret** (`whsec_...`) → `supabase/functions/.env`
|
|
30
|
+
|
|
31
|
+
## 3. Environment Variables
|
|
32
|
+
|
|
33
|
+
### App `.env` (public)
|
|
34
|
+
```
|
|
35
|
+
VITE_STRIPE_PUBLISHABLE_KEY=pk_test_...
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Functions `.env` (secret)
|
|
39
|
+
```
|
|
40
|
+
STRIPE_SECRET_KEY=sk_test_...
|
|
41
|
+
STRIPE_WEBHOOK_SECRET=whsec_...
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 4. Test Clocks (Optional)
|
|
45
|
+
|
|
46
|
+
For subscription testing without waiting for real billing cycles:
|
|
47
|
+
1. Go to https://dashboard.stripe.com/test/test-clocks
|
|
48
|
+
2. Create a test clock
|
|
49
|
+
3. Create customers attached to the test clock
|
|
50
|
+
4. Advance time to trigger billing events
|
|
51
|
+
|
|
52
|
+
## 5. Verify
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
dndev setup stripe # Interactive key setup + validation
|
|
56
|
+
dndev doctor # Health check for all providers
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Check that:
|
|
60
|
+
- Publishable key format is valid
|
|
61
|
+
- Secret key format is valid
|
|
62
|
+
- Key modes match (both test or both live)
|
|
@@ -1,47 +1,44 @@
|
|
|
1
1
|
# Setup: Supabase
|
|
2
2
|
|
|
3
|
-
**From zero to a working Supabase backend
|
|
3
|
+
**From zero to a working Supabase backend. Covers Auth, PostgreSQL, Storage, Edge Functions, and deployment.**
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## Step 1: Run Supabase Setup
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
dndev supabase
|
|
10
|
+
dndev setup supabase
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
This command:
|
|
14
14
|
- Lets you choose the target app (if you have an `apps/` directory)
|
|
15
|
-
- Asks for your **public** Supabase project URL and
|
|
16
|
-
- Writes `VITE_SUPABASE_URL` and `
|
|
15
|
+
- Asks for your **public** Supabase project URL and public key
|
|
16
|
+
- Writes `VITE_SUPABASE_URL` and `VITE_SUPABASE_PUBLIC_KEY` to your app's `.env`
|
|
17
17
|
|
|
18
18
|
**We only ask for public credentials** (safe to ship in your client bundle). We never ask for the service_role key.
|
|
19
19
|
|
|
20
|
-
Get URL and
|
|
20
|
+
Get URL and public key from: [Supabase Dashboard](https://supabase.com/dashboard) → your project → **Settings → API**.
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
24
24
|
## Step 2: Generate Tables from Entities
|
|
25
25
|
|
|
26
|
-
The framework
|
|
26
|
+
The framework generates PostgreSQL migrations from your entity definitions. This runs automatically as part of `dndev setup supabase`, or you can run it separately:
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
|
|
29
|
+
dndev setup supabase
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
During setup, the wizard detects your entities and generates SQL migrations.
|
|
33
|
+
|
|
32
34
|
**What it does:**
|
|
33
|
-
- Discovers entities (e.g. in `entities/` or your configured
|
|
35
|
+
- Discovers entities (e.g. in `entities/` or your configured entity directory)
|
|
34
36
|
- For each entity: `CREATE TABLE` with columns mapped from field types (text, number, boolean, timestamptz, uuid, jsonb, etc.)
|
|
35
37
|
- Adds technical columns: `id` (uuid, default `gen_random_uuid()`), `user_id`, `created_at`, `updated_at` (with `DEFAULT now()`), `created_by_id`, `updated_by_id`, `status`
|
|
36
38
|
- Enables **Row Level Security (RLS)** and creates policies so rows are scoped by `auth.uid() = user_id`
|
|
37
39
|
- Adds a trigger so `updated_at` is set automatically on every `UPDATE`
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
- `--entity-dir <path>` — where to find entity files (default: from app root)
|
|
41
|
-
- `--output-dir <path>` — where to write migrations (default: `supabase/migrations`)
|
|
42
|
-
- `--no-single-file` — one migration file per entity instead of one combined file
|
|
43
|
-
|
|
44
|
-
Output is written to `supabase/migrations/` (or your `--output-dir`) as a timestamped `.sql` file. Apply it with the Supabase CLI or Dashboard SQL editor.
|
|
41
|
+
Output is written to `supabase/migrations/` as a timestamped `.sql` file.
|
|
45
42
|
|
|
46
43
|
---
|
|
47
44
|
|
|
@@ -63,7 +60,80 @@ Copy the contents of the generated migration file into the SQL Editor in the Sup
|
|
|
63
60
|
|
|
64
61
|
---
|
|
65
62
|
|
|
66
|
-
## Step 4:
|
|
63
|
+
## Step 4: Enable Supabase Services
|
|
64
|
+
|
|
65
|
+
Go to the Supabase Dashboard and configure:
|
|
66
|
+
|
|
67
|
+
**Authentication** (enabled by default on new projects):
|
|
68
|
+
- Authentication → Providers → Verify Email is enabled
|
|
69
|
+
- Add OAuth providers if needed (Google, GitHub, etc.)
|
|
70
|
+
|
|
71
|
+
**Storage** (optional — only if your app uploads files):
|
|
72
|
+
- Storage → Create bucket (e.g. `uploads`)
|
|
73
|
+
- Configure public or RLS policies as needed
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Edge Functions
|
|
78
|
+
|
|
79
|
+
Supabase Edge Functions run on Deno at the edge. If you selected functions during scaffolding, they're in `supabase/functions/`.
|
|
80
|
+
|
|
81
|
+
### CRUD Functions (one line)
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
// supabase/functions/crud/index.ts
|
|
85
|
+
import * as entities from '../_shared/entities.ts';
|
|
86
|
+
import { createSupabaseCrudFunctions } from '@donotdev/functions/supabase';
|
|
87
|
+
|
|
88
|
+
const { serve } = createSupabaseCrudFunctions(entities);
|
|
89
|
+
Deno.serve(serve);
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Result:** Auto-generates create, get, list, update, delete handlers for each entity. The dispatcher routes via `_functionName` in the request body.
|
|
93
|
+
|
|
94
|
+
### Custom Functions
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
// supabase/functions/my-function/index.ts
|
|
98
|
+
import * as v from 'valibot';
|
|
99
|
+
import { createSupabaseHandler } from '@donotdev/functions/supabase';
|
|
100
|
+
|
|
101
|
+
const schema = v.object({ productId: v.string() });
|
|
102
|
+
|
|
103
|
+
export default createSupabaseHandler(
|
|
104
|
+
'get_product_details',
|
|
105
|
+
schema,
|
|
106
|
+
async (data, ctx) => {
|
|
107
|
+
const { data: product } = await ctx.supabaseAdmin
|
|
108
|
+
.from('products')
|
|
109
|
+
.select('*')
|
|
110
|
+
.eq('id', data.productId)
|
|
111
|
+
.single();
|
|
112
|
+
return product;
|
|
113
|
+
},
|
|
114
|
+
'user' // requiredRole (default)
|
|
115
|
+
);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Context available in handler:** `ctx.uid` (authenticated user ID), `ctx.userRole`, `ctx.supabaseAdmin` (admin client with full access).
|
|
119
|
+
|
|
120
|
+
Rate limiting, metrics, auth, schema validation — all included by default.
|
|
121
|
+
|
|
122
|
+
### Deploy Edge Functions
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
supabase functions deploy
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Or deploy everything with:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
dndev deploy
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Adapter Behavior (DB-Managed Timestamps)
|
|
67
137
|
|
|
68
138
|
Tables use **snake_case** column names and **timestamptz** for `created_at` / `updated_at`. The framework expects **camelCase** and **ISO date strings** in the app.
|
|
69
139
|
|
|
@@ -74,7 +144,7 @@ The **Supabase CRUD adapter** handles this automatically:
|
|
|
74
144
|
| **Read** (get, query, subscribe) | Rows from Supabase are normalized: snake_case → camelCase (e.g. `created_at` → `createdAt`), and timestamp columns are converted to ISO strings. Your UI receives the same shape as with Firebase. |
|
|
75
145
|
| **Write** (add, set, update) | The adapter does **not** send `createdAt` / `updatedAt` (or snake equivalents). The database sets them via `DEFAULT now()` and the `updated_at` trigger. |
|
|
76
146
|
|
|
77
|
-
|
|
147
|
+
You never set timestamps in app code — the DB owns them.
|
|
78
148
|
|
|
79
149
|
---
|
|
80
150
|
|
|
@@ -85,40 +155,61 @@ So you never set timestamps in app code when using Supabase — the DB owns them
|
|
|
85
155
|
| Variable | Purpose |
|
|
86
156
|
|----------|---------|
|
|
87
157
|
| `VITE_SUPABASE_URL` | Project URL (public) |
|
|
88
|
-
| `
|
|
158
|
+
| `VITE_SUPABASE_PUBLIC_KEY` | Public key (safe in bundle) |
|
|
89
159
|
|
|
90
|
-
**Server (
|
|
160
|
+
**Server (Edge Functions, API routes):** use the same URL and `SUPABASE_SERVICE_ROLE_KEY` for admin operations. Never expose the service_role key to the client. Put it in `functions/.env` or your host's env (Vercel, etc.).
|
|
91
161
|
|
|
92
|
-
See [ENV_SETUP.md](./ENV_SETUP.md) for
|
|
162
|
+
See [ENV_SETUP.md](./ENV_SETUP.md) for the full secrets policy.
|
|
93
163
|
|
|
94
164
|
---
|
|
95
165
|
|
|
96
|
-
##
|
|
166
|
+
## Hosting the Frontend
|
|
167
|
+
|
|
168
|
+
Supabase provides **Auth, PostgreSQL, Storage, and Edge Functions**. It does **not** host your built frontend.
|
|
169
|
+
|
|
170
|
+
**We recommend Vercel** — see [SETUP_VERCEL.md](./SETUP_VERCEL.md).
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
dndev deploy
|
|
174
|
+
```
|
|
97
175
|
|
|
98
|
-
|
|
176
|
+
For Supabase projects, `dndev deploy` deploys frontend to Vercel (via scaffolded `vercel.json`) and Edge Functions to Supabase. Make sure you've set `VITE_SUPABASE_*` in Vercel Dashboard → Environment Variables.
|
|
99
177
|
|
|
100
178
|
---
|
|
101
179
|
|
|
102
|
-
##
|
|
180
|
+
## Local Development
|
|
181
|
+
|
|
182
|
+
**Against hosted Supabase:**
|
|
103
183
|
|
|
104
|
-
|
|
184
|
+
```bash
|
|
185
|
+
dndev dev
|
|
186
|
+
```
|
|
105
187
|
|
|
106
|
-
|
|
188
|
+
The app talks to your Supabase project directly.
|
|
107
189
|
|
|
108
|
-
|
|
109
|
-
- We scaffold **vercel.json**; run `dndev deploy` and choose Frontend (Vercel) or Frontend + Edge Functions. Set `VITE_SUPABASE_*` in Vercel project env.
|
|
190
|
+
**Local emulation:**
|
|
110
191
|
|
|
111
|
-
|
|
192
|
+
```bash
|
|
193
|
+
dndev emu start
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Or install the [Supabase CLI](https://supabase.com/docs/guides/cli) and run `supabase start` for a local Postgres + Auth + Storage stack. Point `VITE_SUPABASE_URL` and keys to the local instance.
|
|
112
197
|
|
|
113
198
|
---
|
|
114
199
|
|
|
115
|
-
##
|
|
200
|
+
## Troubleshooting
|
|
116
201
|
|
|
117
|
-
|
|
118
|
-
|
|
202
|
+
**"Table not found" / "relation does not exist"**
|
|
203
|
+
→ Run `dndev setup supabase` (generates SQL) then apply migrations with `supabase db push`
|
|
119
204
|
|
|
120
|
-
|
|
205
|
+
**"Permission denied" / RLS errors**
|
|
206
|
+
→ Check RLS policies in Supabase Dashboard → Database → Policies
|
|
207
|
+
→ Generated migrations include default policies — verify they were applied
|
|
121
208
|
|
|
122
|
-
|
|
209
|
+
**"Service role key" errors in functions**
|
|
210
|
+
→ Put `SUPABASE_SERVICE_ROLE_KEY` in `functions/.env` (never in `VITE_*` vars)
|
|
211
|
+
→ Get it from Supabase Dashboard → Settings → API
|
|
212
|
+
|
|
213
|
+
---
|
|
123
214
|
|
|
124
|
-
**`dndev
|
|
215
|
+
**`dndev setup supabase` → paste URL + public key → apply migrations → `dndev dev`. The adapter normalizes everything automatically.**
|