@knowcode/doc-builder 1.7.6 → 1.8.1

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.
Files changed (50) hide show
  1. package/.claude/settings.local.json +6 -1
  2. package/CHANGELOG.md +29 -0
  3. package/assets/css/notion-style.css +9 -1
  4. package/assets/js/main.js +10 -6
  5. package/html/README.html +44 -20
  6. package/html/auth.js +62 -13
  7. package/html/css/notion-style.css +9 -1
  8. package/html/documentation-index.html +44 -20
  9. package/html/guides/authentication-default-change.html +44 -20
  10. package/html/guides/authentication-guide.html +228 -263
  11. package/html/guides/claude-workflow-guide.html +44 -20
  12. package/html/guides/documentation-standards.html +44 -20
  13. package/html/guides/phosphor-icons-guide.html +44 -20
  14. package/html/guides/private-directory-authentication.html +472 -0
  15. package/html/guides/public-site-deployment.html +50 -25
  16. package/html/guides/search-engine-verification-guide.html +44 -20
  17. package/html/guides/seo-guide.html +44 -20
  18. package/html/guides/seo-optimization-guide.html +44 -20
  19. package/html/guides/troubleshooting-guide.html +44 -20
  20. package/html/guides/windows-setup-guide.html +44 -20
  21. package/html/index.html +44 -20
  22. package/html/js/auth.js +118 -39
  23. package/html/js/main.js +10 -6
  24. package/html/login.html +4 -4
  25. package/html/logout.html +2 -2
  26. package/html/private/cache-control-anti-pattern.html +408 -0
  27. package/html/private/launch/README.html +350 -0
  28. package/html/private/launch/auth-cleanup-summary.html +340 -0
  29. package/html/private/launch/bubble-plugin-specification.html +986 -0
  30. package/html/private/launch/go-to-market-strategy.html +716 -0
  31. package/html/private/launch/launch-announcements.html +646 -0
  32. package/html/private/launch/vercel-deployment-auth-setup.html +390 -0
  33. package/html/private/next-steps-walkthrough.html +685 -0
  34. package/html/private/supabase-auth-implementation-completed.html +433 -0
  35. package/html/private/supabase-auth-implementation-plan.html +590 -0
  36. package/html/private/supabase-auth-integration-plan.html +718 -0
  37. package/html/private/supabase-auth-setup-guide.html +545 -0
  38. package/html/private/test-private-doc.html +281 -0
  39. package/html/robots.txt +4 -0
  40. package/html/sitemap.xml +113 -29
  41. package/html/vercel-cli-setup-guide.html +44 -20
  42. package/html/vercel-first-time-setup-guide.html +44 -20
  43. package/lib/config.js +21 -3
  44. package/lib/core-builder.js +49 -7
  45. package/lib/supabase-auth.js +80 -25
  46. package/package.json +1 -1
  47. package/user-management/README.md +306 -0
  48. package/user-management/add-users.sh +730 -0
  49. package/user-management/create-user.js +65 -0
  50. package/user-management/users.txt +15 -0
@@ -0,0 +1,730 @@
1
+ #!/bin/bash
2
+
3
+ # Doc-Builder User Management System
4
+ # Uses Supabase CLI for user creation and database operations
5
+
6
+ set -e
7
+
8
+ # Colors for output
9
+ RED='\033[0;31m'
10
+ GREEN='\033[0;32m'
11
+ YELLOW='\033[1;33m'
12
+ BLUE='\033[0;34m'
13
+ CYAN='\033[0;36m'
14
+ MAGENTA='\033[0;35m'
15
+ BOLD='\033[1m'
16
+ NC='\033[0m' # No Color
17
+
18
+ # Configuration
19
+ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
20
+ CONFIG_FILE="$SCRIPT_DIR/.supabase-config"
21
+ PROJECT_ID="xcihhnfcitjrwbynxmka" # Default project ID
22
+
23
+ # Load configuration if exists
24
+ if [ -f "$CONFIG_FILE" ]; then
25
+ source "$CONFIG_FILE"
26
+ fi
27
+
28
+ # Show comprehensive help
29
+ show_help() {
30
+ cat << 'EOF'
31
+ ═══════════════════════════════════════════════════════════════════════════════════
32
+ DOC-BUILDER USER MANAGEMENT SYSTEM
33
+ ═══════════════════════════════════════════════════════════════════════════════════
34
+
35
+ Manage user access to Supabase-authenticated documentation sites using the
36
+ Supabase CLI. This tool creates users and manages their site access.
37
+
38
+ PREREQUISITES:
39
+ ✓ Supabase CLI installed (npm install -g supabase)
40
+ ✓ Logged in to Supabase (supabase login)
41
+ ✓ Project linked (or use 'setup' command)
42
+
43
+ USAGE:
44
+ ./add-users.sh <command> [options]
45
+
46
+ COMMANDS:
47
+
48
+ setup Initial setup - link your Supabase project
49
+ Example: ./add-users.sh setup
50
+
51
+ add <site-url> <email> Grant user access to a site
52
+ Example: ./add-users.sh add docs.example.com user@email.com
53
+ - Checks if user exists
54
+ - If not, prompts you to create via Supabase dashboard
55
+ - Grants access to the specified site
56
+
57
+ bulk <site-url> <file> Add multiple users from a file
58
+ Example: ./add-users.sh bulk docs.example.com users.txt
59
+ - File should contain one email per line
60
+ - Lines starting with # are ignored
61
+ - Empty lines are skipped
62
+
63
+ list <site-url> List all users with access to a site
64
+ Example: ./add-users.sh list docs.example.com
65
+ - Shows email, creation date, last login
66
+ - Shows total user count
67
+
68
+ check <email> Check user status across all sites
69
+ Example: ./add-users.sh check user@email.com
70
+ - Shows if user exists
71
+ - Lists all sites they have access to
72
+ - Shows last login time
73
+
74
+ remove <site-url> <email> Remove user's access to a site
75
+ Example: ./add-users.sh remove docs.example.com user@email.com
76
+ - Only removes access, doesn't delete user
77
+ - User can still access other sites
78
+
79
+ sites List all documentation sites
80
+ Example: ./add-users.sh sites
81
+ - Shows site URL, name, and user count
82
+
83
+ delete-user <email> Remove user access from ALL sites
84
+ Example: ./add-users.sh delete-user user@email.com
85
+ - Removes access to all sites
86
+ - Does NOT delete the user account (use dashboard for that)
87
+
88
+ SITE URLS:
89
+ - Use domain without https:// prefix
90
+ - Examples: docs.example.com, my-app.vercel.app
91
+
92
+ ENVIRONMENT VARIABLES:
93
+ SUPABASE_PROJECT_ID Your Supabase project ID (optional)
94
+ SUPABASE_DB_URL Database connection URL (optional)
95
+
96
+ EXAMPLES:
97
+
98
+ Initial Setup:
99
+ ./add-users.sh setup
100
+
101
+ Add a single user:
102
+ ./add-users.sh add wru-bid-analysis.vercel.app lindsay@knowcode.tech
103
+
104
+ Add multiple users:
105
+ echo "user1@example.com" > users.txt
106
+ echo "user2@example.com" >> users.txt
107
+ ./add-users.sh bulk my-docs.vercel.app users.txt
108
+
109
+ Check who has access:
110
+ ./add-users.sh list my-docs.vercel.app
111
+
112
+ Remove someone's access:
113
+ ./add-users.sh remove my-docs.vercel.app user@example.com
114
+
115
+ TROUBLESHOOTING:
116
+
117
+ "Supabase CLI not found"
118
+ → Install it: npm install -g supabase
119
+
120
+ "Not logged in to Supabase"
121
+ → Run: supabase login
122
+
123
+ "Project not linked"
124
+ → Run: ./add-users.sh setup
125
+
126
+ "User already exists"
127
+ → This is fine, the script will continue
128
+
129
+ "Access already granted"
130
+ → User already has access to this site
131
+
132
+ MORE HELP:
133
+ GitHub: https://github.com/wapdat/doc-builder
134
+ Docs: https://doc-builder-delta.vercel.app
135
+
136
+ ═══════════════════════════════════════════════════════════════════════════════════
137
+ EOF
138
+ }
139
+
140
+ # Check if Supabase CLI is installed
141
+ check_supabase_cli() {
142
+ if ! command -v supabase &> /dev/null; then
143
+ echo -e "${RED}❌ Supabase CLI is not installed${NC}"
144
+ echo -e "${YELLOW}Install it with: ${CYAN}npm install -g supabase${NC}"
145
+ echo -e "${YELLOW}Or visit: ${CYAN}https://supabase.com/docs/guides/cli${NC}"
146
+ exit 1
147
+ fi
148
+ }
149
+
150
+ # Check if logged in to Supabase
151
+ check_supabase_login() {
152
+ if ! supabase projects list &> /dev/null; then
153
+ echo -e "${RED}❌ Not logged in to Supabase${NC}"
154
+ echo -e "${YELLOW}Please run: ${CYAN}supabase login${NC}"
155
+ exit 1
156
+ fi
157
+ }
158
+
159
+ # Setup command - link project
160
+ setup_project() {
161
+ echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
162
+ echo -e "${BOLD}Setting up Supabase User Management${NC}"
163
+ echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
164
+ echo
165
+
166
+ check_supabase_cli
167
+ check_supabase_login
168
+
169
+ # Get project ID
170
+ echo -e "${YELLOW}Enter your Supabase project ID (or press Enter for default):${NC}"
171
+ echo -e "${CYAN}Default: $PROJECT_ID${NC}"
172
+ read -p "> " input_project_id
173
+
174
+ if [ ! -z "$input_project_id" ]; then
175
+ PROJECT_ID="$input_project_id"
176
+ fi
177
+
178
+ # Link the project
179
+ echo -e "\n${BLUE}Linking to project...${NC}"
180
+ if supabase link --project-ref "$PROJECT_ID"; then
181
+ echo -e "${GREEN}✅ Successfully linked to project${NC}"
182
+
183
+ # Save configuration
184
+ echo "PROJECT_ID=\"$PROJECT_ID\"" > "$CONFIG_FILE"
185
+ echo -e "${GREEN}✅ Configuration saved${NC}"
186
+
187
+ # Test database connection
188
+ echo -e "\n${BLUE}Testing database connection...${NC}"
189
+ if supabase db push --dry-run <<< "SELECT 1;" &> /dev/null; then
190
+ echo -e "${GREEN}✅ Database connection successful${NC}"
191
+ else
192
+ echo -e "${YELLOW}⚠️ Could not verify database connection${NC}"
193
+ fi
194
+
195
+ echo -e "\n${GREEN}Setup complete! You can now manage users.${NC}"
196
+ else
197
+ echo -e "${RED}❌ Failed to link project${NC}"
198
+ echo -e "${YELLOW}Make sure the project ID is correct${NC}"
199
+ exit 1
200
+ fi
201
+ }
202
+
203
+ # Execute SQL using Supabase CLI
204
+ execute_sql() {
205
+ local sql="$1"
206
+ local temp_file=$(mktemp)
207
+
208
+ echo "$sql" > "$temp_file"
209
+
210
+ # Use db execute instead of db push for running queries
211
+ local output=$(supabase db execute -f "$temp_file" 2>&1)
212
+ local status=$?
213
+
214
+ rm "$temp_file"
215
+
216
+ if [ $status -eq 0 ]; then
217
+ echo "$output"
218
+ return 0
219
+ else
220
+ echo -e "${RED}SQL Error: $output${NC}" >&2
221
+ return 1
222
+ fi
223
+ }
224
+
225
+ # Create a user using SQL (Supabase doesn't have CLI user creation)
226
+ create_user() {
227
+ local email="$1"
228
+
229
+ echo -e "${BLUE}Checking/Creating user: $email${NC}"
230
+
231
+ # Check if user exists first
232
+ local check_sql="SELECT id, email FROM auth.users WHERE email = '$email';"
233
+
234
+ local result=$(execute_sql "$check_sql")
235
+
236
+ if echo "$result" | grep -q "$email"; then
237
+ echo -e "${YELLOW}ℹ️ User already exists${NC}"
238
+ return 0
239
+ else
240
+ echo -e "${YELLOW}⚠️ User doesn't exist yet${NC}"
241
+ echo -e "${CYAN}You have two options to create the user:${NC}"
242
+ echo
243
+ echo -e "${BOLD}Option 1: Use Supabase Dashboard (Recommended)${NC}"
244
+ echo -e "${CYAN}1. Go to: https://supabase.com/dashboard/project/$PROJECT_ID/auth/users${NC}"
245
+ echo -e "${CYAN}2. Click 'Invite user'${NC}"
246
+ echo -e "${CYAN}3. Enter email: $email${NC}"
247
+ echo
248
+ echo -e "${BOLD}Option 2: Use Service Role Key (Advanced)${NC}"
249
+ echo -e "${CYAN}If you have your service_role key, I can create the user programmatically.${NC}"
250
+ echo -e "${CYAN}Find it at: https://supabase.com/dashboard/project/$PROJECT_ID/settings/api${NC}"
251
+ echo
252
+ echo -e "${YELLOW}Choose an option:${NC}"
253
+ echo -e " 1) Open dashboard and create manually"
254
+ echo -e " 2) Enter service role key"
255
+ echo -e " 3) Skip (user already exists elsewhere)"
256
+ read -p "Choice (1/2/3): " choice
257
+
258
+ case $choice in
259
+ 1)
260
+ echo -e "${CYAN}Opening dashboard...${NC}"
261
+ if command -v open &> /dev/null; then
262
+ open "https://supabase.com/dashboard/project/$PROJECT_ID/auth/users"
263
+ fi
264
+ echo -e "${CYAN}Press Enter after creating the user...${NC}"
265
+ read -p ""
266
+ ;;
267
+ 2)
268
+ echo -e "${YELLOW}Enter your service_role key:${NC}"
269
+ read -s service_key
270
+ echo
271
+ if [ ! -z "$service_key" ]; then
272
+ echo -e "${BLUE}Creating user programmatically...${NC}"
273
+ # Get Supabase URL from config
274
+ local supabase_url="https://$PROJECT_ID.supabase.co"
275
+ if node "$SCRIPT_DIR/create-user.js" "$email" "$supabase_url" "$service_key" 2>&1; then
276
+ echo -e "${GREEN}✅ User created programmatically${NC}"
277
+ return 0
278
+ else
279
+ echo -e "${RED}❌ Failed to create user programmatically${NC}"
280
+ echo -e "${CYAN}Please try the dashboard method instead${NC}"
281
+ return 1
282
+ fi
283
+ fi
284
+ ;;
285
+ 3)
286
+ echo -e "${YELLOW}Skipping user creation...${NC}"
287
+ ;;
288
+ *)
289
+ echo -e "${RED}Invalid choice${NC}"
290
+ return 1
291
+ ;;
292
+ esac
293
+
294
+ # Check again if user was created
295
+ result=$(execute_sql "$check_sql")
296
+ if echo "$result" | grep -q "$email"; then
297
+ echo -e "${GREEN}✅ User confirmed in database${NC}"
298
+ return 0
299
+ else
300
+ echo -e "${RED}❌ User not found. Please create the user first.${NC}"
301
+ return 1
302
+ fi
303
+ fi
304
+ }
305
+
306
+ # Grant access to a site
307
+ grant_access() {
308
+ local site_url="$1"
309
+ local email="$2"
310
+
311
+ echo -e "${BLUE}Granting access to $site_url...${NC}"
312
+
313
+ local sql="
314
+ -- Get site info
315
+ DO \$\$
316
+ DECLARE
317
+ v_site_id UUID;
318
+ v_user_id UUID;
319
+ v_site_name TEXT;
320
+ BEGIN
321
+ -- Get site ID
322
+ SELECT id, name INTO v_site_id, v_site_name
323
+ FROM docbuilder_sites
324
+ WHERE domain = '$site_url';
325
+
326
+ IF v_site_id IS NULL THEN
327
+ RAISE EXCEPTION 'Site not found: $site_url';
328
+ END IF;
329
+
330
+ -- Get user ID
331
+ SELECT id INTO v_user_id
332
+ FROM auth.users
333
+ WHERE email = '$email';
334
+
335
+ IF v_user_id IS NULL THEN
336
+ RAISE EXCEPTION 'User not found: $email';
337
+ END IF;
338
+
339
+ -- Grant access
340
+ INSERT INTO docbuilder_access (user_id, site_id)
341
+ VALUES (v_user_id, v_site_id)
342
+ ON CONFLICT (user_id, site_id) DO NOTHING;
343
+
344
+ RAISE NOTICE 'Access granted to % for site: %', '$email', v_site_name;
345
+ END\$\$;
346
+
347
+ -- Show result
348
+ SELECT
349
+ u.email,
350
+ s.name as site_name,
351
+ s.domain as site_url,
352
+ da.created_at as access_granted
353
+ FROM docbuilder_access da
354
+ JOIN auth.users u ON u.id = da.user_id
355
+ JOIN docbuilder_sites s ON s.id = da.site_id
356
+ WHERE u.email = '$email' AND s.domain = '$site_url';
357
+ "
358
+
359
+ if execute_sql "$sql"; then
360
+ echo -e "${GREEN}✅ Access granted successfully${NC}"
361
+ return 0
362
+ else
363
+ echo -e "${RED}❌ Failed to grant access${NC}"
364
+ return 1
365
+ fi
366
+ }
367
+
368
+ # Add command - create user and grant access
369
+ add_user() {
370
+ local site_url="$1"
371
+ local email="$2"
372
+
373
+ echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
374
+ echo -e "${BOLD}Adding User${NC}"
375
+ echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
376
+ echo -e "Site: ${CYAN}$site_url${NC}"
377
+ echo -e "Email: ${CYAN}$email${NC}"
378
+ echo
379
+
380
+ # Create user first
381
+ if create_user "$email"; then
382
+ # Then grant access
383
+ grant_access "$site_url" "$email"
384
+ fi
385
+ }
386
+
387
+ # Bulk add users
388
+ bulk_add() {
389
+ local site_url="$1"
390
+ local file_path="$2"
391
+
392
+ if [ ! -f "$file_path" ]; then
393
+ echo -e "${RED}❌ File not found: $file_path${NC}"
394
+ exit 1
395
+ fi
396
+
397
+ echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
398
+ echo -e "${BOLD}Bulk Adding Users${NC}"
399
+ echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
400
+ echo -e "Site: ${CYAN}$site_url${NC}"
401
+ echo -e "File: ${CYAN}$file_path${NC}"
402
+ echo
403
+
404
+ local success_count=0
405
+ local fail_count=0
406
+
407
+ while IFS= read -r email; do
408
+ # Skip empty lines and comments
409
+ [[ -z "$email" || "$email" =~ ^#.*$ ]] && continue
410
+
411
+ # Trim whitespace
412
+ email=$(echo "$email" | xargs)
413
+
414
+ echo -e "\n${MAGENTA}Processing: $email${NC}"
415
+ echo -e "${CYAN}────────────────────────────────${NC}"
416
+
417
+ if create_user "$email" && grant_access "$site_url" "$email"; then
418
+ ((success_count++))
419
+ else
420
+ ((fail_count++))
421
+ fi
422
+ done < "$file_path"
423
+
424
+ echo -e "\n${BLUE}═══════════════════════════════════════════════════════════════${NC}"
425
+ echo -e "${BOLD}Summary${NC}"
426
+ echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
427
+ echo -e "${GREEN}✅ Successful: $success_count${NC}"
428
+ echo -e "${RED}❌ Failed: $fail_count${NC}"
429
+ }
430
+
431
+ # List users for a site
432
+ list_users() {
433
+ local site_url="$1"
434
+
435
+ echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
436
+ echo -e "${BOLD}Users with access to: $site_url${NC}"
437
+ echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
438
+
439
+ local sql="
440
+ -- Site info
441
+ SELECT name, domain, created_at
442
+ FROM docbuilder_sites
443
+ WHERE domain = '$site_url';
444
+
445
+ -- Users with access
446
+ SELECT
447
+ u.email,
448
+ u.created_at as user_created,
449
+ da.created_at as access_granted,
450
+ CASE
451
+ WHEN u.last_sign_in_at IS NULL THEN 'Never logged in'
452
+ ELSE 'Last login: ' || to_char(u.last_sign_in_at, 'YYYY-MM-DD HH24:MI')
453
+ END as login_status
454
+ FROM docbuilder_access da
455
+ JOIN auth.users u ON u.id = da.user_id
456
+ JOIN docbuilder_sites s ON s.id = da.site_id
457
+ WHERE s.domain = '$site_url'
458
+ ORDER BY da.created_at DESC;
459
+
460
+ -- Count
461
+ SELECT COUNT(*) as total_users
462
+ FROM docbuilder_access da
463
+ JOIN docbuilder_sites s ON s.id = da.site_id
464
+ WHERE s.domain = '$site_url';
465
+ "
466
+
467
+ execute_sql "$sql"
468
+ }
469
+
470
+ # Check user status
471
+ check_user() {
472
+ local email="$1"
473
+
474
+ echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
475
+ echo -e "${BOLD}User Status: $email${NC}"
476
+ echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
477
+
478
+ local sql="
479
+ -- User info
480
+ SELECT
481
+ id,
482
+ email,
483
+ created_at,
484
+ last_sign_in_at,
485
+ CASE
486
+ WHEN last_sign_in_at IS NULL THEN 'Never logged in'
487
+ ELSE 'Last login: ' || to_char(last_sign_in_at, 'YYYY-MM-DD HH24:MI')
488
+ END as login_status
489
+ FROM auth.users
490
+ WHERE email = '$email';
491
+
492
+ -- Sites with access
493
+ SELECT
494
+ s.name as site_name,
495
+ s.domain as site_url,
496
+ da.created_at as access_granted
497
+ FROM docbuilder_access da
498
+ JOIN auth.users u ON u.id = da.user_id
499
+ JOIN docbuilder_sites s ON s.id = da.site_id
500
+ WHERE u.email = '$email'
501
+ ORDER BY da.created_at DESC;
502
+
503
+ -- Count
504
+ SELECT COUNT(*) as total_sites
505
+ FROM docbuilder_access da
506
+ JOIN auth.users u ON u.id = da.user_id
507
+ WHERE u.email = '$email';
508
+ "
509
+
510
+ execute_sql "$sql"
511
+ }
512
+
513
+ # Remove user access
514
+ remove_access() {
515
+ local site_url="$1"
516
+ local email="$2"
517
+
518
+ echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
519
+ echo -e "${BOLD}Removing Access${NC}"
520
+ echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
521
+ echo -e "Site: ${CYAN}$site_url${NC}"
522
+ echo -e "Email: ${CYAN}$email${NC}"
523
+ echo
524
+
525
+ # Confirm
526
+ echo -e "${YELLOW}Are you sure you want to remove access? (y/N)${NC}"
527
+ read -p "> " confirm
528
+
529
+ if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
530
+ echo -e "${YELLOW}Cancelled${NC}"
531
+ return
532
+ fi
533
+
534
+ local sql="
535
+ -- Remove access
536
+ DELETE FROM docbuilder_access
537
+ WHERE user_id = (SELECT id FROM auth.users WHERE email = '$email')
538
+ AND site_id = (SELECT id FROM docbuilder_sites WHERE domain = '$site_url');
539
+
540
+ -- Verify removal
541
+ SELECT
542
+ CASE
543
+ WHEN COUNT(*) = 0 THEN 'Access removed successfully'
544
+ ELSE 'ERROR: User still has access'
545
+ END as status
546
+ FROM docbuilder_access da
547
+ JOIN auth.users u ON u.id = da.user_id
548
+ JOIN docbuilder_sites s ON s.id = da.site_id
549
+ WHERE u.email = '$email' AND s.domain = '$site_url';
550
+
551
+ -- Show remaining sites for this user
552
+ SELECT
553
+ s.name as site_name,
554
+ s.domain as site_url,
555
+ da.created_at as access_granted
556
+ FROM docbuilder_access da
557
+ JOIN auth.users u ON u.id = da.user_id
558
+ JOIN docbuilder_sites s ON s.id = da.site_id
559
+ WHERE u.email = '$email'
560
+ ORDER BY da.created_at DESC;
561
+ "
562
+
563
+ if execute_sql "$sql"; then
564
+ echo -e "${GREEN}✅ Access removed${NC}"
565
+ else
566
+ echo -e "${RED}❌ Failed to remove access${NC}"
567
+ fi
568
+ }
569
+
570
+ # List all sites
571
+ list_sites() {
572
+ echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
573
+ echo -e "${BOLD}All Documentation Sites${NC}"
574
+ echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
575
+
576
+ local sql="
577
+ SELECT
578
+ id as site_id,
579
+ domain,
580
+ name,
581
+ created_at,
582
+ (SELECT COUNT(*) FROM docbuilder_access WHERE site_id = docbuilder_sites.id) as user_count
583
+ FROM docbuilder_sites
584
+ ORDER BY created_at DESC;
585
+ "
586
+
587
+ execute_sql "$sql"
588
+ }
589
+
590
+ # Delete user completely
591
+ delete_user() {
592
+ local email="$1"
593
+
594
+ echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
595
+ echo -e "${BOLD}${RED}⚠️ REMOVE USER ACCESS - NOT DELETE ⚠️${NC}"
596
+ echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
597
+ echo -e "Email: ${CYAN}$email${NC}"
598
+ echo
599
+ echo -e "${YELLOW}Note: Supabase CLI cannot delete users directly.${NC}"
600
+ echo -e "${YELLOW}This will remove the user's access to ALL sites.${NC}"
601
+ echo -e "${YELLOW}To fully delete, use Supabase dashboard.${NC}"
602
+ echo
603
+ echo -e "${YELLOW}Type 'REMOVE' to confirm:${NC}"
604
+ read -p "> " confirm
605
+
606
+ if [[ "$confirm" != "REMOVE" ]]; then
607
+ echo -e "${YELLOW}Cancelled${NC}"
608
+ return
609
+ fi
610
+
611
+ echo -e "${BLUE}Removing all access...${NC}"
612
+
613
+ local sql="
614
+ -- Remove all access for user
615
+ DELETE FROM docbuilder_access
616
+ WHERE user_id = (SELECT id FROM auth.users WHERE email = '$email');
617
+
618
+ -- Show result
619
+ SELECT
620
+ CASE
621
+ WHEN COUNT(*) = 0 THEN 'All access removed successfully'
622
+ ELSE 'ERROR: User still has some access'
623
+ END as status
624
+ FROM docbuilder_access da
625
+ JOIN auth.users u ON u.id = da.user_id
626
+ WHERE u.email = '$email';
627
+ "
628
+
629
+ if execute_sql "$sql"; then
630
+ echo -e "${GREEN}✅ All access removed${NC}"
631
+ echo -e "${CYAN}To fully delete user, go to:${NC}"
632
+ echo -e "${CYAN}https://supabase.com/dashboard/project/$PROJECT_ID/auth/users${NC}"
633
+ else
634
+ echo -e "${RED}❌ Failed to remove access${NC}"
635
+ fi
636
+ }
637
+
638
+ # Main script logic
639
+ main() {
640
+ case "$1" in
641
+ setup)
642
+ setup_project
643
+ ;;
644
+
645
+ add)
646
+ if [ -z "$2" ] || [ -z "$3" ]; then
647
+ echo -e "${RED}❌ Missing parameters${NC}"
648
+ echo -e "${YELLOW}Usage: $0 add <site-url> <email>${NC}"
649
+ echo -e "${CYAN}Example: $0 add docs.example.com user@email.com${NC}"
650
+ exit 1
651
+ fi
652
+ check_supabase_cli
653
+ check_supabase_login
654
+ add_user "$2" "$3"
655
+ ;;
656
+
657
+ bulk)
658
+ if [ -z "$2" ] || [ -z "$3" ]; then
659
+ echo -e "${RED}❌ Missing parameters${NC}"
660
+ echo -e "${YELLOW}Usage: $0 bulk <site-url> <file>${NC}"
661
+ echo -e "${CYAN}Example: $0 bulk docs.example.com users.txt${NC}"
662
+ exit 1
663
+ fi
664
+ check_supabase_cli
665
+ check_supabase_login
666
+ bulk_add "$2" "$3"
667
+ ;;
668
+
669
+ list)
670
+ if [ -z "$2" ]; then
671
+ echo -e "${RED}❌ Missing site URL${NC}"
672
+ echo -e "${YELLOW}Usage: $0 list <site-url>${NC}"
673
+ echo -e "${CYAN}Example: $0 list docs.example.com${NC}"
674
+ exit 1
675
+ fi
676
+ check_supabase_cli
677
+ check_supabase_login
678
+ list_users "$2"
679
+ ;;
680
+
681
+ check)
682
+ if [ -z "$2" ]; then
683
+ echo -e "${RED}❌ Missing email${NC}"
684
+ echo -e "${YELLOW}Usage: $0 check <email>${NC}"
685
+ echo -e "${CYAN}Example: $0 check user@email.com${NC}"
686
+ exit 1
687
+ fi
688
+ check_supabase_cli
689
+ check_supabase_login
690
+ check_user "$2"
691
+ ;;
692
+
693
+ remove)
694
+ if [ -z "$2" ] || [ -z "$3" ]; then
695
+ echo -e "${RED}❌ Missing parameters${NC}"
696
+ echo -e "${YELLOW}Usage: $0 remove <site-url> <email>${NC}"
697
+ echo -e "${CYAN}Example: $0 remove docs.example.com user@email.com${NC}"
698
+ exit 1
699
+ fi
700
+ check_supabase_cli
701
+ check_supabase_login
702
+ remove_access "$2" "$3"
703
+ ;;
704
+
705
+ sites)
706
+ check_supabase_cli
707
+ check_supabase_login
708
+ list_sites
709
+ ;;
710
+
711
+ delete-user)
712
+ if [ -z "$2" ]; then
713
+ echo -e "${RED}❌ Missing email${NC}"
714
+ echo -e "${YELLOW}Usage: $0 delete-user <email>${NC}"
715
+ echo -e "${CYAN}Example: $0 delete-user user@email.com${NC}"
716
+ exit 1
717
+ fi
718
+ check_supabase_cli
719
+ check_supabase_login
720
+ delete_user "$2"
721
+ ;;
722
+
723
+ *)
724
+ show_help
725
+ ;;
726
+ esac
727
+ }
728
+
729
+ # Run main function
730
+ main "$@"