mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-13 04:45:19 +00:00
Compare commits
No commits in common. "main" and "v4.10.1" have entirely different histories.
32 changed files with 233 additions and 237 deletions
18
.github/workflows/ghcr-publish.yml
vendored
18
.github/workflows/ghcr-publish.yml
vendored
|
|
@ -27,9 +27,9 @@ jobs:
|
||||||
platform: [linux/amd64, linux/arm64]
|
platform: [linux/amd64, linux/arm64]
|
||||||
include:
|
include:
|
||||||
- platform: linux/amd64
|
- platform: linux/amd64
|
||||||
runs-on: ubuntu-latest
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||||
- platform: linux/arm64
|
- platform: linux/arm64
|
||||||
runs-on: ubuntu-24.04-arm
|
runs-on: blacksmith-8vcpu-ubuntu-2204-arm
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Prepare
|
- name: Prepare
|
||||||
|
|
@ -57,8 +57,8 @@ jobs:
|
||||||
with:
|
with:
|
||||||
cosign-release: "v2.2.4"
|
cosign-release: "v2.2.4"
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Setup Blacksmith Builder
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: useblacksmith/setup-docker-builder@v1
|
||||||
|
|
||||||
- name: Login to GitHub Packages Docker Registry
|
- name: Login to GitHub Packages Docker Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
|
|
@ -69,12 +69,10 @@ jobs:
|
||||||
|
|
||||||
- name: Build Docker image
|
- name: Build Docker image
|
||||||
id: build
|
id: build
|
||||||
uses: docker/build-push-action@v6
|
uses: useblacksmith/build-push-action@v2
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
cache-from: type=gha,scope=${{ env.PLATFORM_PAIR }}
|
|
||||||
cache-to: type=gha,mode=max,scope=${{ env.PLATFORM_PAIR }}
|
|
||||||
platforms: ${{ matrix.platform }}
|
platforms: ${{ matrix.platform }}
|
||||||
outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true,annotation.org.opencontainers.image.description=Blazingly fast code search
|
outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true,annotation.org.opencontainers.image.description=Blazingly fast code search
|
||||||
build-args: |
|
build-args: |
|
||||||
|
|
@ -109,7 +107,7 @@ jobs:
|
||||||
run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST}
|
run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST}
|
||||||
|
|
||||||
merge:
|
merge:
|
||||||
runs-on: ubuntu-latest
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||||
permissions:
|
permissions:
|
||||||
packages: write
|
packages: write
|
||||||
needs:
|
needs:
|
||||||
|
|
@ -122,8 +120,8 @@ jobs:
|
||||||
pattern: digests-*
|
pattern: digests-*
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Setup Blacksmith Builder
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: useblacksmith/setup-docker-builder@v1
|
||||||
|
|
||||||
- name: Extract Docker metadata
|
- name: Extract Docker metadata
|
||||||
id: meta
|
id: meta
|
||||||
|
|
|
||||||
4
.github/workflows/pr-gate.yml
vendored
4
.github/workflows/pr-gate.yml
vendored
|
|
@ -8,7 +8,7 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
|
|
@ -19,6 +19,6 @@ jobs:
|
||||||
|
|
||||||
- name: Build Docker image
|
- name: Build Docker image
|
||||||
id: build
|
id: build
|
||||||
uses: docker/build-push-action@v6
|
uses: useblacksmith/build-push-action@v2
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
|
|
|
||||||
2
.github/workflows/test-backend.yml
vendored
2
.github/workflows/test-backend.yml
vendored
|
|
@ -7,7 +7,7 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
|
|
|
||||||
2
.github/workflows/test-web.yml
vendored
2
.github/workflows/test-web.yml
vendored
|
|
@ -7,7 +7,7 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
|
|
|
||||||
21
CHANGELOG.md
21
CHANGELOG.md
|
|
@ -7,26 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Fixed issue where parenthesis in query params were not being encoded, resulting in a poor experience when embedding links in Markdown. [#674](https://github.com/sourcebot-dev/sourcebot/pull/674)
|
|
||||||
|
|
||||||
## [4.10.3] - 2025-12-12
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Fixed review agent so that it works with GHES instances [#611](https://github.com/sourcebot-dev/sourcebot/pull/611)
|
|
||||||
- Updated next package version to fix CVE-2025-55184 and CVE-2025-55183. [#673](https://github.com/sourcebot-dev/sourcebot/pull/673)
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- Added support for arbitrary user IDs required for OpenShift. [#658](https://github.com/sourcebot-dev/sourcebot/pull/658)
|
|
||||||
|
|
||||||
### Updated
|
|
||||||
- Improved error messages in file source api. [#665](https://github.com/sourcebot-dev/sourcebot/pull/665)
|
|
||||||
|
|
||||||
## [4.10.2] - 2025-12-04
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Fixed issue where the disable telemetry flag was not being respected for web server telemetry. [#657](https://github.com/sourcebot-dev/sourcebot/pull/657)
|
|
||||||
|
|
||||||
## [4.10.1] - 2025-12-03
|
## [4.10.1] - 2025-12-03
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
@ -56,7 +36,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- Changed the default search behaviour to match patterns as substrings and **not** regular expressions. Regular expressions can be used by toggling the regex button in search bar. [#623](https://github.com/sourcebot-dev/sourcebot/pull/623)
|
- Changed the default search behaviour to match patterns as substrings and **not** regular expressions. Regular expressions can be used by toggling the regex button in search bar. [#623](https://github.com/sourcebot-dev/sourcebot/pull/623)
|
||||||
- Renamed `public` query prefix to `visibility`. Allowed values for `visibility` are `public`, `private`, and `any`. [#623](https://github.com/sourcebot-dev/sourcebot/pull/623)
|
- Renamed `public` query prefix to `visibility`. Allowed values for `visibility` are `public`, `private`, and `any`. [#623](https://github.com/sourcebot-dev/sourcebot/pull/623)
|
||||||
- Changed `archived` query prefix to accept values `yes`, `no`, and `only`. [#623](https://github.com/sourcebot-dev/sourcebot/pull/623)
|
- Changed `archived` query prefix to accept values `yes`, `no`, and `only`. [#623](https://github.com/sourcebot-dev/sourcebot/pull/623)
|
||||||
- Changed `lang` query prefix to be case sensitive. [#623](https://github.com/sourcebot-dev/sourcebot/pull/623)
|
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- Removed `case` query prefix. [#623](https://github.com/sourcebot-dev/sourcebot/pull/623)
|
- Removed `case` query prefix. [#623](https://github.com/sourcebot-dev/sourcebot/pull/623)
|
||||||
|
|
|
||||||
|
|
@ -195,7 +195,6 @@ RUN addgroup -g $GID sourcebot && \
|
||||||
adduser -D -u $UID -h /app -S sourcebot && \
|
adduser -D -u $UID -h /app -S sourcebot && \
|
||||||
adduser sourcebot postgres && \
|
adduser sourcebot postgres && \
|
||||||
adduser sourcebot redis && \
|
adduser sourcebot redis && \
|
||||||
chown -R sourcebot /app && \
|
|
||||||
adduser sourcebot node && \
|
adduser sourcebot node && \
|
||||||
mkdir /var/log/sourcebot && \
|
mkdir /var/log/sourcebot && \
|
||||||
chown sourcebot /var/log/sourcebot
|
chown sourcebot /var/log/sourcebot
|
||||||
|
|
@ -245,12 +244,7 @@ RUN mkdir -p /run/postgresql && \
|
||||||
chown -R postgres:postgres /run/postgresql && \
|
chown -R postgres:postgres /run/postgresql && \
|
||||||
chmod 775 /run/postgresql
|
chmod 775 /run/postgresql
|
||||||
|
|
||||||
# Make app directory accessible to both root and sourcebot user
|
RUN chown -R sourcebot:sourcebot /data
|
||||||
RUN chown -R sourcebot /app \
|
|
||||||
&& chgrp -R 0 /app \
|
|
||||||
&& chmod -R g=u /app
|
|
||||||
# Make data directory accessible to both root and sourcebot user
|
|
||||||
RUN chown -R sourcebot /data
|
|
||||||
|
|
||||||
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||||
COPY prefix-output.sh ./prefix-output.sh
|
COPY prefix-output.sh ./prefix-output.sh
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ services:
|
||||||
- DATABASE_URL=${DATABASE_URL:-postgresql://postgres:postgres@postgres:5432/postgres} # CHANGEME
|
- DATABASE_URL=${DATABASE_URL:-postgresql://postgres:postgres@postgres:5432/postgres} # CHANGEME
|
||||||
- REDIS_URL=${REDIS_URL:-redis://redis:6379} # CHANGEME
|
- REDIS_URL=${REDIS_URL:-redis://redis:6379} # CHANGEME
|
||||||
- SOURCEBOT_EE_LICENSE_KEY=${SOURCEBOT_EE_LICENSE_KEY:-}
|
- SOURCEBOT_EE_LICENSE_KEY=${SOURCEBOT_EE_LICENSE_KEY:-}
|
||||||
|
- SOURCEBOT_TELEMETRY_DISABLED=${SOURCEBOT_TELEMETRY_DISABLED:-false}
|
||||||
|
|
||||||
# For the full list of environment variables see:
|
# For the full list of environment variables see:
|
||||||
# https://docs.sourcebot.dev/docs/configuration/environment-variables
|
# https://docs.sourcebot.dev/docs/configuration/environment-variables
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
---
|
---
|
||||||
title: "Permission syncing"
|
title: "Permission syncing"
|
||||||
sidebarTitle: "Permission syncing"
|
sidebarTitle: "Permission syncing"
|
||||||
|
tag: "experimental"
|
||||||
---
|
---
|
||||||
|
|
||||||
import LicenseKeyRequired from '/snippets/license-key-required.mdx'
|
import LicenseKeyRequired from '/snippets/license-key-required.mdx'
|
||||||
|
import ExperimentalFeatureWarning from '/snippets/experimental-feature-warning.mdx'
|
||||||
|
|
||||||
<LicenseKeyRequired />
|
<LicenseKeyRequired />
|
||||||
|
<ExperimentalFeatureWarning />
|
||||||
|
|
||||||
# Overview
|
# Overview
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,7 @@
|
||||||
"langfuse-vercel": "^3.38.4",
|
"langfuse-vercel": "^3.38.4",
|
||||||
"lucide-react": "^0.517.0",
|
"lucide-react": "^0.517.0",
|
||||||
"micromatch": "^4.0.8",
|
"micromatch": "^4.0.8",
|
||||||
"next": "15.5.9",
|
"next": "^15.5.7",
|
||||||
"next-auth": "^5.0.0-beta.30",
|
"next-auth": "^5.0.0-beta.30",
|
||||||
"next-navigation-guard": "^0.2.0",
|
"next-navigation-guard": "^0.2.0",
|
||||||
"next-themes": "^0.3.0",
|
"next-themes": "^0.3.0",
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,8 @@ const auditService = getAuditService();
|
||||||
/**
|
/**
|
||||||
* "Service Error Wrapper".
|
* "Service Error Wrapper".
|
||||||
*
|
*
|
||||||
* Captures any thrown exceptions, logs them to the console and Sentry,
|
* Captures any thrown exceptions and converts them to a unexpected
|
||||||
* and returns a generic unexpected service error.
|
* service error. Also logs them with Sentry.
|
||||||
*/
|
*/
|
||||||
export const sew = async <T>(fn: () => Promise<T>): Promise<T | ServiceError> => {
|
export const sew = async <T>(fn: () => Promise<T>): Promise<T | ServiceError> => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -52,6 +52,10 @@ export const sew = async <T>(fn: () => Promise<T>): Promise<T | ServiceError> =>
|
||||||
return e.serviceError;
|
return e.serviceError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (e instanceof Error) {
|
||||||
|
return unexpectedError(e.message);
|
||||||
|
}
|
||||||
|
|
||||||
return unexpectedError(`An unexpected error occurred. Please try again later.`);
|
return unexpectedError(`An unexpected error occurred. Please try again later.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,8 @@ export const CodePreviewPanel = async ({ path, repoName, revisionName }: CodePre
|
||||||
getRepoInfoByName(repoName),
|
getRepoInfoByName(repoName),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (isServiceError(fileSourceResponse)) {
|
if (isServiceError(fileSourceResponse) || isServiceError(repoInfoResponse)) {
|
||||||
return <div>Error loading file source: {fileSourceResponse.message}</div>
|
return <div>Error loading file source</div>
|
||||||
}
|
|
||||||
|
|
||||||
if (isServiceError(repoInfoResponse)) {
|
|
||||||
return <div>Error loading repo info: {repoInfoResponse.message}</div>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const codeHostInfo = getCodeHostInfoForRepo({
|
const codeHostInfo = getCodeHostInfoForRepo({
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ export const TrialIndicator = ({ subscription }: Props) => {
|
||||||
|
|
||||||
if (isServiceError(subscription)) {
|
if (isServiceError(subscription)) {
|
||||||
captureEvent('wa_trial_nav_subscription_fetch_fail', {
|
captureEvent('wa_trial_nav_subscription_fetch_fail', {
|
||||||
errorCode: subscription.errorCode,
|
error: subscription.errorCode,
|
||||||
});
|
});
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ export const SearchLandingPage = async ({
|
||||||
<Query query="repo:torvalds/linux test" domain={domain}><Highlight>repo:</Highlight>torvalds/linux test</Query> <QueryExplanation>(by repo)</QueryExplanation>
|
<Query query="repo:torvalds/linux test" domain={domain}><Highlight>repo:</Highlight>torvalds/linux test</Query> <QueryExplanation>(by repo)</QueryExplanation>
|
||||||
</QueryExample>
|
</QueryExample>
|
||||||
<QueryExample>
|
<QueryExample>
|
||||||
<Query query="lang:TypeScript" domain={domain}><Highlight>lang:</Highlight>TypeScript</Query> <QueryExplanation>(by language)</QueryExplanation>
|
<Query query="lang:typescript" domain={domain}><Highlight>lang:</Highlight>typescript</Query> <QueryExplanation>(by language)</QueryExplanation>
|
||||||
</QueryExample>
|
</QueryExample>
|
||||||
<QueryExample>
|
<QueryExample>
|
||||||
<Query query="rev:HEAD" domain={domain}><Highlight>rev:</Highlight>HEAD</Query> <QueryExplanation>(by branch or tag)</QueryExplanation>
|
<Query query="rev:HEAD" domain={domain}><Highlight>rev:</Highlight>HEAD</Query> <QueryExplanation>(by branch or tag)</QueryExplanation>
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ export function ChangeOrgDomainCard({ orgDomain, currentUserRole, rootDomain }:
|
||||||
description: `❌ Failed to update organization url. Reason: ${result.message}`,
|
description: `❌ Failed to update organization url. Reason: ${result.message}`,
|
||||||
})
|
})
|
||||||
captureEvent('wa_org_domain_updated_fail', {
|
captureEvent('wa_org_domain_updated_fail', {
|
||||||
errorCode: result.errorCode,
|
error: result.errorCode,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
toast({
|
toast({
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ export function ChangeOrgNameCard({ orgName, currentUserRole }: ChangeOrgNameCar
|
||||||
description: `❌ Failed to update organization name. Reason: ${result.message}`,
|
description: `❌ Failed to update organization name. Reason: ${result.message}`,
|
||||||
})
|
})
|
||||||
captureEvent('wa_org_name_updated_fail', {
|
captureEvent('wa_org_name_updated_fail', {
|
||||||
errorCode: result.errorCode,
|
error: result.errorCode,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
toast({
|
toast({
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ export const InviteMemberCard = ({ currentUserRole, isBillingEnabled, seatsAvail
|
||||||
description: `❌ Failed to invite members. Reason: ${res.message}`
|
description: `❌ Failed to invite members. Reason: ${res.message}`
|
||||||
});
|
});
|
||||||
captureEvent('wa_invite_member_card_invite_fail', {
|
captureEvent('wa_invite_member_card_invite_fail', {
|
||||||
errorCode: res.errorCode,
|
error: res.errorCode,
|
||||||
num_emails: data.emails.length,
|
num_emails: data.emails.length,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ export const InvitesList = ({ invites, currentUserRole }: InviteListProps) => {
|
||||||
description: `❌ Failed to cancel invite. Reason: ${response.message}`
|
description: `❌ Failed to cancel invite. Reason: ${response.message}`
|
||||||
})
|
})
|
||||||
captureEvent('wa_invites_list_cancel_invite_fail', {
|
captureEvent('wa_invites_list_cancel_invite_fail', {
|
||||||
errorCode: response.errorCode,
|
error: response.errorCode,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
toast({
|
toast({
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ export const MembersList = ({ members, currentUserId, currentUserRole, orgName }
|
||||||
description: `❌ Failed to remove member. Reason: ${response.message}`
|
description: `❌ Failed to remove member. Reason: ${response.message}`
|
||||||
})
|
})
|
||||||
captureEvent('wa_members_list_remove_member_fail', {
|
captureEvent('wa_members_list_remove_member_fail', {
|
||||||
errorCode: response.errorCode,
|
error: response.errorCode,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
toast({
|
toast({
|
||||||
|
|
@ -91,7 +91,7 @@ export const MembersList = ({ members, currentUserId, currentUserRole, orgName }
|
||||||
description: `❌ Failed to transfer ownership. Reason: ${response.message}`
|
description: `❌ Failed to transfer ownership. Reason: ${response.message}`
|
||||||
})
|
})
|
||||||
captureEvent('wa_members_list_transfer_ownership_fail', {
|
captureEvent('wa_members_list_transfer_ownership_fail', {
|
||||||
errorCode: response.errorCode,
|
error: response.errorCode,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
toast({
|
toast({
|
||||||
|
|
@ -111,7 +111,7 @@ export const MembersList = ({ members, currentUserId, currentUserRole, orgName }
|
||||||
description: `❌ Failed to leave organization. Reason: ${response.message}`
|
description: `❌ Failed to leave organization. Reason: ${response.message}`
|
||||||
})
|
})
|
||||||
captureEvent('wa_members_list_leave_org_fail', {
|
captureEvent('wa_members_list_leave_org_fail', {
|
||||||
errorCode: response.errorCode,
|
error: response.errorCode,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
toast({
|
toast({
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ export const RequestsList = ({ requests, currentUserRole }: RequestsListProps) =
|
||||||
description: `❌ Failed to approve request. Reason: ${response.message}`
|
description: `❌ Failed to approve request. Reason: ${response.message}`
|
||||||
})
|
})
|
||||||
captureEvent('wa_requests_list_approve_request_fail', {
|
captureEvent('wa_requests_list_approve_request_fail', {
|
||||||
errorCode: response.errorCode,
|
error: response.errorCode,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
toast({
|
toast({
|
||||||
|
|
@ -83,7 +83,7 @@ export const RequestsList = ({ requests, currentUserRole }: RequestsListProps) =
|
||||||
description: `❌ Failed to reject request.`
|
description: `❌ Failed to reject request.`
|
||||||
})
|
})
|
||||||
captureEvent('wa_requests_list_reject_request_fail', {
|
captureEvent('wa_requests_list_reject_request_fail', {
|
||||||
errorCode: response.errorCode,
|
error: response.errorCode,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
toast({
|
toast({
|
||||||
|
|
|
||||||
|
|
@ -6,32 +6,28 @@ import { WebhookEventDefinition} from "@octokit/webhooks/types";
|
||||||
import { EndpointDefaults } from "@octokit/types";
|
import { EndpointDefaults } from "@octokit/types";
|
||||||
import { env } from "@sourcebot/shared";
|
import { env } from "@sourcebot/shared";
|
||||||
import { processGitHubPullRequest } from "@/features/agents/review-agent/app";
|
import { processGitHubPullRequest } from "@/features/agents/review-agent/app";
|
||||||
import { throttling, type ThrottlingOptions } from "@octokit/plugin-throttling";
|
import { throttling } from "@octokit/plugin-throttling";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import { GitHubPullRequest } from "@/features/agents/review-agent/types";
|
import { GitHubPullRequest } from "@/features/agents/review-agent/types";
|
||||||
import { createLogger } from "@sourcebot/shared";
|
import { createLogger } from "@sourcebot/shared";
|
||||||
|
|
||||||
const logger = createLogger('github-webhook');
|
const logger = createLogger('github-webhook');
|
||||||
|
|
||||||
const DEFAULT_GITHUB_API_BASE_URL = "https://api.github.com";
|
let githubApp: App | undefined;
|
||||||
type GitHubAppBaseOptions = Omit<ConstructorParameters<typeof App>[0], "Octokit"> & { throttle: ThrottlingOptions };
|
|
||||||
|
|
||||||
let githubAppBaseOptions: GitHubAppBaseOptions | undefined;
|
|
||||||
const githubAppCache = new Map<string, App>();
|
|
||||||
|
|
||||||
if (env.GITHUB_REVIEW_AGENT_APP_ID && env.GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET && env.GITHUB_REVIEW_AGENT_APP_PRIVATE_KEY_PATH) {
|
if (env.GITHUB_REVIEW_AGENT_APP_ID && env.GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET && env.GITHUB_REVIEW_AGENT_APP_PRIVATE_KEY_PATH) {
|
||||||
try {
|
try {
|
||||||
const privateKey = fs.readFileSync(env.GITHUB_REVIEW_AGENT_APP_PRIVATE_KEY_PATH, "utf8");
|
const privateKey = fs.readFileSync(env.GITHUB_REVIEW_AGENT_APP_PRIVATE_KEY_PATH, "utf8");
|
||||||
|
|
||||||
githubAppBaseOptions = {
|
const throttledOctokit = Octokit.plugin(throttling);
|
||||||
|
githubApp = new App({
|
||||||
appId: env.GITHUB_REVIEW_AGENT_APP_ID,
|
appId: env.GITHUB_REVIEW_AGENT_APP_ID,
|
||||||
privateKey,
|
privateKey: privateKey,
|
||||||
webhooks: {
|
webhooks: {
|
||||||
secret: env.GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET,
|
secret: env.GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET,
|
||||||
},
|
},
|
||||||
|
Octokit: throttledOctokit,
|
||||||
throttle: {
|
throttle: {
|
||||||
enabled: true,
|
onRateLimit: (retryAfter: number, options: Required<EndpointDefaults>, octokit: Octokit, retryCount: number) => {
|
||||||
onRateLimit: (retryAfter, _options, _octokit, retryCount) => {
|
|
||||||
if (retryCount > 3) {
|
if (retryCount > 3) {
|
||||||
logger.warn(`Rate limit exceeded: ${retryAfter} seconds`);
|
logger.warn(`Rate limit exceeded: ${retryAfter} seconds`);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -39,55 +35,13 @@ if (env.GITHUB_REVIEW_AGENT_APP_ID && env.GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
onSecondaryRateLimit: (_retryAfter, options) => {
|
|
||||||
// no retries on secondary rate limits
|
|
||||||
logger.warn(`SecondaryRateLimit detected for ${options.method} ${options.url}`);
|
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
};
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Error initializing GitHub app: ${error}`);
|
logger.error(`Error initializing GitHub app: ${error}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const normalizeGithubApiBaseUrl = (baseUrl?: string) => {
|
|
||||||
if (!baseUrl) {
|
|
||||||
return DEFAULT_GITHUB_API_BASE_URL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return baseUrl.replace(/\/+$/, "");
|
|
||||||
};
|
|
||||||
|
|
||||||
const resolveGithubApiBaseUrl = (headers: Record<string, string>) => {
|
|
||||||
const enterpriseHost = headers["x-github-enterprise-host"];
|
|
||||||
if (enterpriseHost) {
|
|
||||||
return normalizeGithubApiBaseUrl(`https://${enterpriseHost}/api/v3`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return DEFAULT_GITHUB_API_BASE_URL;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getGithubAppForBaseUrl = (baseUrl: string) => {
|
|
||||||
if (!githubAppBaseOptions) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalizedBaseUrl = normalizeGithubApiBaseUrl(baseUrl);
|
|
||||||
const cachedApp = githubAppCache.get(normalizedBaseUrl);
|
|
||||||
if (cachedApp) {
|
|
||||||
return cachedApp;
|
|
||||||
}
|
|
||||||
|
|
||||||
const OctokitWithBaseUrl = Octokit.plugin(throttling).defaults({ baseUrl: normalizedBaseUrl });
|
|
||||||
const app = new App({
|
|
||||||
...githubAppBaseOptions,
|
|
||||||
Octokit: OctokitWithBaseUrl,
|
|
||||||
});
|
|
||||||
|
|
||||||
githubAppCache.set(normalizedBaseUrl, app);
|
|
||||||
return app;
|
|
||||||
};
|
|
||||||
|
|
||||||
function isPullRequestEvent(eventHeader: string, payload: unknown): payload is WebhookEventDefinition<"pull-request-opened"> | WebhookEventDefinition<"pull-request-synchronize"> {
|
function isPullRequestEvent(eventHeader: string, payload: unknown): payload is WebhookEventDefinition<"pull-request-opened"> | WebhookEventDefinition<"pull-request-synchronize"> {
|
||||||
return eventHeader === "pull_request" && typeof payload === "object" && payload !== null && "action" in payload && typeof payload.action === "string" && (payload.action === "opened" || payload.action === "synchronize");
|
return eventHeader === "pull_request" && typeof payload === "object" && payload !== null && "action" in payload && typeof payload.action === "string" && (payload.action === "opened" || payload.action === "synchronize");
|
||||||
}
|
}
|
||||||
|
|
@ -98,16 +52,12 @@ function isIssueCommentEvent(eventHeader: string, payload: unknown): payload is
|
||||||
|
|
||||||
export const POST = async (request: NextRequest) => {
|
export const POST = async (request: NextRequest) => {
|
||||||
const body = await request.json();
|
const body = await request.json();
|
||||||
const headers = Object.fromEntries(Array.from(request.headers.entries(), ([key, value]) => [key.toLowerCase(), value]));
|
const headers = Object.fromEntries(request.headers.entries());
|
||||||
|
|
||||||
const githubEvent = headers['x-github-event'];
|
const githubEvent = headers['x-github-event'] || headers['X-GitHub-Event'];
|
||||||
if (githubEvent) {
|
if (githubEvent) {
|
||||||
logger.info('GitHub event received:', githubEvent);
|
logger.info('GitHub event received:', githubEvent);
|
||||||
|
|
||||||
const githubApiBaseUrl = resolveGithubApiBaseUrl(headers);
|
|
||||||
logger.debug('Using GitHub API base URL for event', { githubApiBaseUrl });
|
|
||||||
const githubApp = getGithubAppForBaseUrl(githubApiBaseUrl);
|
|
||||||
|
|
||||||
if (!githubApp) {
|
if (!githubApp) {
|
||||||
logger.warn('Received GitHub webhook event but GitHub app env vars are not set');
|
logger.warn('Received GitHub webhook event but GitHub app env vars are not set');
|
||||||
return Response.json({ status: 'ok' });
|
return Response.json({ status: 'ok' });
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import { Toaster } from "@/components/ui/toaster";
|
||||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||||
import { SessionProvider } from "next-auth/react";
|
import { SessionProvider } from "next-auth/react";
|
||||||
import { env } from "@sourcebot/shared";
|
import { env } from "@sourcebot/shared";
|
||||||
import { env as clientEnv } from "@sourcebot/shared/client";
|
|
||||||
import { PlanProvider } from "@/features/entitlements/planProvider";
|
import { PlanProvider } from "@/features/entitlements/planProvider";
|
||||||
import { getEntitlements } from "@sourcebot/shared";
|
import { getEntitlements } from "@sourcebot/shared";
|
||||||
|
|
||||||
|
|
@ -43,8 +42,6 @@ export default function RootLayout({
|
||||||
// @note: the posthog api key doesn't need to be kept secret,
|
// @note: the posthog api key doesn't need to be kept secret,
|
||||||
// so we are safe to send it to the client.
|
// so we are safe to send it to the client.
|
||||||
posthogApiKey={env.POSTHOG_PAPIK}
|
posthogApiKey={env.POSTHOG_PAPIK}
|
||||||
sourcebotVersion={clientEnv.NEXT_PUBLIC_SOURCEBOT_VERSION}
|
|
||||||
sourcebotInstallId={env.SOURCEBOT_INSTALL_ID}
|
|
||||||
>
|
>
|
||||||
<ThemeProvider
|
<ThemeProvider
|
||||||
attribute="class"
|
attribute="class"
|
||||||
|
|
|
||||||
|
|
@ -34,17 +34,9 @@ interface PostHogProviderProps {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
isDisabled: boolean
|
isDisabled: boolean
|
||||||
posthogApiKey: string
|
posthogApiKey: string
|
||||||
sourcebotVersion: string
|
|
||||||
sourcebotInstallId: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PostHogProvider({
|
export function PostHogProvider({ children, isDisabled, posthogApiKey }: PostHogProviderProps) {
|
||||||
children,
|
|
||||||
isDisabled,
|
|
||||||
posthogApiKey,
|
|
||||||
sourcebotVersion,
|
|
||||||
sourcebotInstallId,
|
|
||||||
}: PostHogProviderProps) {
|
|
||||||
const { data: session } = useSession();
|
const { data: session } = useSession();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -69,33 +61,27 @@ export function PostHogProvider({
|
||||||
'$referrer',
|
'$referrer',
|
||||||
'$referring_domain',
|
'$referring_domain',
|
||||||
'$ip',
|
'$ip',
|
||||||
] : [],
|
] : []
|
||||||
loaded: (posthog) => {
|
|
||||||
// Include install id & version in all events.
|
|
||||||
posthog.register({
|
|
||||||
sourcebot_version: sourcebotVersion,
|
|
||||||
install_id: sourcebotInstallId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.debug("PostHog telemetry disabled");
|
console.debug("PostHog telemetry disabled");
|
||||||
}
|
}
|
||||||
}, [isDisabled, posthogApiKey, sourcebotInstallId, sourcebotVersion]);
|
}, [isDisabled, posthogApiKey]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!session) {
|
if (!session) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
posthog.identify(
|
// Only identify the user if we are running in a cloud environment.
|
||||||
session.user.id,
|
if (env.NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT !== undefined) {
|
||||||
// Only include email & name when running in a cloud environment.
|
posthog.identify(session.user.id, {
|
||||||
env.NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT !== undefined ? {
|
|
||||||
email: session.user.email,
|
email: session.user.email,
|
||||||
name: session.user.name,
|
name: session.user.name,
|
||||||
} : undefined
|
});
|
||||||
);
|
} else {
|
||||||
|
console.debug("PostHog identify skipped");
|
||||||
|
}
|
||||||
}, [session]);
|
}, [session]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ export function ChangeBillingEmailCard({ currentUserRole, billingEmail }: Change
|
||||||
description: "❌ Failed to update billing email. Please try again.",
|
description: "❌ Failed to update billing email. Please try again.",
|
||||||
})
|
})
|
||||||
captureEvent('wa_billing_email_updated_fail', {
|
captureEvent('wa_billing_email_updated_fail', {
|
||||||
errorCode: result.errorCode,
|
error: result.message,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ export const Checkout = () => {
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
captureEvent('wa_onboard_checkout_fail', {
|
captureEvent('wa_onboard_checkout_fail', {
|
||||||
errorCode: errorMessage,
|
error: errorMessage,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [errorCode, errorMessage, toast, captureEvent]);
|
}, [errorCode, errorMessage, toast, captureEvent]);
|
||||||
|
|
@ -45,7 +45,7 @@ export const Checkout = () => {
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
})
|
})
|
||||||
captureEvent('wa_onboard_checkout_fail', {
|
captureEvent('wa_onboard_checkout_fail', {
|
||||||
errorCode: response.errorCode,
|
error: response.errorCode,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
captureEvent('wa_onboard_checkout_success', {});
|
captureEvent('wa_onboard_checkout_success', {});
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ export function ManageSubscriptionButton({ currentUserRole }: { currentUserRole:
|
||||||
const session = await getCustomerPortalSessionLink(domain);
|
const session = await getCustomerPortalSessionLink(domain);
|
||||||
if (isServiceError(session)) {
|
if (isServiceError(session)) {
|
||||||
captureEvent('wa_manage_subscription_button_create_portal_session_fail', {
|
captureEvent('wa_manage_subscription_button_create_portal_session_fail', {
|
||||||
errorCode: session.errorCode,
|
error: session.errorCode,
|
||||||
});
|
});
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ export const TeamUpgradeCard = ({ buttonText }: TeamUpgradeCardProps) => {
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
captureEvent('wa_team_upgrade_checkout_fail', {
|
captureEvent('wa_team_upgrade_checkout_fail', {
|
||||||
errorCode: response.errorCode,
|
error: response.errorCode,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
router.push(response.url);
|
router.push(response.url);
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,17 @@
|
||||||
import { CaptureOptions } from "posthog-js";
|
import { CaptureOptions } from "posthog-js";
|
||||||
import posthog from "posthog-js";
|
import posthog from "posthog-js";
|
||||||
import { PosthogEvent, PosthogEventMap } from "../lib/posthogEvents";
|
import { PosthogEvent, PosthogEventMap } from "../lib/posthogEvents";
|
||||||
|
import { env } from "@sourcebot/shared/client";
|
||||||
|
|
||||||
export function captureEvent<E extends PosthogEvent>(event: E, properties: PosthogEventMap[E], options?: CaptureOptions) {
|
export function captureEvent<E extends PosthogEvent>(event: E, properties: PosthogEventMap[E], options?: CaptureOptions) {
|
||||||
if(!options) {
|
if(!options) {
|
||||||
options = {};
|
options = {};
|
||||||
}
|
}
|
||||||
options.send_instantly = true;
|
options.send_instantly = true;
|
||||||
posthog.capture(event, properties, options);
|
posthog.capture(event, {
|
||||||
|
...properties,
|
||||||
|
sourcebot_version: env.NEXT_PUBLIC_SOURCEBOT_VERSION,
|
||||||
|
}, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,9 @@
|
||||||
import { PostHog } from 'posthog-node'
|
import { PostHog } from 'posthog-node'
|
||||||
import { env } from '@sourcebot/shared'
|
import { env } from '@sourcebot/shared'
|
||||||
import { env as clientEnv } from '@sourcebot/shared/client';
|
|
||||||
import { RequestCookies } from 'next/dist/compiled/@edge-runtime/cookies';
|
import { RequestCookies } from 'next/dist/compiled/@edge-runtime/cookies';
|
||||||
import * as Sentry from "@sentry/nextjs";
|
import * as Sentry from "@sentry/nextjs";
|
||||||
import { PosthogEvent, PosthogEventMap } from './posthogEvents';
|
import { PosthogEvent, PosthogEventMap } from './posthogEvents';
|
||||||
import { cookies, headers } from 'next/headers';
|
import { cookies } from 'next/headers';
|
||||||
import { auth } from '@/auth';
|
|
||||||
import { getVerifiedApiObject } from '@/withAuthV2';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @note: This is a subset of the properties stored in the
|
* @note: This is a subset of the properties stored in the
|
||||||
|
|
@ -50,43 +47,9 @@ const getPostHogCookie = (cookieStore: Pick<RequestCookies, 'get'>): PostHogCook
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export async function captureEvent<E extends PosthogEvent>(event: E, properties: PosthogEventMap[E]) {
|
||||||
* Attempts to retrieve the distinct id of the current user.
|
|
||||||
*/
|
|
||||||
const tryGetDistinctId = async () => {
|
|
||||||
// First, attempt to retrieve the distinct id from the cookie.
|
|
||||||
const cookieStore = await cookies();
|
const cookieStore = await cookies();
|
||||||
const cookie = getPostHogCookie(cookieStore);
|
const cookie = getPostHogCookie(cookieStore);
|
||||||
if (cookie) {
|
|
||||||
return cookie.distinct_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next, from the session.
|
|
||||||
const session = await auth();
|
|
||||||
if (session) {
|
|
||||||
return session.user.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, from the api key.
|
|
||||||
const headersList = await headers();
|
|
||||||
const apiKeyString = headersList.get("X-Sourcebot-Api-Key") ?? undefined;
|
|
||||||
if (!apiKeyString) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const apiKey = await getVerifiedApiObject(apiKeyString);
|
|
||||||
return apiKey?.createdById;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function captureEvent<E extends PosthogEvent>(event: E, properties: PosthogEventMap[E]) {
|
|
||||||
if (env.SOURCEBOT_TELEMETRY_DISABLED === 'true') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const distinctId = await tryGetDistinctId();
|
|
||||||
|
|
||||||
const headersList = await headers();
|
|
||||||
const host = headersList.get("host") ?? undefined;
|
|
||||||
|
|
||||||
const posthog = new PostHog(env.POSTHOG_PAPIK, {
|
const posthog = new PostHog(env.POSTHOG_PAPIK, {
|
||||||
host: 'https://us.i.posthog.com',
|
host: 'https://us.i.posthog.com',
|
||||||
|
|
@ -96,12 +59,7 @@ export async function captureEvent<E extends PosthogEvent>(event: E, properties:
|
||||||
|
|
||||||
posthog.capture({
|
posthog.capture({
|
||||||
event,
|
event,
|
||||||
properties: {
|
properties,
|
||||||
...properties,
|
distinctId: cookie?.distinct_id ?? '',
|
||||||
sourcebot_version: clientEnv.NEXT_PUBLIC_SOURCEBOT_VERSION,
|
|
||||||
install_id: env.SOURCEBOT_INSTALL_ID,
|
|
||||||
$host: host,
|
|
||||||
},
|
|
||||||
distinctId,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -29,10 +29,71 @@ export type PosthogEventMap = {
|
||||||
fileLanguages: string[],
|
fileLanguages: string[],
|
||||||
isSearchExhaustive: boolean
|
isSearchExhaustive: boolean
|
||||||
},
|
},
|
||||||
|
share_link_created: {},
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
wa_secret_created_success: {
|
||||||
|
key: string,
|
||||||
|
},
|
||||||
|
wa_secret_deleted_success: {
|
||||||
|
key: string,
|
||||||
|
},
|
||||||
|
wa_secret_deleted_fail: {
|
||||||
|
key: string,
|
||||||
|
error: string,
|
||||||
|
},
|
||||||
|
wa_secret_created_fail: {
|
||||||
|
key: string,
|
||||||
|
error: string,
|
||||||
|
},
|
||||||
|
wa_secret_fetch_fail: {
|
||||||
|
error: string,
|
||||||
|
},
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
|
wa_warning_nav_connection_fetch_fail: {
|
||||||
|
error: string,
|
||||||
|
},
|
||||||
|
wa_warning_nav_hover: {},
|
||||||
|
wa_warning_nav_pressed: {},
|
||||||
|
wa_warning_nav_connection_pressed: {},
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
|
wa_error_nav_connection_fetch_fail: {
|
||||||
|
error: string,
|
||||||
|
},
|
||||||
|
wa_error_nav_hover: {},
|
||||||
|
wa_error_nav_pressed: {},
|
||||||
|
wa_error_nav_job_pressed: {},
|
||||||
|
wa_error_nav_job_fetch_fail: {
|
||||||
|
error: string,
|
||||||
|
},
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
|
wa_progress_nav_connection_fetch_fail: {
|
||||||
|
error: string,
|
||||||
|
},
|
||||||
|
wa_progress_nav_repo_fetch_fail: {
|
||||||
|
error: string,
|
||||||
|
},
|
||||||
|
wa_progress_nav_hover: {},
|
||||||
|
wa_progress_nav_pressed: {},
|
||||||
|
wa_progress_nav_job_pressed: {},
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
wa_trial_nav_pressed: {},
|
wa_trial_nav_pressed: {},
|
||||||
wa_trial_nav_subscription_fetch_fail: {
|
wa_trial_nav_subscription_fetch_fail: {
|
||||||
errorCode: string,
|
error: string,
|
||||||
|
},
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
|
wa_connection_list_item_error_hover: {},
|
||||||
|
wa_connection_list_item_error_pressed: {},
|
||||||
|
wa_connection_list_item_warning_hover: {},
|
||||||
|
wa_connection_list_item_warning_pressed: {},
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
|
wa_connection_list_item_manage_pressed: {},
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
|
wa_create_connection_success: {
|
||||||
|
type: string,
|
||||||
|
},
|
||||||
|
wa_create_connection_fail: {
|
||||||
|
type: string,
|
||||||
|
error: string,
|
||||||
},
|
},
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
wa_config_editor_quick_action_pressed: {
|
wa_config_editor_quick_action_pressed: {
|
||||||
|
|
@ -40,64 +101,124 @@ export type PosthogEventMap = {
|
||||||
type: string,
|
type: string,
|
||||||
},
|
},
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
|
wa_secret_combobox_import_secret_pressed: {
|
||||||
|
type: string,
|
||||||
|
},
|
||||||
|
wa_secret_combobox_import_secret_success: {
|
||||||
|
type: string,
|
||||||
|
},
|
||||||
|
wa_secret_combobox_import_secret_fail: {
|
||||||
|
type: string,
|
||||||
|
error: string,
|
||||||
|
},
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
wa_billing_email_updated_success: {},
|
wa_billing_email_updated_success: {},
|
||||||
wa_billing_email_updated_fail: {
|
wa_billing_email_updated_fail: {
|
||||||
errorCode: string,
|
error: string,
|
||||||
|
},
|
||||||
|
wa_billing_email_fetch_fail: {
|
||||||
|
error: string,
|
||||||
},
|
},
|
||||||
wa_manage_subscription_button_create_portal_session_success: {},
|
wa_manage_subscription_button_create_portal_session_success: {},
|
||||||
wa_manage_subscription_button_create_portal_session_fail: {
|
wa_manage_subscription_button_create_portal_session_fail: {
|
||||||
errorCode: string,
|
error: string,
|
||||||
},
|
},
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
wa_invite_member_card_invite_success: {
|
wa_invite_member_card_invite_success: {
|
||||||
num_emails: number,
|
num_emails: number,
|
||||||
},
|
},
|
||||||
wa_invite_member_card_invite_fail: {
|
wa_invite_member_card_invite_fail: {
|
||||||
errorCode: string,
|
error: string,
|
||||||
num_emails: number,
|
num_emails: number,
|
||||||
},
|
},
|
||||||
wa_invite_member_card_invite_cancel: {
|
wa_invite_member_card_invite_cancel: {
|
||||||
num_emails: number,
|
num_emails: number,
|
||||||
},
|
},
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
|
wa_onboard_skip_onboarding: {
|
||||||
|
step: string,
|
||||||
|
},
|
||||||
|
wa_onboard_invite_team_invite_success: {
|
||||||
|
num_emails: number,
|
||||||
|
},
|
||||||
|
wa_onboard_invite_team_invite_fail: {
|
||||||
|
error: string,
|
||||||
|
num_emails: number,
|
||||||
|
},
|
||||||
|
wa_onboard_invite_team_skip: {
|
||||||
|
num_emails: number,
|
||||||
|
},
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
wa_members_list_remove_member_success: {},
|
wa_members_list_remove_member_success: {},
|
||||||
wa_members_list_remove_member_fail: {
|
wa_members_list_remove_member_fail: {
|
||||||
errorCode: string,
|
error: string,
|
||||||
},
|
},
|
||||||
wa_members_list_transfer_ownership_success: {},
|
wa_members_list_transfer_ownership_success: {},
|
||||||
wa_members_list_transfer_ownership_fail: {
|
wa_members_list_transfer_ownership_fail: {
|
||||||
errorCode: string,
|
error: string,
|
||||||
},
|
},
|
||||||
wa_members_list_leave_org_success: {},
|
wa_members_list_leave_org_success: {},
|
||||||
wa_members_list_leave_org_fail: {
|
wa_members_list_leave_org_fail: {
|
||||||
errorCode: string,
|
error: string,
|
||||||
},
|
},
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
wa_invites_list_cancel_invite_success: {},
|
wa_invites_list_cancel_invite_success: {},
|
||||||
wa_invites_list_cancel_invite_fail: {
|
wa_invites_list_cancel_invite_fail: {
|
||||||
errorCode: string,
|
error: string,
|
||||||
},
|
},
|
||||||
wa_invites_list_copy_invite_link_success: {},
|
wa_invites_list_copy_invite_link_success: {},
|
||||||
wa_invites_list_copy_invite_link_fail: {},
|
wa_invites_list_copy_invite_link_fail: {},
|
||||||
wa_invites_list_copy_email_success: {},
|
wa_invites_list_copy_email_success: {},
|
||||||
wa_invites_list_copy_email_fail: {},
|
wa_invites_list_copy_email_fail: {},
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
|
wa_onboard_org_create_success: {},
|
||||||
|
wa_onboard_org_create_fail: {
|
||||||
|
error: string,
|
||||||
|
},
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
wa_connect_code_host_button_pressed: {
|
wa_connect_code_host_button_pressed: {
|
||||||
name: string,
|
name: string,
|
||||||
},
|
},
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
wa_onboard_checkout_success: {},
|
wa_onboard_checkout_success: {},
|
||||||
wa_onboard_checkout_fail: {
|
wa_onboard_checkout_fail: {
|
||||||
errorCode: string,
|
error: string,
|
||||||
},
|
},
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
wa_team_upgrade_card_pressed: {},
|
wa_team_upgrade_card_pressed: {},
|
||||||
wa_team_upgrade_checkout_success: {},
|
wa_team_upgrade_checkout_success: {},
|
||||||
wa_team_upgrade_checkout_fail: {
|
wa_team_upgrade_checkout_fail: {
|
||||||
errorCode: string,
|
error: string,
|
||||||
},
|
},
|
||||||
wa_enterprise_upgrade_card_pressed: {},
|
wa_enterprise_upgrade_card_pressed: {},
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
|
wa_connection_delete_success: {},
|
||||||
|
wa_connection_delete_fail: {
|
||||||
|
error: string,
|
||||||
|
},
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
|
wa_connection_failed_status_hover: {},
|
||||||
|
wa_connection_retry_sync_success: {},
|
||||||
|
wa_connection_retry_sync_fail: {
|
||||||
|
error: string,
|
||||||
|
},
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
|
wa_connection_not_found_warning_displayed: {},
|
||||||
|
wa_connection_secrets_navigation_pressed: {},
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
|
wa_connection_retry_all_failed_repos_pressed: {},
|
||||||
|
wa_connection_retry_all_failed_repos_fetch_fail: {
|
||||||
|
error: string,
|
||||||
|
},
|
||||||
|
wa_connection_retry_all_failed_repos_fail: {},
|
||||||
|
wa_connection_retry_all_failed_repos_success: {},
|
||||||
|
wa_connection_retry_all_failed_no_repos: {},
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
|
wa_repo_retry_index_success: {},
|
||||||
|
wa_repo_retry_index_fail: {
|
||||||
|
error: string,
|
||||||
|
},
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
wa_login_with_github: {},
|
wa_login_with_github: {},
|
||||||
wa_login_with_google: {},
|
wa_login_with_google: {},
|
||||||
wa_login_with_gitlab: {},
|
wa_login_with_gitlab: {},
|
||||||
|
|
@ -114,16 +235,24 @@ export type PosthogEventMap = {
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
wa_org_name_updated_success: {},
|
wa_org_name_updated_success: {},
|
||||||
wa_org_name_updated_fail: {
|
wa_org_name_updated_fail: {
|
||||||
errorCode: string,
|
error: string,
|
||||||
},
|
},
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
wa_org_domain_updated_success: {},
|
wa_org_domain_updated_success: {},
|
||||||
wa_org_domain_updated_fail: {
|
wa_org_domain_updated_fail: {
|
||||||
errorCode: string,
|
error: string,
|
||||||
},
|
},
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
|
wa_onboard_github_selected: {},
|
||||||
|
wa_onboard_gitlab_selected: {},
|
||||||
|
wa_onboard_gitea_selected: {},
|
||||||
|
wa_onboard_gerrit_selected: {},
|
||||||
|
wa_onboard_bitbucket_cloud_selected: {},
|
||||||
|
wa_onboard_bitbucket_server_selected: {},
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
wa_security_page_click: {},
|
wa_security_page_click: {},
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
|
wa_demo_card_click: {},
|
||||||
wa_demo_try_card_pressed: {},
|
wa_demo_try_card_pressed: {},
|
||||||
wa_share_link_created: {},
|
wa_share_link_created: {},
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
|
|
@ -133,12 +262,12 @@ export type PosthogEventMap = {
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
wa_requests_list_approve_request_success: {},
|
wa_requests_list_approve_request_success: {},
|
||||||
wa_requests_list_approve_request_fail: {
|
wa_requests_list_approve_request_fail: {
|
||||||
errorCode: string,
|
error: string,
|
||||||
},
|
},
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
wa_requests_list_reject_request_success: {},
|
wa_requests_list_reject_request_success: {},
|
||||||
wa_requests_list_reject_request_fail: {
|
wa_requests_list_reject_request_fail: {
|
||||||
errorCode: string,
|
error: string,
|
||||||
},
|
},
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
wa_api_key_created: {},
|
wa_api_key_created: {},
|
||||||
|
|
@ -152,6 +281,11 @@ export type PosthogEventMap = {
|
||||||
wa_chat_thread_created: {},
|
wa_chat_thread_created: {},
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
wa_demo_docs_link_pressed: {},
|
wa_demo_docs_link_pressed: {},
|
||||||
|
wa_demo_search_context_card_pressed: {
|
||||||
|
contextType: string,
|
||||||
|
contextName: string,
|
||||||
|
contextDisplayName: string,
|
||||||
|
},
|
||||||
wa_demo_search_example_card_pressed: {
|
wa_demo_search_example_card_pressed: {
|
||||||
exampleTitle: string,
|
exampleTitle: string,
|
||||||
exampleUrl: string,
|
exampleUrl: string,
|
||||||
|
|
|
||||||
|
|
@ -62,18 +62,10 @@ export const createPathWithQueryParams = (path: string, ...queryParams: [string,
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
const queryString = queryParams.map(([key, value]) => `${encodeURIComponent(key)}=${encodeRFC3986URIComponent(value ?? '')}`).join('&');
|
const queryString = queryParams.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value ?? '')}`).join('&');
|
||||||
return `${path}?${queryString}`;
|
return `${path}?${queryString}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent#encoding_for_rfc3986
|
|
||||||
const encodeRFC3986URIComponent = (str: string) => {
|
|
||||||
return encodeURIComponent(str).replace(
|
|
||||||
/[!'()*]/g,
|
|
||||||
(c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
type AuthProviderInfo = {
|
type AuthProviderInfo = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,7 @@ export const getAuthenticatedUser = async () => {
|
||||||
/**
|
/**
|
||||||
* Returns a API key object if the API key string is valid, otherwise returns undefined.
|
* Returns a API key object if the API key string is valid, otherwise returns undefined.
|
||||||
*/
|
*/
|
||||||
export const getVerifiedApiObject = async (apiKeyString: string): Promise<ApiKey | undefined> => {
|
const getVerifiedApiObject = async (apiKeyString: string): Promise<ApiKey | undefined> => {
|
||||||
const parts = apiKeyString.split("-");
|
const parts = apiKeyString.split("-");
|
||||||
if (parts.length !== 2 || parts[0] !== "sourcebot") {
|
if (parts.length !== 2 || parts[0] !== "sourcebot") {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
|
||||||
20
yarn.lock
20
yarn.lock
|
|
@ -3294,10 +3294,10 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@next/env@npm:15.5.9":
|
"@next/env@npm:15.5.7":
|
||||||
version: 15.5.9
|
version: 15.5.7
|
||||||
resolution: "@next/env@npm:15.5.9"
|
resolution: "@next/env@npm:15.5.7"
|
||||||
checksum: 10c0/92c4e29d81a8e78c33c2da179648a4f478a9a6852966192e079007b19ec9955e72530d5ca7df55ea0efeccbf5b1c9d0efcaf80433e26af89c6478193e1d088f1
|
checksum: 10c0/f92d99e5fa3516c6b7699abafd9bd813f5c1889dd257ab098f1b71f93137f5e4f49792e22f6dddf8a59efcb134e8e84277c983ff88607b2a42aac651bfde78ea
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
|
@ -8243,7 +8243,7 @@ __metadata:
|
||||||
langfuse-vercel: "npm:^3.38.4"
|
langfuse-vercel: "npm:^3.38.4"
|
||||||
lucide-react: "npm:^0.517.0"
|
lucide-react: "npm:^0.517.0"
|
||||||
micromatch: "npm:^4.0.8"
|
micromatch: "npm:^4.0.8"
|
||||||
next: "npm:15.5.9"
|
next: "npm:^15.5.7"
|
||||||
next-auth: "npm:^5.0.0-beta.30"
|
next-auth: "npm:^5.0.0-beta.30"
|
||||||
next-navigation-guard: "npm:^0.2.0"
|
next-navigation-guard: "npm:^0.2.0"
|
||||||
next-themes: "npm:^0.3.0"
|
next-themes: "npm:^0.3.0"
|
||||||
|
|
@ -16178,11 +16178,11 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"next@npm:15.5.9":
|
"next@npm:^15.5.7":
|
||||||
version: 15.5.9
|
version: 15.5.7
|
||||||
resolution: "next@npm:15.5.9"
|
resolution: "next@npm:15.5.7"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@next/env": "npm:15.5.9"
|
"@next/env": "npm:15.5.7"
|
||||||
"@next/swc-darwin-arm64": "npm:15.5.7"
|
"@next/swc-darwin-arm64": "npm:15.5.7"
|
||||||
"@next/swc-darwin-x64": "npm:15.5.7"
|
"@next/swc-darwin-x64": "npm:15.5.7"
|
||||||
"@next/swc-linux-arm64-gnu": "npm:15.5.7"
|
"@next/swc-linux-arm64-gnu": "npm:15.5.7"
|
||||||
|
|
@ -16233,7 +16233,7 @@ __metadata:
|
||||||
optional: true
|
optional: true
|
||||||
bin:
|
bin:
|
||||||
next: dist/bin/next
|
next: dist/bin/next
|
||||||
checksum: 10c0/6a120afbc45b96aa14debba6375602d6319093af4e3e8c648cf22b12ffb9db016c889df5e764cf5e0aa414ad60505db4e2095624a19f4b71316561076158651a
|
checksum: 10c0/baf5b9f42416c478702b3894479b3d7862bc4abf18afe0e43b7fc7ed35567b8dc6cb76cd94906505bab9013cb8d0f3370cdc0451c01ec15ae5a638d37b5ba7c7
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue