Skip to content

Commit 9cde663

Browse files
committed
feedback
1 parent ac9b6b4 commit 9cde663

File tree

11 files changed

+100
-83
lines changed

11 files changed

+100
-83
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111
- Added analytics dashboard. [#358](https://github.com/sourcebot-dev/sourcebot/pull/358)
1212

13+
### Changed
14+
- Audit logging is now enabled by default. [#358](https://github.com/sourcebot-dev/sourcebot/pull/358)
15+
1316
## [4.4.0] - 2025-06-18
1417

1518
### Added

docs/docs/configuration/audit-logs.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ action, and when the action took place.
1212

1313
This feature gives security and compliance teams the necessary information to ensure proper governance and administration of your Sourcebot deployment.
1414

15-
## Enabling Audit Logs
16-
Audit logs must be explicitly enabled by setting the `SOURCEBOT_EE_AUDIT_LOGGING_ENABLED` [environment variable](/docs/configuration/environment-variables) to `true`
15+
## Enabling/Disabling Audit Logs
16+
Audit logs are enabled by default and can be controlled with the `SOURCEBOT_EE_AUDIT_LOGGING_ENABLED` [environment variable](/docs/configuration/environment-variables).
1717

1818
## Fetching Audit Logs
1919
Audit logs are stored in the [postgres database](/docs/overview#architecture) connected to Sourcebot. To fetch all of the audit logs, you can use the following API:

docs/docs/features/analytics.mdx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ and engagement with Sourcebot.
3434
![DAU Chart](/images/dau_chart.png)
3535

3636
### Code Searches
37-
Counts the number of code search operations performed by your team. Code searches include all semantic and syntax-based queries
38-
performed through the search interface.
37+
Counts the number of code search operations performed by your team.
3938

4039
![Code Search Chart](/images/code_search_chart.png)
4140

packages/web/src/app/[domain]/browse/[...path]/components/pureCodePreviewPanel.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { BrowseHighlightRange, HIGHLIGHT_RANGE_QUERY_PARAM, useBrowseNavigation
1717
import { useBrowseState } from "../../hooks/useBrowseState";
1818
import { rangeHighlightingExtension } from "./rangeHighlightingExtension";
1919
import useCaptureEvent from "@/hooks/useCaptureEvent";
20-
import { createAuditClient } from "@/ee/features/audit/actions";
20+
import { createAuditAction } from "@/ee/features/audit/actions";
2121
import { useDomain } from "@/hooks/useDomain";
2222

2323
interface PureCodePreviewPanelProps {
@@ -137,7 +137,7 @@ export const PureCodePreviewPanel = ({
137137

138138
const onFindReferences = useCallback((symbolName: string) => {
139139
captureEvent('wa_browse_find_references_pressed', {});
140-
createAuditClient({
140+
createAuditAction({
141141
action: "user.performed_find_references",
142142
metadata: {
143143
message: symbolName,
@@ -161,7 +161,7 @@ export const PureCodePreviewPanel = ({
161161
// instead popup the bottom sheet with the list of matches.
162162
const onGotoDefinition = useCallback((symbolName: string, symbolDefinitions: SymbolDefinition[]) => {
163163
captureEvent('wa_browse_goto_definition_pressed', {});
164-
createAuditClient({
164+
createAuditAction({
165165
action: "user.performed_goto_definition",
166166
metadata: {
167167
message: symbolName,

packages/web/src/app/[domain]/components/searchBar/searchBar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip
4343
import { Toggle } from "@/components/ui/toggle";
4444
import { useDomain } from "@/hooks/useDomain";
4545
import { KeyboardShortcutHint } from "@/app/components/keyboardShortcutHint";
46-
import { createAuditClient } from "@/ee/features/audit/actions";
46+
import { createAuditAction } from "@/ee/features/audit/actions";
4747
import tailwind from "@/tailwind";
4848

4949
interface SearchBarProps {
@@ -205,7 +205,7 @@ export const SearchBar = ({
205205
setIsSuggestionsEnabled(false);
206206
setIsHistorySearchEnabled(false);
207207

208-
createAuditClient({
208+
createAuditAction({
209209
action: "user.performed_code_search",
210210
metadata: {
211211
message: query,

packages/web/src/app/[domain]/search/components/codePreviewPanel/codePreview.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ import { SymbolHoverPopup } from "@/ee/features/codeNav/components/symbolHoverPo
2121
import { symbolHoverTargetsExtension } from "@/ee/features/codeNav/components/symbolHoverPopup/symbolHoverTargetsExtension";
2222
import { useHasEntitlement } from "@/features/entitlements/useHasEntitlement";
2323
import { SymbolDefinition } from "@/ee/features/codeNav/components/symbolHoverPopup/useHoveredOverSymbolInfo";
24+
import { createAuditAction } from "@/ee/features/audit/actions";
25+
import { useDomain } from "@/hooks/useDomain";
26+
2427
import useCaptureEvent from "@/hooks/useCaptureEvent";
2528

2629
export interface CodePreviewFile {
@@ -50,6 +53,7 @@ export const CodePreview = ({
5053
const [editorRef, setEditorRef] = useState<ReactCodeMirrorRef | null>(null);
5154
const { navigateToPath } = useBrowseNavigation();
5255
const hasCodeNavEntitlement = useHasEntitlement("code-nav");
56+
const domain = useDomain();
5357

5458
const [gutterWidth, setGutterWidth] = useState(0);
5559
const theme = useCodeMirrorTheme();
@@ -116,6 +120,12 @@ export const CodePreview = ({
116120

117121
const onGotoDefinition = useCallback((symbolName: string, symbolDefinitions: SymbolDefinition[]) => {
118122
captureEvent('wa_preview_panel_goto_definition_pressed', {});
123+
createAuditAction({
124+
action: "user.performed_goto_definition",
125+
metadata: {
126+
message: symbolName,
127+
},
128+
}, domain)
119129

120130
if (symbolDefinitions.length === 0) {
121131
return;
@@ -150,10 +160,16 @@ export const CodePreview = ({
150160
}
151161
});
152162
}
153-
}, [captureEvent, file.filepath, file.language, file.revision, navigateToPath, repoName]);
163+
}, [captureEvent, file.filepath, file.language, file.revision, navigateToPath, repoName, domain]);
154164

155165
const onFindReferences = useCallback((symbolName: string) => {
156166
captureEvent('wa_preview_panel_find_references_pressed', {});
167+
createAuditAction({
168+
action: "user.performed_find_references",
169+
metadata: {
170+
message: symbolName,
171+
},
172+
}, domain)
157173

158174
navigateToPath({
159175
repoName,
@@ -171,7 +187,7 @@ export const CodePreview = ({
171187
isBottomPanelCollapsed: false,
172188
}
173189
})
174-
}, [captureEvent, file.filepath, file.language, file.revision, navigateToPath, repoName]);
190+
}, [captureEvent, file.filepath, file.language, file.revision, navigateToPath, repoName, domain]);
175191

176192
return (
177193
<div className="flex flex-col h-full">

packages/web/src/ee/features/analytics/analyticsContent.tsx

Lines changed: 59 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -305,22 +305,43 @@ function SavingsChart({ data, title, icon: Icon, period, color, gradientId, avgM
305305

306306
function LoadingSkeleton() {
307307
return (
308-
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-6">
309-
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((i) => (
310-
<Card key={i} className="bg-card border-border">
311-
<CardHeader className="pb-4">
312-
<div className="flex items-center space-x-3">
313-
<Skeleton className="h-9 w-9 rounded-lg bg-muted" />
314-
<div className="space-y-2">
315-
<Skeleton className="h-5 w-32 bg-muted" />
316-
<Skeleton className="h-8 w-24 bg-muted" />
308+
<div className="space-y-8">
309+
{[1, 2, 3].map((groupIndex) => (
310+
<div key={groupIndex} className="space-y-4">
311+
{/* Full-width chart skeleton */}
312+
<Card className="bg-card border-border shadow-lg">
313+
<CardHeader className="pb-4">
314+
<div className="flex items-center space-x-3">
315+
<Skeleton className="h-9 w-9 rounded-lg bg-muted" />
316+
<div className="space-y-2">
317+
<Skeleton className="h-5 w-32 bg-muted" />
318+
</div>
317319
</div>
318-
</div>
319-
</CardHeader>
320-
<CardContent className="pt-0">
321-
<Skeleton className="h-[240px] w-full bg-muted rounded-lg" />
322-
</CardContent>
323-
</Card>
320+
</CardHeader>
321+
<CardContent className="pt-0">
322+
<Skeleton className="h-[240px] w-full bg-muted rounded-lg" />
323+
</CardContent>
324+
</Card>
325+
326+
{/* Side-by-side charts skeleton */}
327+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
328+
{[1, 2].map((chartIndex) => (
329+
<Card key={chartIndex} className="bg-card border-border shadow-lg">
330+
<CardHeader className="pb-4">
331+
<div className="flex items-center space-x-3">
332+
<Skeleton className="h-9 w-9 rounded-lg bg-muted" />
333+
<div className="space-y-2">
334+
<Skeleton className="h-5 w-32 bg-muted" />
335+
</div>
336+
</div>
337+
</CardHeader>
338+
<CardContent className="pt-0">
339+
<Skeleton className="h-[240px] w-full bg-muted rounded-lg" />
340+
</CardContent>
341+
</Card>
342+
))}
343+
</div>
344+
</div>
324345
))}
325346
</div>
326347
)
@@ -330,13 +351,17 @@ export function AnalyticsContent() {
330351
const domain = useDomain()
331352
const { theme } = useTheme()
332353

333-
const [avgMinutesSaved, setAvgMinutesSaved] = useState(2)
334-
const [avgSalary, setAvgSalary] = useState(100000)
354+
// Store these values as strings in the state to allow us to have empty fields for better UX
355+
const [avgMinutesSaved, setAvgMinutesSaved] = useState("2")
356+
const [avgSalary, setAvgSalary] = useState("100000")
357+
const numericAvgMinutesSaved = parseFloat(avgMinutesSaved) || 0
358+
const numericAvgSalary = parseInt(avgSalary, 10) || 0
335359

336360
const {
337361
data: analyticsResponse,
338-
isLoading,
339-
error,
362+
isPending,
363+
isError,
364+
error
340365
} = useQuery({
341366
queryKey: ["analytics", domain],
342367
queryFn: () => unwrapServiceError(getAnalytics(domain)),
@@ -368,20 +393,20 @@ export function AnalyticsContent() {
368393
const totalSavings = useMemo(() => {
369394
if (!analyticsResponse) return 0
370395
const totalOperations = analyticsResponse.reduce((sum, row) => sum + row.code_searches + row.navigations, 0)
371-
const totalMinutesSaved = totalOperations * avgMinutesSaved
372-
const hourlyRate = avgSalary / (40 * 52)
396+
const totalMinutesSaved = totalOperations * numericAvgMinutesSaved
397+
const hourlyRate = numericAvgSalary / (40 * 52)
373398
return Math.round((totalMinutesSaved / 60 * hourlyRate) * 100) / 100
374-
}, [analyticsResponse, avgMinutesSaved, avgSalary])
399+
}, [analyticsResponse, numericAvgMinutesSaved, numericAvgSalary])
375400

376-
if (isLoading) {
401+
if (isPending) {
377402
return (
378403
<div className="min-h-screen bg-background p-6">
379404
<LoadingSkeleton />
380405
</div>
381406
)
382407
}
383408

384-
if (error) {
409+
if (isError) {
385410
return (
386411
<div className="min-h-screen bg-background flex items-center justify-center">
387412
<Card className="bg-destructive/10 border-destructive/20 p-8">
@@ -397,22 +422,6 @@ export function AnalyticsContent() {
397422
)
398423
}
399424

400-
if (!analyticsResponse) {
401-
return (
402-
<div className="min-h-screen bg-background flex items-center justify-center">
403-
<Card className="bg-card border-border p-8">
404-
<div className="text-center">
405-
<div className="p-3 rounded-full bg-muted/50 w-fit mx-auto mb-4">
406-
<Activity className="h-8 w-8 text-muted-foreground" />
407-
</div>
408-
<h3 className="text-xl font-semibold text-card-foreground mb-2">No Data Available</h3>
409-
<p className="text-muted-foreground">No analytics data available for this domain</p>
410-
</div>
411-
</Card>
412-
</div>
413-
)
414-
}
415-
416425
const dailyData = analyticsResponse.filter((row) => row.period === "day")
417426
const weeklyData = analyticsResponse.filter((row) => row.period === "week")
418427
const monthlyData = analyticsResponse.filter((row) => row.period === "month")
@@ -563,10 +572,10 @@ export function AnalyticsContent() {
563572
<Input
564573
id="avgMinutesSaved"
565574
type="number"
566-
min="0.1"
575+
min="0"
567576
step="0.1"
568577
value={avgMinutesSaved}
569-
onChange={(e) => setAvgMinutesSaved(parseFloat(e.target.value) || 0)}
578+
onChange={(e) => setAvgMinutesSaved(e.target.value)}
570579
placeholder="2"
571580
/>
572581
<p className="text-xs text-muted-foreground">Estimated time saved per search or navigation operation</p>
@@ -576,10 +585,10 @@ export function AnalyticsContent() {
576585
<Input
577586
id="avgSalary"
578587
type="number"
579-
min="10000"
588+
min="0"
580589
step="1000"
581590
value={avgSalary}
582-
onChange={(e) => setAvgSalary(parseInt(e.target.value) || 0)}
591+
onChange={(e) => setAvgSalary(e.target.value)}
583592
placeholder="100000"
584593
/>
585594
<p className="text-xs text-muted-foreground">Average annual salary of your engineering team</p>
@@ -608,8 +617,8 @@ export function AnalyticsContent() {
608617
period="day"
609618
color={getColor("savings")}
610619
gradientId="dailySavings"
611-
avgMinutesSaved={avgMinutesSaved}
612-
avgSalary={avgSalary}
620+
avgMinutesSaved={numericAvgMinutesSaved}
621+
avgSalary={numericAvgSalary}
613622
/>
614623

615624
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
@@ -620,8 +629,8 @@ export function AnalyticsContent() {
620629
period="week"
621630
color={getColor("savings")}
622631
gradientId="weeklySavings"
623-
avgMinutesSaved={avgMinutesSaved}
624-
avgSalary={avgSalary}
632+
avgMinutesSaved={numericAvgMinutesSaved}
633+
avgSalary={numericAvgSalary}
625634
/>
626635
<SavingsChart
627636
data={monthlyData}
@@ -630,8 +639,8 @@ export function AnalyticsContent() {
630639
period="month"
631640
color={getColor("savings")}
632641
gradientId="monthlySavings"
633-
avgMinutesSaved={avgMinutesSaved}
634-
avgSalary={avgSalary}
642+
avgMinutesSaved={numericAvgMinutesSaved}
643+
avgSalary={numericAvgSalary}
635644
/>
636645
</div>
637646
</div>

packages/web/src/ee/features/analytics/analyticsEntitlementMessage.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,23 @@ import { BarChart3, Mail } from "lucide-react"
77
export function AnalyticsEntitlementMessage() {
88
return (
99
<div className="flex items-center justify-center min-h-[60vh] py-12">
10-
<Card className="w-full max-w-lg bg-slate-900 border-slate-800 shadow-xl p-2">
10+
<Card className="w-full max-w-lg bg-card border-border shadow-xl p-2">
1111
<CardHeader className="text-center pb-4">
1212
<div className="flex justify-center mb-4">
13-
<div className="p-3 rounded-full bg-slate-800">
14-
<BarChart3 className="h-8 w-8 text-slate-400" />
13+
<div className="p-3 rounded-full bg-muted">
14+
<BarChart3 className="h-8 w-8 text-muted-foreground" />
1515
</div>
1616
</div>
17-
<CardTitle className="text-xl font-semibold text-white">
17+
<CardTitle className="text-xl font-semibold text-card-foreground">
1818
Analytics is an Enterprise Feature
1919
</CardTitle>
20-
<CardDescription className="text-slate-400 mt-2">
21-
Get insights into your organization&apos;s usage patterns and user activity with our analytics dashboard.
20+
<CardDescription className="text-muted-foreground mt-2">
21+
Get insights into your organization&apos;s usage patterns and activity. <a href="https://docs.sourcebot.dev/docs/features/analytics" target="_blank" rel="noopener" className="text-primary hover:underline">Learn more</a>
2222
</CardDescription>
2323
</CardHeader>
2424
<CardContent className="text-center space-y-4">
25-
<div className="bg-slate-800/50 rounded-lg p-4 border border-slate-700">
26-
<p className="text-sm text-slate-300">
25+
<div className="bg-muted/50 rounded-lg p-4 border border-border">
26+
<p className="text-sm text-muted-foreground">
2727
Want to try out Sourcebot&apos;s enterprise features? Reach out to us and we&apos;ll get back to you within
2828
a couple hours with a trial license.
2929
</p>

packages/web/src/ee/features/audit/actions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { AuditEvent } from "./types";
1313
const auditService = getAuditService();
1414
const logger = createLogger('audit-utils');
1515

16-
export const createAuditClient = async (event: Omit<AuditEvent, 'sourcebotVersion' | 'orgId' | 'actor' | 'target'>, domain: string) => sew(async () =>
16+
export const createAuditAction = async (event: Omit<AuditEvent, 'sourcebotVersion' | 'orgId' | 'actor' | 'target'>, domain: string) => sew(async () =>
1717
withAuth((userId) =>
1818
withOrgMembership(userId, domain, async ({ org }) => {
1919
await auditService.createAudit({ ...event, orgId: org.id, actor: { id: userId, type: "user" }, target: { id: org.id.toString(), type: "org" } })
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { IAuditService } from '@/ee/features/audit/types';
22
import { MockAuditService } from '@/ee/features/audit/mockAuditService';
33
import { AuditService } from '@/ee/features/audit/auditService';
4+
import { hasEntitlement } from '@sourcebot/shared';
45
import { env } from '@/env.mjs';
56

67
let enterpriseService: IAuditService | undefined;
78

89
export function getAuditService(): IAuditService {
9-
enterpriseService = enterpriseService ?? (env.SOURCEBOT_EE_AUDIT_LOGGING_ENABLED === 'true' ? new AuditService() : new MockAuditService());
10+
const auditLogsEnabled = (env.SOURCEBOT_EE_AUDIT_LOGGING_ENABLED === 'true') && hasEntitlement("audit");
11+
enterpriseService = enterpriseService ?? (auditLogsEnabled ? new AuditService() : new MockAuditService());
1012
return enterpriseService;
1113
}

0 commit comments

Comments
 (0)