@knowcode/doc-builder 1.8.0 → 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 (47) hide show
  1. package/.claude/settings.local.json +4 -1
  2. package/CHANGELOG.md +7 -0
  3. package/assets/js/main.js +10 -6
  4. package/html/README.html +43 -5
  5. package/html/auth.js +62 -13
  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 +43 -5
  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 +237 -117
  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 +4 -4
  23. package/html/logout.html +2 -2
  24. package/html/private/cache-control-anti-pattern.html +66 -5
  25. package/html/private/launch/README.html +66 -5
  26. package/html/private/launch/auth-cleanup-summary.html +66 -5
  27. package/html/private/launch/bubble-plugin-specification.html +66 -5
  28. package/html/private/launch/go-to-market-strategy.html +66 -5
  29. package/html/private/launch/launch-announcements.html +66 -5
  30. package/html/private/launch/vercel-deployment-auth-setup.html +66 -5
  31. package/html/private/next-steps-walkthrough.html +66 -5
  32. package/html/private/supabase-auth-implementation-completed.html +66 -5
  33. package/html/private/supabase-auth-implementation-plan.html +66 -5
  34. package/html/private/supabase-auth-integration-plan.html +66 -5
  35. package/html/private/supabase-auth-setup-guide.html +66 -5
  36. package/html/private/test-private-doc.html +66 -5
  37. package/html/robots.txt +4 -0
  38. package/html/sitemap.xml +43 -43
  39. package/html/vercel-cli-setup-guide.html +43 -5
  40. package/html/vercel-first-time-setup-guide.html +43 -5
  41. package/lib/config.js +15 -21
  42. package/lib/core-builder.js +9 -1
  43. package/lib/supabase-auth.js +20 -14
  44. package/package.json +1 -1
  45. package/user-management/README.md +276 -51
  46. package/user-management/add-users.sh +635 -262
  47. 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,341 @@ 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 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
64
223
  }
65
224
 
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
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
105
348
  SELECT
106
349
  u.email,
107
350
  s.name as site_name,
@@ -110,57 +353,130 @@ SELECT
110
353
  FROM docbuilder_access da
111
354
  JOIN auth.users u ON u.id = da.user_id
112
355
  JOIN docbuilder_sites s ON s.id = da.site_id
113
- WHERE u.email = '${email}' AND s.domain = '${site_url}';
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
+ }
114
367
 
115
- EOF
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
116
385
  }
117
386
 
118
- # Generate SQL for listing users
119
- generate_list_sql() {
120
- local site_url=$1
121
- cat << EOF
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
+ }
122
430
 
123
- -- =====================================================
124
- -- LIST ALL USERS WITH ACCESS
125
- -- Site: ${site_url}
126
- -- Generated: $(date)
127
- -- =====================================================
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';
128
444
 
445
+ -- Users with access
129
446
  SELECT
130
447
  u.email,
131
448
  u.created_at as user_created,
132
449
  da.created_at as access_granted,
133
450
  CASE
134
451
  WHEN u.last_sign_in_at IS NULL THEN 'Never logged in'
135
- ELSE 'Last login: ' || u.last_sign_in_at::text
452
+ ELSE 'Last login: ' || to_char(u.last_sign_in_at, 'YYYY-MM-DD HH24:MI')
136
453
  END as login_status
137
454
  FROM docbuilder_access da
138
455
  JOIN auth.users u ON u.id = da.user_id
139
456
  JOIN docbuilder_sites s ON s.id = da.site_id
140
- WHERE s.domain = '${site_url}'
457
+ WHERE s.domain = '$site_url'
141
458
  ORDER BY da.created_at DESC;
142
459
 
143
- -- Site info
144
- SELECT name, domain, created_at
145
- FROM docbuilder_sites
146
- WHERE domain = '${site_url}';
147
-
148
- EOF
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"
149
468
  }
150
469
 
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
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
164
480
  SELECT
165
481
  id,
166
482
  email,
@@ -168,52 +484,58 @@ SELECT
168
484
  last_sign_in_at,
169
485
  CASE
170
486
  WHEN last_sign_in_at IS NULL THEN 'Never logged in'
171
- ELSE 'Last login: ' || last_sign_in_at::text
487
+ ELSE 'Last login: ' || to_char(last_sign_in_at, 'YYYY-MM-DD HH24:MI')
172
488
  END as login_status
173
489
  FROM auth.users
174
- WHERE email = '${email}';
490
+ WHERE email = '$email';
175
491
 
176
- -- Check if user has access to this site
492
+ -- Sites with access
177
493
  SELECT
178
494
  s.name as site_name,
179
495
  s.domain as site_url,
180
- 'Has Access' as status,
181
496
  da.created_at as access_granted
182
497
  FROM docbuilder_access da
183
498
  JOIN auth.users u ON u.id = da.user_id
184
499
  JOIN docbuilder_sites s ON s.id = da.site_id
185
- WHERE u.email = '${email}' AND s.domain = '${site_url}';
500
+ WHERE u.email = '$email'
501
+ ORDER BY da.created_at DESC;
186
502
 
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
503
+ -- Count
504
+ SELECT COUNT(*) as total_sites
192
505
  FROM docbuilder_access da
193
506
  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
507
+ WHERE u.email = '$email';
508
+ "
509
+
510
+ execute_sql "$sql"
199
511
  }
200
512
 
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)
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
214
536
  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}');
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');
217
539
 
218
540
  -- Verify removal
219
541
  SELECT
@@ -224,7 +546,7 @@ SELECT
224
546
  FROM docbuilder_access da
225
547
  JOIN auth.users u ON u.id = da.user_id
226
548
  JOIN docbuilder_sites s ON s.id = da.site_id
227
- WHERE u.email = '${email}' AND s.domain = '${site_url}';
549
+ WHERE u.email = '$email' AND s.domain = '$site_url';
228
550
 
229
551
  -- Show remaining sites for this user
230
552
  SELECT
@@ -234,124 +556,175 @@ SELECT
234
556
  FROM docbuilder_access da
235
557
  JOIN auth.users u ON u.id = da.user_id
236
558
  JOIN docbuilder_sites s ON s.id = da.site_id
237
- WHERE u.email = '${email}'
559
+ WHERE u.email = '$email'
238
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
+ }
239
569
 
240
- EOF
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
241
636
  }
242
637
 
243
638
  # 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
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 "$@"