better-auth-studio 1.0.14-beta.1 → 1.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,11 @@
1
- # Better Auth Studio
1
+ # Better Auth Studio
2
2
 
3
- A powerful web-based studio interface for managing Better Auth applications. Better Auth Studio provides a comprehensive dashboard for managing users, organizations, teams, sessions, and more.
3
+ > ⚠️ **Alpha Version Notice**
4
+ >
5
+ > Better Auth Studio is currently in **alpha** and in early development. You may encounter bugs or incomplete features. Please report any issues you find on our GitHub repository to help us improve the project. Your feedback is greatly appreciated!
6
+
7
+
8
+ A web-based studio interface for managing Better Auth applications. Better Auth Studio provides a comprehensive dashboard for managing users, organizations, teams, and more.
4
9
 
5
10
  ## 🚀 Quick Start
6
11
 
@@ -111,7 +116,7 @@ export const auth = betterAuth({
111
116
  - **Edit users** - Update user information, email verification status
112
117
  - **Delete users** - Remove users from the system
113
118
  - **Bulk operations** - Seed multiple test users
114
- - **User details** - View user profiles, sessions, and accounts
119
+ - **User details** - View user profiles, and accounts
115
120
 
116
121
  ### 🏢 Organization Management
117
122
  - **View organizations** - List all organizations with pagination
@@ -122,11 +127,6 @@ export const auth = betterAuth({
122
127
  - **Member management** - Add/remove members from teams
123
128
  - **Bulk seeding** - Generate test organizations and teams
124
129
 
125
- ### 🔐 Session Management
126
- - **Active sessions** - View all active user sessions
127
- - **Session details** - IP addresses, user agents, expiration times
128
- - **Session termination** - Revoke specific sessions
129
-
130
130
  ### ⚙️ Settings & Configuration
131
131
  - **Plugin status** - Check which Better Auth plugins are enabled
132
132
  - **Database configuration** - View current database adapter and settings
@@ -165,7 +165,7 @@ pnpx better-auth-studio --version
165
165
 
166
166
  # Show help
167
167
  pnpx better-auth-studio --help
168
-
168
+ ```
169
169
  ## 📝 Development
170
170
 
171
171
  ### Running from Source
@@ -199,10 +199,7 @@ MIT License - see [LICENSE](LICENSE) file for details.
199
199
 
200
200
  If you encounter any issues or have questions:
201
201
 
202
- 2. **Search existing issues** on GitHub
203
- 3. **Create a new issue** with detailed information
204
- 4. **Join our Discord** for community support
202
+ **Search existing issues** on GitHub
203
+ **Create a new issue** with detailed information
205
204
 
206
205
  ---
207
-
208
- **Happy coding with Better Auth Studio! 🚀**
package/dist/cli.js CHANGED
@@ -48,7 +48,7 @@ program
48
48
  program
49
49
  .command('start')
50
50
  .description('Start Better Auth Studio')
51
- .option('-p, --port <port>', 'Port to run the studio on', '3001')
51
+ .option('-p, --port <port>', 'Port to run the studio on', '3002')
52
52
  .option('-h, --host <host>', 'Host to run the studio on', 'localhost')
53
53
  .option('--no-open', 'Do not open browser automatically')
54
54
  .action(async (options) => {
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAoCzC,wBAAsB,oBAAoB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CA6J/E;AAwBD,wBAAgB,YAAY,CAAC,UAAU,EAAE,UAAU,8CAuxDlD"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAoCzC,wBAAsB,oBAAoB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CA6J/E;AAwBD,wBAAgB,YAAY,CAAC,UAAU,EAAE,UAAU,8CA6jElD"}
package/dist/routes.js CHANGED
@@ -420,6 +420,271 @@ export function createRoutes(authConfig) {
420
420
  res.status(500).json({ error: 'Failed to fetch users' });
421
421
  }
422
422
  });
423
+ router.get('/api/users/:userId', async (req, res) => {
424
+ try {
425
+ const { userId } = req.params;
426
+ const adapter = await getAuthAdapter();
427
+ if (!adapter || !adapter.findMany) {
428
+ return res.status(500).json({ error: 'Auth adapter not available' });
429
+ }
430
+ const users = await adapter.findMany({ model: 'user', limit: 10000 });
431
+ const user = users && users.length > 0 ? users.find((u) => u.id === userId) : null;
432
+ if (!user) {
433
+ return res.status(404).json({ error: 'User not found' });
434
+ }
435
+ res.json({ user });
436
+ }
437
+ catch (error) {
438
+ console.error('Error fetching user:', error);
439
+ res.status(500).json({ error: 'Failed to fetch user' });
440
+ }
441
+ });
442
+ router.put('/api/users/:userId', async (req, res) => {
443
+ try {
444
+ const { userId } = req.params;
445
+ const { name, email } = req.body;
446
+ const adapter = await getAuthAdapter();
447
+ if (!adapter || !adapter.update) {
448
+ return res.status(500).json({ error: 'Auth adapter not available' });
449
+ }
450
+ const user = await adapter.update({
451
+ model: 'user',
452
+ id: userId,
453
+ data: { name, email }
454
+ });
455
+ res.json({ success: true, user });
456
+ }
457
+ catch (error) {
458
+ console.error('Error updating user:', error);
459
+ res.status(500).json({ error: 'Failed to update user' });
460
+ }
461
+ });
462
+ router.delete('/api/users/:userId', async (req, res) => {
463
+ try {
464
+ const { userId } = req.params;
465
+ const adapter = await getAuthAdapter();
466
+ if (!adapter || !adapter.delete) {
467
+ return res.status(500).json({ error: 'Auth adapter not available' });
468
+ }
469
+ await adapter.delete({ model: 'user', id: userId });
470
+ res.json({ success: true });
471
+ }
472
+ catch (error) {
473
+ console.error('Error deleting user:', error);
474
+ res.status(500).json({ error: 'Failed to delete user' });
475
+ }
476
+ });
477
+ router.get('/api/users/:userId/organizations', async (req, res) => {
478
+ try {
479
+ const { userId } = req.params;
480
+ const adapter = await getAuthAdapter();
481
+ if (!adapter || !adapter.findMany) {
482
+ return res.status(500).json({ error: 'Auth adapter not available' });
483
+ }
484
+ const [memberships, organizations] = await Promise.all([
485
+ adapter.findMany({ model: 'member', limit: 10000 }),
486
+ adapter.findMany({ model: 'organization', limit: 10000 })
487
+ ]);
488
+ const userMemberships = memberships.filter((membership) => membership.userId === userId);
489
+ const formattedMemberships = userMemberships.map((membership) => {
490
+ const organization = organizations.find((org) => org.id === membership.organizationId);
491
+ return {
492
+ id: membership.id,
493
+ organization: organization ? {
494
+ id: organization.id,
495
+ name: organization.name || 'Unknown Organization',
496
+ slug: organization.slug || 'unknown',
497
+ image: organization.image,
498
+ createdAt: organization.createdAt
499
+ } : {
500
+ id: membership.organizationId,
501
+ name: 'Unknown Organization',
502
+ slug: 'unknown',
503
+ createdAt: membership.createdAt
504
+ },
505
+ role: membership.role || 'member',
506
+ joinedAt: membership.createdAt
507
+ };
508
+ });
509
+ res.json({ memberships: formattedMemberships });
510
+ }
511
+ catch (error) {
512
+ console.error('Error fetching user organizations:', error);
513
+ res.status(500).json({ error: 'Failed to fetch user organizations' });
514
+ }
515
+ });
516
+ router.get('/api/users/:userId/teams', async (req, res) => {
517
+ try {
518
+ const { userId } = req.params;
519
+ const adapter = await getAuthAdapter();
520
+ if (!adapter || !adapter.findMany) {
521
+ return res.status(500).json({ error: 'Auth adapter not available' });
522
+ }
523
+ const [memberships, teams, organizations] = await Promise.all([
524
+ adapter.findMany({ model: 'teamMember', limit: 10000 }),
525
+ adapter.findMany({ model: 'team', limit: 10000 }),
526
+ adapter.findMany({ model: 'organization', limit: 10000 })
527
+ ]);
528
+ const userMemberships = memberships.filter((membership) => membership.userId === userId);
529
+ const formattedMemberships = userMemberships.map((membership) => {
530
+ const team = teams.find((t) => t.id === membership.teamId);
531
+ const organization = team ? organizations.find((org) => org.id === team.organizationId) : null;
532
+ return {
533
+ id: membership.id,
534
+ team: team ? {
535
+ id: team.id,
536
+ name: team.name || 'Unknown Team',
537
+ organizationId: team.organizationId,
538
+ organizationName: organization ? organization.name || 'Unknown Organization' : 'Unknown Organization'
539
+ } : {
540
+ id: membership.teamId,
541
+ name: 'Unknown Team',
542
+ organizationId: 'unknown',
543
+ organizationName: 'Unknown Organization'
544
+ },
545
+ role: membership.role || 'member',
546
+ joinedAt: membership.createdAt
547
+ };
548
+ });
549
+ res.json({ memberships: formattedMemberships });
550
+ }
551
+ catch (error) {
552
+ console.error('Error fetching user teams:', error);
553
+ res.status(500).json({ error: 'Failed to fetch user teams' });
554
+ }
555
+ });
556
+ router.delete('/api/organizations/members/:membershipId', async (req, res) => {
557
+ try {
558
+ const { membershipId } = req.params;
559
+ const adapter = await getAuthAdapter();
560
+ if (!adapter || !adapter.delete) {
561
+ return res.status(500).json({ error: 'Auth adapter not available' });
562
+ }
563
+ await adapter.delete({ model: 'member', id: membershipId });
564
+ res.json({ success: true });
565
+ }
566
+ catch (error) {
567
+ console.error('Error removing user from organization:', error);
568
+ res.status(500).json({ error: 'Failed to remove user from organization' });
569
+ }
570
+ });
571
+ router.delete('/api/teams/members/:membershipId', async (req, res) => {
572
+ try {
573
+ const { membershipId } = req.params;
574
+ const adapter = await getAuthAdapter();
575
+ if (!adapter || !adapter.delete) {
576
+ return res.status(500).json({ error: 'Auth adapter not available' });
577
+ }
578
+ await adapter.delete({ model: 'teamMember', id: membershipId });
579
+ res.json({ success: true });
580
+ }
581
+ catch (error) {
582
+ console.error('Error removing user from team:', error);
583
+ res.status(500).json({ error: 'Failed to remove user from team' });
584
+ }
585
+ });
586
+ router.post('/api/users/:userId/ban', async (req, res) => {
587
+ try {
588
+ const { userId } = req.params;
589
+ const adapter = await getAuthAdapter();
590
+ if (!adapter || !adapter.update) {
591
+ return res.status(500).json({ error: 'Auth adapter not available' });
592
+ }
593
+ const user = await adapter.update({
594
+ model: 'user',
595
+ id: userId,
596
+ data: { banned: true }
597
+ });
598
+ res.json({ success: true, user });
599
+ }
600
+ catch (error) {
601
+ console.error('Error banning user:', error);
602
+ res.status(500).json({ error: 'Failed to ban user' });
603
+ }
604
+ });
605
+ router.get('/api/users/:userId/sessions', async (req, res) => {
606
+ try {
607
+ const { userId } = req.params;
608
+ const adapter = await getAuthAdapter();
609
+ if (!adapter || !adapter.findMany) {
610
+ return res.status(500).json({ error: 'Auth adapter not available' });
611
+ }
612
+ const sessions = await adapter.findMany({
613
+ model: 'session',
614
+ limit: 10000
615
+ });
616
+ const userSessions = sessions.filter((session) => session.userId === userId);
617
+ const formattedSessions = userSessions.map((session) => ({
618
+ id: session.id,
619
+ token: session.token,
620
+ expiresAt: session.expiresAt,
621
+ ipAddress: session.ipAddress || 'Unknown',
622
+ userAgent: session.userAgent || 'Unknown',
623
+ activeOrganizationId: session.activeOrganizationId,
624
+ activeTeamId: session.activeTeamId,
625
+ createdAt: session.createdAt,
626
+ updatedAt: session.updatedAt
627
+ }));
628
+ res.json({ sessions: formattedSessions });
629
+ }
630
+ catch (error) {
631
+ console.error('Error fetching user sessions:', error);
632
+ res.status(500).json({ error: 'Failed to fetch user sessions' });
633
+ }
634
+ });
635
+ router.delete('/api/sessions/:sessionId', async (req, res) => {
636
+ try {
637
+ const { sessionId } = req.params;
638
+ const adapter = await getAuthAdapter();
639
+ if (!adapter || !adapter.delete) {
640
+ return res.status(500).json({ error: 'Auth adapter not available' });
641
+ }
642
+ await adapter.delete({ model: 'session', id: sessionId });
643
+ res.json({ success: true });
644
+ }
645
+ catch (error) {
646
+ console.error('Error deleting session:', error);
647
+ res.status(500).json({ error: 'Failed to delete session' });
648
+ }
649
+ });
650
+ router.get('/api/teams/:teamId', async (req, res) => {
651
+ try {
652
+ const { teamId } = req.params;
653
+ const adapter = await getAuthAdapter();
654
+ if (!adapter || !adapter.findMany) {
655
+ return res.status(500).json({ error: 'Auth adapter not available' });
656
+ }
657
+ const teams = await adapter.findMany({ model: 'team', limit: 10000 });
658
+ const team = teams.find((t) => t.id === teamId);
659
+ if (!team) {
660
+ return res.status(404).json({ error: 'Team not found' });
661
+ }
662
+ res.json({ team });
663
+ }
664
+ catch (error) {
665
+ console.error('Error fetching team:', error);
666
+ res.status(500).json({ error: 'Failed to fetch team' });
667
+ }
668
+ });
669
+ router.get('/api/organizations/:orgId', async (req, res) => {
670
+ try {
671
+ const { orgId } = req.params;
672
+ const adapter = await getAuthAdapter();
673
+ if (!adapter || !adapter.findMany) {
674
+ return res.status(500).json({ error: 'Auth adapter not available' });
675
+ }
676
+ const organizations = await adapter.findMany({ model: 'organization', limit: 10000 });
677
+ const organization = organizations.find((org) => org.id === orgId);
678
+ if (!organization) {
679
+ return res.status(404).json({ error: 'Organization not found' });
680
+ }
681
+ res.json({ organization });
682
+ }
683
+ catch (error) {
684
+ console.error('Error fetching organization:', error);
685
+ res.status(500).json({ error: 'Failed to fetch organization' });
686
+ }
687
+ });
423
688
  router.get('/api/users', async (req, res) => {
424
689
  try {
425
690
  const page = parseInt(req.query.page) || 1;