2025-04-25 05:28:13 +00:00
import { env } from "@/env.mjs" ;
import { getPlan , hasEntitlement } from "@/features/entitlements/server" ;
import { SINGLE_TENANT_ORG_ID , SOURCEBOT_SUPPORT_EMAIL } from "@/lib/constants" ;
import { prisma } from "@/prisma" ;
import { SearchContext } from "@sourcebot/schemas/v3/index.type" ;
import micromatch from "micromatch" ;
2025-06-02 18:16:01 +00:00
import { createLogger } from "@sourcebot/logger" ;
const logger = createLogger ( 'sync-search-contexts' ) ;
2025-04-25 05:28:13 +00:00
export const syncSearchContexts = async ( contexts ? : { [ key : string ] : SearchContext } ) = > {
if ( env . SOURCEBOT_TENANCY_MODE !== 'single' ) {
throw new Error ( "Search contexts are not supported in this tenancy mode. Set SOURCEBOT_TENANCY_MODE=single in your environment variables." ) ;
}
if ( ! hasEntitlement ( "search-contexts" ) ) {
if ( contexts ) {
const plan = getPlan ( ) ;
2025-06-02 18:16:01 +00:00
logger . error ( ` Search contexts are not supported in your current plan: ${ plan } . If you have a valid enterprise license key, pass it via SOURCEBOT_EE_LICENSE_KEY. For support, contact ${ SOURCEBOT_SUPPORT_EMAIL } . ` ) ;
2025-04-25 05:28:13 +00:00
}
return ;
}
if ( contexts ) {
for ( const [ key , newContextConfig ] of Object . entries ( contexts ) ) {
const allRepos = await prisma . repo . findMany ( {
where : {
orgId : SINGLE_TENANT_ORG_ID ,
} ,
select : {
id : true ,
name : true ,
}
} ) ;
let newReposInContext = allRepos . filter ( repo = > {
return micromatch . isMatch ( repo . name , newContextConfig . include ) ;
} ) ;
if ( newContextConfig . exclude ) {
const exclude = newContextConfig . exclude ;
newReposInContext = newReposInContext . filter ( repo = > {
return ! micromatch . isMatch ( repo . name , exclude ) ;
} ) ;
}
const currentReposInContext = ( await prisma . searchContext . findUnique ( {
where : {
name_orgId : {
name : key ,
orgId : SINGLE_TENANT_ORG_ID ,
}
} ,
include : {
repos : true ,
}
} ) ) ? . repos ? ? [ ] ;
await prisma . searchContext . upsert ( {
where : {
name_orgId : {
name : key ,
orgId : SINGLE_TENANT_ORG_ID ,
}
} ,
update : {
repos : {
connect : newReposInContext.map ( repo = > ( {
id : repo.id ,
} ) ) ,
disconnect : currentReposInContext
. filter ( repo = > ! newReposInContext . map ( r = > r . id ) . includes ( repo . id ) )
. map ( repo = > ( {
id : repo.id ,
} ) ) ,
} ,
description : newContextConfig.description ,
} ,
create : {
name : key ,
description : newContextConfig.description ,
org : {
connect : {
id : SINGLE_TENANT_ORG_ID ,
}
} ,
repos : {
connect : newReposInContext.map ( repo = > ( {
id : repo.id ,
} ) ) ,
}
}
} ) ;
}
}
const deletedContexts = await prisma . searchContext . findMany ( {
where : {
name : {
notIn : Object.keys ( contexts ? ? { } ) ,
} ,
orgId : SINGLE_TENANT_ORG_ID ,
}
} ) ;
for ( const context of deletedContexts ) {
2025-06-02 18:16:01 +00:00
logger . info ( ` Deleting search context with name ' ${ context . name } '. ID: ${ context . id } ` ) ;
2025-04-25 05:28:13 +00:00
await prisma . searchContext . delete ( {
where : {
id : context.id ,
}
} )
}
}