mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-13 21:05:22 +00:00
move feature to EE
This commit is contained in:
parent
c7e2f5ae06
commit
0e527f4e08
10 changed files with 44 additions and 22 deletions
|
|
@ -2,7 +2,7 @@ Copyright (c) 2025 Taqla Inc.
|
||||||
|
|
||||||
Portions of this software are licensed as follows:
|
Portions of this software are licensed as follows:
|
||||||
|
|
||||||
- All content that resides under the "ee/", "packages/web/src/ee/", and "packages/shared/src/ee/" directories of this repository, if these directories exist, is licensed under the license defined in "ee/LICENSE".
|
- All content that resides under the "ee/", "packages/web/src/ee/", "packages/backend/src/ee/", and "packages/shared/src/ee/" directories of this repository, if these directories exist, is licensed under the license defined in "ee/LICENSE".
|
||||||
- All third party components incorporated into the Sourcebot Software are licensed under the original license provided by the owner of the applicable component.
|
- All third party components incorporated into the Sourcebot Software are licensed under the original license provided by the owner of the applicable component.
|
||||||
- Content outside of the above mentioned directories or restrictions above is available under the "Functional Source License" as defined below.
|
- Content outside of the above mentioned directories or restrictions above is available under the "Functional Source License" as defined below.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,11 @@ import { GithubConnectionConfig } from "@sourcebot/schemas/v3/github.type";
|
||||||
import { GitlabConnectionConfig } from "@sourcebot/schemas/v3/gitlab.type";
|
import { GitlabConnectionConfig } from "@sourcebot/schemas/v3/gitlab.type";
|
||||||
import { Job, Queue, Worker } from 'bullmq';
|
import { Job, Queue, Worker } from 'bullmq';
|
||||||
import { Redis } from 'ioredis';
|
import { Redis } from 'ioredis';
|
||||||
import { env } from "./env.js";
|
import { env } from "../env.js";
|
||||||
import { createOctokitFromConfig, getUserIdsWithReadAccessToRepo } from "./github.js";
|
import { createOctokitFromConfig, getUserIdsWithReadAccessToRepo } from "../github.js";
|
||||||
import { RepoWithConnections } from "./types.js";
|
import { RepoWithConnections } from "../types.js";
|
||||||
import { PERMISSION_SYNC_SUPPORTED_CODE_HOST_TYPES } from "./constants.js";
|
import { PERMISSION_SYNC_SUPPORTED_CODE_HOST_TYPES } from "../constants.js";
|
||||||
|
import { hasEntitlement } from "@sourcebot/shared";
|
||||||
|
|
||||||
type RepoPermissionSyncJob = {
|
type RepoPermissionSyncJob = {
|
||||||
jobId: string;
|
jobId: string;
|
||||||
|
|
@ -41,6 +42,10 @@ export class RepoPermissionSyncer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public startScheduler() {
|
public startScheduler() {
|
||||||
|
if (!hasEntitlement('permission-syncing')) {
|
||||||
|
throw new Error('Permission syncing is not supported in current plan.');
|
||||||
|
}
|
||||||
|
|
||||||
logger.debug('Starting scheduler');
|
logger.debug('Starting scheduler');
|
||||||
|
|
||||||
return setInterval(async () => {
|
return setInterval(async () => {
|
||||||
|
|
@ -4,9 +4,10 @@ import { PrismaClient, User, UserPermissionSyncJobStatus } from "@sourcebot/db";
|
||||||
import { createLogger } from "@sourcebot/logger";
|
import { createLogger } from "@sourcebot/logger";
|
||||||
import { Job, Queue, Worker } from "bullmq";
|
import { Job, Queue, Worker } from "bullmq";
|
||||||
import { Redis } from "ioredis";
|
import { Redis } from "ioredis";
|
||||||
import { PERMISSION_SYNC_SUPPORTED_CODE_HOST_TYPES } from "./constants.js";
|
import { PERMISSION_SYNC_SUPPORTED_CODE_HOST_TYPES } from "../constants.js";
|
||||||
import { env } from "./env.js";
|
import { env } from "../env.js";
|
||||||
import { getReposThatAuthenticatedUserHasReadAccessTo } from "./github.js";
|
import { getReposThatAuthenticatedUserHasReadAccessTo } from "../github.js";
|
||||||
|
import { hasEntitlement } from "@sourcebot/shared";
|
||||||
|
|
||||||
const logger = createLogger('user-permission-syncer');
|
const logger = createLogger('user-permission-syncer');
|
||||||
|
|
||||||
|
|
@ -37,6 +38,10 @@ export class UserPermissionSyncer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public startScheduler() {
|
public startScheduler() {
|
||||||
|
if (!hasEntitlement('permission-syncing')) {
|
||||||
|
throw new Error('Permission syncing is not supported in current plan.');
|
||||||
|
}
|
||||||
|
|
||||||
logger.debug('Starting scheduler');
|
logger.debug('Starting scheduler');
|
||||||
|
|
||||||
return setInterval(async () => {
|
return setInterval(async () => {
|
||||||
|
|
@ -53,7 +53,7 @@ export const env = createEnv({
|
||||||
|
|
||||||
GITLAB_CLIENT_QUERY_TIMEOUT_SECONDS: numberSchema.default(60 * 10),
|
GITLAB_CLIENT_QUERY_TIMEOUT_SECONDS: numberSchema.default(60 * 10),
|
||||||
|
|
||||||
EXPERIMENT_PERMISSION_SYNC_ENABLED: booleanSchema.default("false"),
|
EXPERIMENT_EE_PERMISSION_SYNC_ENABLED: booleanSchema.default('false'),
|
||||||
},
|
},
|
||||||
runtimeEnv: process.env,
|
runtimeEnv: process.env,
|
||||||
emptyStringAsUndefined: true,
|
emptyStringAsUndefined: true,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import "./instrument.js";
|
||||||
|
|
||||||
import { PrismaClient } from "@sourcebot/db";
|
import { PrismaClient } from "@sourcebot/db";
|
||||||
import { createLogger } from "@sourcebot/logger";
|
import { createLogger } from "@sourcebot/logger";
|
||||||
import { loadConfig } from '@sourcebot/shared';
|
import { hasEntitlement, loadConfig } from '@sourcebot/shared';
|
||||||
import { existsSync } from 'fs';
|
import { existsSync } from 'fs';
|
||||||
import { mkdir } from 'fs/promises';
|
import { mkdir } from 'fs/promises';
|
||||||
import { Redis } from 'ioredis';
|
import { Redis } from 'ioredis';
|
||||||
|
|
@ -10,11 +10,11 @@ import path from 'path';
|
||||||
import { ConnectionManager } from './connectionManager.js';
|
import { ConnectionManager } from './connectionManager.js';
|
||||||
import { DEFAULT_SETTINGS } from './constants.js';
|
import { DEFAULT_SETTINGS } from './constants.js';
|
||||||
import { env } from "./env.js";
|
import { env } from "./env.js";
|
||||||
import { RepoPermissionSyncer } from './repoPermissionSyncer.js';
|
import { RepoPermissionSyncer } from './ee/repoPermissionSyncer.js';
|
||||||
import { PromClient } from './promClient.js';
|
import { PromClient } from './promClient.js';
|
||||||
import { RepoManager } from './repoManager.js';
|
import { RepoManager } from './repoManager.js';
|
||||||
import { AppContext } from "./types.js";
|
import { AppContext } from "./types.js";
|
||||||
import { UserPermissionSyncer } from "./userPermissionSyncer.js";
|
import { UserPermissionSyncer } from "./ee/userPermissionSyncer.js";
|
||||||
|
|
||||||
|
|
||||||
const logger = createLogger('backend-entrypoint');
|
const logger = createLogger('backend-entrypoint');
|
||||||
|
|
@ -76,9 +76,18 @@ await repoManager.validateIndexedReposHaveShards();
|
||||||
|
|
||||||
const connectionManagerInterval = connectionManager.startScheduler();
|
const connectionManagerInterval = connectionManager.startScheduler();
|
||||||
const repoManagerInterval = repoManager.startScheduler();
|
const repoManagerInterval = repoManager.startScheduler();
|
||||||
const repoPermissionSyncerInterval = env.EXPERIMENT_PERMISSION_SYNC_ENABLED === 'true' ? repoPermissionSyncer.startScheduler() : null;
|
|
||||||
const userPermissionSyncerInterval = env.EXPERIMENT_PERMISSION_SYNC_ENABLED === 'true' ? userPermissionSyncer.startScheduler() : null;
|
|
||||||
|
|
||||||
|
let repoPermissionSyncerInterval: NodeJS.Timeout | null = null;
|
||||||
|
let userPermissionSyncerInterval: NodeJS.Timeout | null = null;
|
||||||
|
|
||||||
|
if (env.EXPERIMENT_EE_PERMISSION_SYNC_ENABLED === 'true' && !hasEntitlement('permission-syncing')) {
|
||||||
|
logger.error('Permission syncing is not supported in current plan. Please contact support@sourcebot.dev for assistance.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
else if (env.EXPERIMENT_EE_PERMISSION_SYNC_ENABLED === 'true' && hasEntitlement('permission-syncing')) {
|
||||||
|
repoPermissionSyncerInterval = repoPermissionSyncer.startScheduler();
|
||||||
|
userPermissionSyncerInterval = userPermissionSyncer.startScheduler();
|
||||||
|
}
|
||||||
|
|
||||||
const cleanup = async (signal: string) => {
|
const cleanup = async (signal: string) => {
|
||||||
logger.info(`Recieved ${signal}, cleaning up...`);
|
logger.info(`Recieved ${signal}, cleaning up...`);
|
||||||
|
|
|
||||||
|
|
@ -38,15 +38,16 @@ const entitlements = [
|
||||||
"sso",
|
"sso",
|
||||||
"code-nav",
|
"code-nav",
|
||||||
"audit",
|
"audit",
|
||||||
"analytics"
|
"analytics",
|
||||||
|
"permission-syncing"
|
||||||
] as const;
|
] as const;
|
||||||
export type Entitlement = (typeof entitlements)[number];
|
export type Entitlement = (typeof entitlements)[number];
|
||||||
|
|
||||||
const entitlementsByPlan: Record<Plan, Entitlement[]> = {
|
const entitlementsByPlan: Record<Plan, Entitlement[]> = {
|
||||||
oss: ["anonymous-access"],
|
oss: ["anonymous-access"],
|
||||||
"cloud:team": ["billing", "multi-tenancy", "sso", "code-nav"],
|
"cloud:team": ["billing", "multi-tenancy", "sso", "code-nav"],
|
||||||
"self-hosted:enterprise": ["search-contexts", "sso", "code-nav", "audit", "analytics"],
|
"self-hosted:enterprise": ["search-contexts", "sso", "code-nav", "audit", "analytics", "permission-syncing"],
|
||||||
"self-hosted:enterprise-unlimited": ["search-contexts", "anonymous-access", "sso", "code-nav", "audit", "analytics"],
|
"self-hosted:enterprise-unlimited": ["search-contexts", "anonymous-access", "sso", "code-nav", "audit", "analytics", "permission-syncing"],
|
||||||
// Special entitlement for https://demo.sourcebot.dev
|
// Special entitlement for https://demo.sourcebot.dev
|
||||||
"cloud:demo": ["anonymous-access", "code-nav", "search-contexts"],
|
"cloud:demo": ["anonymous-access", "code-nav", "search-contexts"],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
||||||
|
|
@ -1035,7 +1035,7 @@ export const flagReposForIndex = async (repoIds: number[], domain: string) => se
|
||||||
where: {
|
where: {
|
||||||
id: { in: repoIds },
|
id: { in: repoIds },
|
||||||
orgId: org.id,
|
orgId: org.id,
|
||||||
...(env.EXPERIMENT_PERMISSION_SYNC_ENABLED === 'true' ? {
|
...(env.EXPERIMENT_EE_PERMISSION_SYNC_ENABLED === 'true' ? {
|
||||||
permittedUsers: {
|
permittedUsers: {
|
||||||
some: {
|
some: {
|
||||||
userId: userId,
|
userId: userId,
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import Credentials from "next-auth/providers/credentials";
|
||||||
import type { User as AuthJsUser } from "next-auth";
|
import type { User as AuthJsUser } from "next-auth";
|
||||||
import { onCreateUser } from "@/lib/authUtils";
|
import { onCreateUser } from "@/lib/authUtils";
|
||||||
import { createLogger } from "@sourcebot/logger";
|
import { createLogger } from "@sourcebot/logger";
|
||||||
|
import { hasEntitlement } from "@sourcebot/shared";
|
||||||
|
|
||||||
const logger = createLogger('web-sso');
|
const logger = createLogger('web-sso');
|
||||||
|
|
||||||
|
|
@ -30,10 +31,10 @@ export const getSSOProviders = (): Provider[] => {
|
||||||
scope: [
|
scope: [
|
||||||
'read:user',
|
'read:user',
|
||||||
'user:email',
|
'user:email',
|
||||||
// Permission syncing requires the `repo` in order to fetch repositories
|
// Permission syncing requires the `repo` scope in order to fetch repositories
|
||||||
// for the authenticated user.
|
// for the authenticated user.
|
||||||
// @see: https://docs.github.com/en/rest/repos/repos?apiVersion=2022-11-28#list-repositories-for-the-authenticated-user
|
// @see: https://docs.github.com/en/rest/repos/repos?apiVersion=2022-11-28#list-repositories-for-the-authenticated-user
|
||||||
...(env.EXPERIMENT_PERMISSION_SYNC_ENABLED === 'true' ?
|
...(env.EXPERIMENT_EE_PERMISSION_SYNC_ENABLED === 'true' && hasEntitlement('permission-syncing') ?
|
||||||
['repo'] :
|
['repo'] :
|
||||||
[]
|
[]
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@ export const env = createEnv({
|
||||||
// @NOTE: Take care to update actions.ts when changing the name of this.
|
// @NOTE: Take care to update actions.ts when changing the name of this.
|
||||||
EXPERIMENT_SELF_SERVE_REPO_INDEXING_GITHUB_TOKEN: z.string().optional(),
|
EXPERIMENT_SELF_SERVE_REPO_INDEXING_GITHUB_TOKEN: z.string().optional(),
|
||||||
|
|
||||||
EXPERIMENT_PERMISSION_SYNC_ENABLED: booleanSchema.default('false'),
|
EXPERIMENT_EE_PERMISSION_SYNC_ENABLED: booleanSchema.default('false'),
|
||||||
},
|
},
|
||||||
// @NOTE: Please make sure of the following:
|
// @NOTE: Please make sure of the following:
|
||||||
// - Make sure you destructure all client variables in
|
// - Make sure you destructure all client variables in
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import 'server-only';
|
import 'server-only';
|
||||||
import { env } from "@/env.mjs";
|
import { env } from "@/env.mjs";
|
||||||
import { Prisma, PrismaClient } from "@sourcebot/db";
|
import { Prisma, PrismaClient } from "@sourcebot/db";
|
||||||
|
import { hasEntitlement } from "@sourcebot/shared";
|
||||||
|
|
||||||
// @see: https://authjs.dev/getting-started/adapters/prisma
|
// @see: https://authjs.dev/getting-started/adapters/prisma
|
||||||
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient }
|
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient }
|
||||||
|
|
@ -24,7 +25,7 @@ export const userScopedPrismaClientExtension = (userId?: string) => {
|
||||||
(prisma) => {
|
(prisma) => {
|
||||||
return prisma.$extends({
|
return prisma.$extends({
|
||||||
query: {
|
query: {
|
||||||
...(env.EXPERIMENT_PERMISSION_SYNC_ENABLED === 'true' ? {
|
...(env.EXPERIMENT_EE_PERMISSION_SYNC_ENABLED === 'true' && hasEntitlement('permission-syncing') ? {
|
||||||
repo: {
|
repo: {
|
||||||
$allOperations({ args, query }) {
|
$allOperations({ args, query }) {
|
||||||
if ('where' in args) {
|
if ('where' in args) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue