@knowcode/doc-builder 1.8.0 → 1.8.2

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