@@ -12,7 +12,7 @@ import { gitlabSchema } from "@sourcebot/schemas/v3/gitlab.schema";
1212import { ConnectionConfig } from "@sourcebot/schemas/v3/connection.type" ;
1313import { encrypt } from "@sourcebot/crypto"
1414import { getConnection } from "./data/connection" ;
15- import { ConnectionSyncStatus , Prisma , Invite } from "@sourcebot/db" ;
15+ import { ConnectionSyncStatus , Prisma , Invite , OrgRole } from "@sourcebot/db" ;
1616import { headers } from "next/headers"
1717import { getStripe } from "@/lib/stripe"
1818import { getUser } from "@/data/user" ;
@@ -58,6 +58,37 @@ export const withOrgMembership = async <T>(session: Session, domain: string, fn:
5858 return fn ( org . id ) ;
5959}
6060
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+
6192export const isAuthed = async ( ) => {
6293 const session = await auth ( ) ;
6394 return session != null ;
@@ -282,9 +313,29 @@ export const deleteConnection = async (connectionId: number, domain: string): Pr
282313 }
283314 } ) ) ;
284315
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 > =>
286317 withAuth ( ( session ) =>
287318 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 ) => {
288339 console . log ( "Creating invite for" , email , userId , orgId ) ;
289340
290341 if ( email === session . user . email ) {
@@ -377,6 +428,75 @@ export const redeemInvite = async (invite: Invite, userId: string): Promise<{ su
377428 }
378429 } ) ;
379430
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+
380500const parseConnectionConfig = ( connectionType : string , config : string ) => {
381501 let parsedConfig : ConnectionConfig ;
382502 try {
@@ -530,7 +650,7 @@ export async function fetchStripeSession(sessionId: string) {
530650
531651export const getCustomerPortalSessionLink = async ( domain : string ) : Promise < string | ServiceError > =>
532652 withAuth ( ( session ) =>
533- withOrgMembership ( session , domain , async ( orgId ) => {
653+ withOwner ( session , domain , async ( orgId ) => {
534654 const org = await prisma . org . findUnique ( {
535655 where : {
536656 id : orgId ,
@@ -574,6 +694,69 @@ export const fetchSubscription = (domain: string): Promise<Stripe.Subscription |
574694 return subscriptions . data [ 0 ] ;
575695 } ) ;
576696
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+
577760export const checkIfUserHasOrg = async ( userId : string ) : Promise < boolean | ServiceError > => {
578761 const orgs = await prisma . userToOrg . findMany ( {
579762 where : {
0 commit comments