@@ -12,7 +12,7 @@ import { gitlabSchema } from "@sourcebot/schemas/v3/gitlab.schema";
12
12
import { ConnectionConfig } from "@sourcebot/schemas/v3/connection.type" ;
13
13
import { encrypt } from "@sourcebot/crypto"
14
14
import { getConnection } from "./data/connection" ;
15
- import { ConnectionSyncStatus , Prisma , Invite } from "@sourcebot/db" ;
15
+ import { ConnectionSyncStatus , Prisma , Invite , OrgRole } from "@sourcebot/db" ;
16
16
import { headers } from "next/headers"
17
17
import { getStripe } from "@/lib/stripe"
18
18
import { getUser } from "@/data/user" ;
@@ -58,6 +58,37 @@ export const withOrgMembership = async <T>(session: Session, domain: string, fn:
58
58
return fn ( org . id ) ;
59
59
}
60
60
61
+ export const withOwner = async < T > ( session : Session , domain : string , fn : ( orgId : number ) => Promise < T > ) => {
62
+ const org = await prisma . org . findUnique ( {
63
+ where : {
64
+ domain,
65
+ } ,
66
+ } ) ;
67
+
68
+ if ( ! org ) {
69
+ return notFound ( ) ;
70
+ }
71
+
72
+ const userRole = await prisma . userToOrg . findUnique ( {
73
+ where : {
74
+ orgId_userId : {
75
+ orgId : org . id ,
76
+ userId : session . user . id ,
77
+ } ,
78
+ } ,
79
+ } ) ;
80
+
81
+ if ( ! userRole || userRole . role !== OrgRole . OWNER ) {
82
+ return {
83
+ statusCode : StatusCodes . FORBIDDEN ,
84
+ errorCode : ErrorCode . MEMBER_NOT_OWNER ,
85
+ message : "Only org owners can perform this action" ,
86
+ } satisfies ServiceError ;
87
+ }
88
+
89
+ return fn ( org . id ) ;
90
+ }
91
+
61
92
export const isAuthed = async ( ) => {
62
93
const session = await auth ( ) ;
63
94
return session != null ;
@@ -282,9 +313,29 @@ export const deleteConnection = async (connectionId: number, domain: string): Pr
282
313
}
283
314
} ) ) ;
284
315
285
- export const createInvite = async ( email : string , userId : string , domain : string ) : Promise < { success : boolean } | ServiceError > =>
316
+ export const getCurrentUserRole = async ( domain : string ) : Promise < OrgRole | ServiceError > =>
286
317
withAuth ( ( session ) =>
287
318
withOrgMembership ( session , domain , async ( orgId ) => {
319
+ const userRole = await prisma . userToOrg . findUnique ( {
320
+ where : {
321
+ orgId_userId : {
322
+ orgId,
323
+ userId : session . user . id ,
324
+ } ,
325
+ } ,
326
+ } ) ;
327
+
328
+ if ( ! userRole ) {
329
+ return notFound ( ) ;
330
+ }
331
+
332
+ return userRole . role ;
333
+ } )
334
+ ) ;
335
+
336
+ export const createInvite = async ( email : string , userId : string , domain : string ) : Promise < { success : boolean } | ServiceError > =>
337
+ withAuth ( ( session ) =>
338
+ withOwner ( session , domain , async ( orgId ) => {
288
339
console . log ( "Creating invite for" , email , userId , orgId ) ;
289
340
290
341
if ( email === session . user . email ) {
@@ -377,6 +428,75 @@ export const redeemInvite = async (invite: Invite, userId: string): Promise<{ su
377
428
}
378
429
} ) ;
379
430
431
+ export const makeOwner = async ( newOwnerId : string , domain : string ) : Promise < { success : boolean } | ServiceError > =>
432
+ withAuth ( ( session ) =>
433
+ withOwner ( session , domain , async ( orgId ) => {
434
+ const currentUserId = session . user . id ;
435
+ const currentUserRole = await prisma . userToOrg . findUnique ( {
436
+ where : {
437
+ orgId_userId : {
438
+ userId : currentUserId ,
439
+ orgId,
440
+ } ,
441
+ } ,
442
+ } ) ;
443
+
444
+ if ( newOwnerId === currentUserId ) {
445
+ return {
446
+ statusCode : StatusCodes . BAD_REQUEST ,
447
+ errorCode : ErrorCode . INVALID_REQUEST_BODY ,
448
+ message : "You're already the owner of this org" ,
449
+ } satisfies ServiceError ;
450
+ }
451
+
452
+ const newOwner = await prisma . userToOrg . findUnique ( {
453
+ where : {
454
+ orgId_userId : {
455
+ userId : newOwnerId ,
456
+ orgId,
457
+ } ,
458
+ } ,
459
+ } ) ;
460
+
461
+ if ( ! newOwner ) {
462
+ return {
463
+ statusCode : StatusCodes . BAD_REQUEST ,
464
+ errorCode : ErrorCode . INVALID_REQUEST_BODY ,
465
+ message : "The user you're trying to make the owner doesn't exist" ,
466
+ } satisfies ServiceError ;
467
+ }
468
+
469
+ await prisma . $transaction ( [
470
+ prisma . userToOrg . update ( {
471
+ where : {
472
+ orgId_userId : {
473
+ userId : newOwnerId ,
474
+ orgId,
475
+ } ,
476
+ } ,
477
+ data : {
478
+ role : "OWNER" ,
479
+ }
480
+ } ) ,
481
+ prisma . userToOrg . update ( {
482
+ where : {
483
+ orgId_userId : {
484
+ userId : currentUserId ,
485
+ orgId,
486
+ } ,
487
+ } ,
488
+ data : {
489
+ role : "MEMBER" ,
490
+ }
491
+ } )
492
+ ] ) ;
493
+
494
+ return {
495
+ success : true ,
496
+ }
497
+ } )
498
+ ) ;
499
+
380
500
const parseConnectionConfig = ( connectionType : string , config : string ) => {
381
501
let parsedConfig : ConnectionConfig ;
382
502
try {
@@ -530,7 +650,7 @@ export async function fetchStripeSession(sessionId: string) {
530
650
531
651
export const getCustomerPortalSessionLink = async ( domain : string ) : Promise < string | ServiceError > =>
532
652
withAuth ( ( session ) =>
533
- withOrgMembership ( session , domain , async ( orgId ) => {
653
+ withOwner ( session , domain , async ( orgId ) => {
534
654
const org = await prisma . org . findUnique ( {
535
655
where : {
536
656
id : orgId ,
@@ -574,6 +694,69 @@ export const fetchSubscription = (domain: string): Promise<Stripe.Subscription |
574
694
return subscriptions . data [ 0 ] ;
575
695
} ) ;
576
696
697
+ export const getSubscriptionBillingEmail = async ( domain : string ) : Promise < string | ServiceError > =>
698
+ withAuth ( async ( session ) =>
699
+ withOrgMembership ( session , domain , async ( orgId ) => {
700
+ const org = await prisma . org . findUnique ( {
701
+ where : {
702
+ id : orgId ,
703
+ } ,
704
+ } ) ;
705
+
706
+ if ( ! org || ! org . stripeCustomerId ) {
707
+ return notFound ( ) ;
708
+ }
709
+
710
+ const stripe = getStripe ( ) ;
711
+ const customer = await stripe . customers . retrieve ( org . stripeCustomerId ) ;
712
+ if ( ! ( 'email' in customer ) || customer . deleted ) {
713
+ return notFound ( ) ;
714
+ }
715
+ return customer . email ! ;
716
+ } )
717
+ ) ;
718
+
719
+ export const changeSubscriptionBillingEmail = async ( domain : string , newEmail : string ) : Promise < { success : boolean } | ServiceError > =>
720
+ withAuth ( ( session ) =>
721
+ withOrgMembership ( session , domain , async ( orgId ) => {
722
+ const userRole = await prisma . userToOrg . findUnique ( {
723
+ where : {
724
+ orgId_userId : {
725
+ orgId,
726
+ userId : session . user . id ,
727
+ }
728
+ }
729
+ } ) ;
730
+
731
+ if ( ! userRole || userRole . role !== "OWNER" ) {
732
+ return {
733
+ statusCode : StatusCodes . FORBIDDEN ,
734
+ errorCode : ErrorCode . MEMBER_NOT_OWNER ,
735
+ message : "Only org owners can change billing email" ,
736
+ } satisfies ServiceError ;
737
+ }
738
+
739
+ const org = await prisma . org . findUnique ( {
740
+ where : {
741
+ id : orgId ,
742
+ } ,
743
+ } ) ;
744
+
745
+ if ( ! org || ! org . stripeCustomerId ) {
746
+ return notFound ( ) ;
747
+ }
748
+
749
+ const stripe = getStripe ( ) ;
750
+ await stripe . customers . update ( org . stripeCustomerId , {
751
+ email : newEmail ,
752
+ } ) ;
753
+
754
+ return {
755
+ success : true ,
756
+ }
757
+ } )
758
+ ) ;
759
+
577
760
export const checkIfUserHasOrg = async ( userId : string ) : Promise < boolean | ServiceError > => {
578
761
const orgs = await prisma . userToOrg . findMany ( {
579
762
where : {
0 commit comments