diff --git a/CHANGELOG.md b/CHANGELOG.md index ddf0a10c1..222887e9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Links in Ask Sourcebot chat responses now open in a new tab with a subtle external link icon indicator. [#1059](https://github.com/sourcebot-dev/sourcebot/pull/1059) +- Removed `/~` from the root of most app URLs. [#1076](https://github.com/sourcebot-dev/sourcebot/pull/1076) ## [4.16.7] - 2026-04-03 diff --git a/packages/db/prisma/migrations/20260402052154_remove_domain_from_org/migration.sql b/packages/db/prisma/migrations/20260402052154_remove_domain_from_org/migration.sql new file mode 100644 index 000000000..2f22971e4 --- /dev/null +++ b/packages/db/prisma/migrations/20260402052154_remove_domain_from_org/migration.sql @@ -0,0 +1,11 @@ +/* + Warnings: + + - You are about to drop the column `domain` on the `Org` table. All the data in the column will be lost. + +*/ +-- DropIndex +DROP INDEX "Org_domain_key"; + +-- AlterTable +ALTER TABLE "Org" DROP COLUMN "domain"; diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma index 0c58714b6..3a96eea6e 100644 --- a/packages/db/prisma/schema.prisma +++ b/packages/db/prisma/schema.prisma @@ -265,7 +265,6 @@ model AccountRequest { model Org { id Int @id @default(autoincrement()) name String - domain String @unique createdAt DateTime @default(now()) updatedAt DateTime @updatedAt members UserToOrg[] diff --git a/packages/web/src/__mocks__/prisma.ts b/packages/web/src/__mocks__/prisma.ts index acf687294..a2d11360e 100644 --- a/packages/web/src/__mocks__/prisma.ts +++ b/packages/web/src/__mocks__/prisma.ts @@ -1,4 +1,4 @@ -import { SINGLE_TENANT_ORG_DOMAIN, SINGLE_TENANT_ORG_ID, SINGLE_TENANT_ORG_NAME } from '@/lib/constants'; +import { SINGLE_TENANT_ORG_ID, SINGLE_TENANT_ORG_NAME } from '@/lib/constants'; import { Account, ApiKey, OAuthRefreshToken, OAuthToken, Org, PrismaClient, User } from '@prisma/client'; import { beforeEach, vi } from 'vitest'; import { mockDeep, mockReset } from 'vitest-mock-extended'; @@ -8,11 +8,11 @@ beforeEach(() => { }); export const prisma = mockDeep(); +export const __unsafePrisma = prisma; export const MOCK_ORG: Org = { id: SINGLE_TENANT_ORG_ID, name: SINGLE_TENANT_ORG_NAME, - domain: SINGLE_TENANT_ORG_DOMAIN, createdAt: new Date(), updatedAt: new Date(), isOnboarded: true, diff --git a/packages/web/src/actions.ts b/packages/web/src/actions.ts index ad5d5f24e..939ba442c 100644 --- a/packages/web/src/actions.ts +++ b/packages/web/src/actions.ts @@ -4,12 +4,12 @@ import { getAuditService } from "@/ee/features/audit/factory"; import { env, getSMTPConnectionURL } from "@sourcebot/shared"; import { addUserToOrganization, orgHasAvailability } from "@/lib/authUtils"; import { ErrorCode } from "@/lib/errorCodes"; -import { notFound, orgNotFound, ServiceError } from "@/lib/serviceError"; +import { notAuthenticated, notFound, orgNotFound, ServiceError } from "@/lib/serviceError"; import { getOrgMetadata, isHttpError, isServiceError } from "@/lib/utils"; -import { prisma } from "@/prisma"; +import { __unsafePrisma } from "@/prisma"; import { render } from "@react-email/components"; -import { generateApiKey, getTokenFromConfig, hashSecret } from "@sourcebot/shared"; -import { ApiKey, ConnectionSyncJobStatus, OrgRole, Prisma, RepoIndexingJobStatus, RepoIndexingJobType } from "@sourcebot/db"; +import { generateApiKey, getTokenFromConfig } from "@sourcebot/shared"; +import { ConnectionSyncJobStatus, OrgRole, Prisma, RepoIndexingJobStatus, RepoIndexingJobType } from "@sourcebot/db"; import { createLogger } from "@sourcebot/shared"; import { GiteaConnectionConfig } from "@sourcebot/schemas/v3/gitea.type"; import { GithubConnectionConfig } from "@sourcebot/schemas/v3/github.type"; @@ -19,15 +19,14 @@ import { StatusCodes } from "http-status-codes"; import { cookies } from "next/headers"; import { createTransport } from "nodemailer"; import { Octokit } from "octokit"; -import { getOrgFromDomain } from "./data/org"; import InviteUserEmail from "./emails/inviteUserEmail"; import JoinRequestApprovedEmail from "./emails/joinRequestApprovedEmail"; import JoinRequestSubmittedEmail from "./emails/joinRequestSubmittedEmail"; -import { AGENTIC_SEARCH_TUTORIAL_DISMISSED_COOKIE_NAME, MOBILE_UNSUPPORTED_SPLASH_SCREEN_DISMISSED_COOKIE_NAME, SOURCEBOT_SUPPORT_EMAIL } from "./lib/constants"; -import { ApiKeyPayload, RepositoryQuery } from "./lib/types"; -import { withAuth, withOptionalAuth, withAuth_skipOrgMembershipCheck } from "./middleware/withAuth"; +import { AGENTIC_SEARCH_TUTORIAL_DISMISSED_COOKIE_NAME, MOBILE_UNSUPPORTED_SPLASH_SCREEN_DISMISSED_COOKIE_NAME, SINGLE_TENANT_ORG_ID, SOURCEBOT_SUPPORT_EMAIL } from "./lib/constants"; +import { RepositoryQuery } from "./lib/types"; +import { getAuthenticatedUser, withAuth, withOptionalAuth } from "./middleware/withAuth"; import { withMinimumOrgRole } from "./middleware/withMinimumOrgRole"; -import { getBrowsePath } from "./app/[domain]/browse/hooks/utils"; +import { getBrowsePath } from "./app/(app)/browse/hooks/utils"; import { sew } from "@/middleware/sew"; const logger = createLogger('web-actions'); @@ -48,59 +47,6 @@ export const completeOnboarding = async (): Promise<{ success: boolean } | Servi } })); -export const verifyApiKey = async (apiKeyPayload: ApiKeyPayload): Promise<{ apiKey: ApiKey } | ServiceError> => sew(async () => { - const parts = apiKeyPayload.apiKey.split("-"); - if (parts.length !== 2 || parts[0] !== "sourcebot") { - return { - statusCode: StatusCodes.BAD_REQUEST, - errorCode: ErrorCode.INVALID_API_KEY, - message: "Invalid API key", - } satisfies ServiceError; - } - - const hash = hashSecret(parts[1]) - const apiKey = await prisma.apiKey.findUnique({ - where: { - hash, - }, - }); - - if (!apiKey) { - return { - statusCode: StatusCodes.UNAUTHORIZED, - errorCode: ErrorCode.INVALID_API_KEY, - message: "Invalid API key", - } satisfies ServiceError; - } - - const apiKeyTargetOrg = await prisma.org.findUnique({ - where: { - domain: apiKeyPayload.domain, - }, - }); - - if (!apiKeyTargetOrg) { - return { - statusCode: StatusCodes.UNAUTHORIZED, - errorCode: ErrorCode.INVALID_API_KEY, - message: `Invalid API key payload. Provided domain ${apiKeyPayload.domain} does not exist.`, - } satisfies ServiceError; - } - - if (apiKey.orgId !== apiKeyTargetOrg.id) { - return { - statusCode: StatusCodes.UNAUTHORIZED, - errorCode: ErrorCode.INVALID_API_KEY, - message: `Invalid API key payload. Provided domain ${apiKeyPayload.domain} does not match the API key's org.`, - } satisfies ServiceError; - } - - return { - apiKey, - } -}); - - export const createApiKey = async (name: string): Promise<{ key: string } | ServiceError> => sew(() => withAuth(async ({ org, user, role, prisma }) => { if ((env.DISABLE_API_KEY_CREATION_FOR_NON_OWNER_USERS === 'true' || env.DISABLE_API_KEY_USAGE_FOR_NON_OWNER_USERS === 'true') && role !== OrgRole.OWNER) { @@ -188,7 +134,7 @@ export const deleteApiKey = async (name: string): Promise<{ success: boolean } | type: "user" }, target: { - id: org.domain, + id: org.id.toString(), type: "org" }, orgId: org.id, @@ -600,7 +546,7 @@ export const createInvites = async (emails: string[]): Promise<{ success: boolea }); } - const hasAvailability = await orgHasAvailability(org.domain); + const hasAvailability = await orgHasAvailability(); if (!hasAvailability) { await auditService.createAudit({ action: "user.invite_failed", @@ -807,123 +753,11 @@ export const getMe = async () => sew(() => memberships: userWithOrgs.orgs.map((org) => ({ id: org.orgId, role: org.role, - domain: org.org.domain, name: org.org.name, })) } })); -export const redeemInvite = async (inviteId: string): Promise<{ success: boolean } | ServiceError> => sew(() => - withAuth_skipOrgMembershipCheck(async ({ user, prisma }) => { - const invite = await prisma.invite.findUnique({ - where: { - id: inviteId, - }, - include: { - org: true, - } - }); - - if (!invite) { - return notFound(); - } - - const failAuditCallback = async (error: string) => { - await auditService.createAudit({ - action: "user.invite_accept_failed", - actor: { - id: user.id, - type: "user" - }, - target: { - id: inviteId, - type: "invite" - }, - orgId: invite.org.id, - metadata: { - message: error - } - }); - } - - - const hasAvailability = await orgHasAvailability(invite.org.domain); - if (!hasAvailability) { - await failAuditCallback("Organization is at max capacity"); - return { - statusCode: StatusCodes.BAD_REQUEST, - errorCode: ErrorCode.ORG_SEAT_COUNT_REACHED, - message: "Organization is at max capacity", - } satisfies ServiceError; - } - - // Check if the user is the recipient of the invite - if (user.email !== invite.recipientEmail) { - await failAuditCallback("User is not the recipient of the invite"); - return notFound(); - } - - const addUserToOrgRes = await addUserToOrganization(user.id, invite.orgId); - if (isServiceError(addUserToOrgRes)) { - await failAuditCallback(addUserToOrgRes.message); - return addUserToOrgRes; - } - - await auditService.createAudit({ - action: "user.invite_accepted", - actor: { - id: user.id, - type: "user" - }, - orgId: invite.org.id, - target: { - id: inviteId, - type: "invite" - } - }); - - return { - success: true, - } - })); - -export const getInviteInfo = async (inviteId: string) => sew(() => - withAuth_skipOrgMembershipCheck(async ({ user, prisma }) => { - const invite = await prisma.invite.findUnique({ - where: { - id: inviteId, - }, - include: { - org: true, - host: true, - } - }); - - if (!invite) { - return notFound(); - } - - if (invite.recipientEmail !== user.email) { - return notFound(); - } - - return { - id: invite.id, - orgName: invite.org.name, - orgImageUrl: invite.org.imageUrl ?? undefined, - orgDomain: invite.org.domain, - host: { - name: invite.host.name ?? undefined, - email: invite.host.email!, - avatarUrl: invite.host.image ?? undefined, - }, - recipient: { - name: user.name ?? undefined, - email: user.email!, - } - } - })); - export const getOrgMembers = async () => sew(() => withAuth(async ({ org, prisma }) => { const members = await prisma.userToOrg.findMany({ @@ -983,20 +817,17 @@ export const getOrgAccountRequests = async () => sew(() => })); })); -export const createAccountRequest = async (userId: string, domain: string) => sew(async () => { - const user = await prisma.user.findUnique({ - where: { - id: userId, - }, - }); - - if (!user) { - return notFound("User not found"); +export const createAccountRequest = async () => sew(async () => { + const authResult = await getAuthenticatedUser(); + if (!authResult) { + return notAuthenticated(); } - const org = await prisma.org.findUnique({ + const { user } = authResult; + + const org = await __unsafePrisma.org.findUnique({ where: { - domain, + id: SINGLE_TENANT_ORG_ID, }, }); @@ -1004,17 +835,17 @@ export const createAccountRequest = async (userId: string, domain: string) => se return notFound("Organization not found"); } - const existingRequest = await prisma.accountRequest.findUnique({ + const existingRequest = await __unsafePrisma.accountRequest.findUnique({ where: { requestedById_orgId: { - requestedById: userId, + requestedById: user.id, orgId: org.id, }, }, }); if (existingRequest) { - logger.warn(`User ${userId} already has an account request for org ${org.id}. Skipping account request creation.`); + logger.warn(`User ${user.id} already has an account request for org ${org.id}. Skipping account request creation.`); return { success: true, existingRequest: true, @@ -1022,9 +853,9 @@ export const createAccountRequest = async (userId: string, domain: string) => se } if (!existingRequest) { - await prisma.accountRequest.create({ + await __unsafePrisma.accountRequest.create({ data: { - requestedById: userId, + requestedById: user.id, orgId: org.id, }, }); @@ -1035,7 +866,7 @@ export const createAccountRequest = async (userId: string, domain: string) => se // on user creation (the header isn't set when next-auth calls onCreateUser for some reason) const deploymentUrl = env.AUTH_URL; - const owner = await prisma.user.findFirst({ + const owners = await __unsafePrisma.user.findMany({ where: { orgs: { some: { @@ -1046,8 +877,8 @@ export const createAccountRequest = async (userId: string, domain: string) => se }, }); - if (!owner) { - logger.error(`Failed to find owner for org ${org.id} when drafting email for account request from ${userId}`); + if (owners.length === 0) { + logger.error(`Failed to find any owners for org ${org.id} when drafting email for account request from ${user.id}`); } else { const html = await render(JoinRequestSubmittedEmail({ baseUrl: deploymentUrl, @@ -1057,13 +888,16 @@ export const createAccountRequest = async (userId: string, domain: string) => se avatarUrl: user.image ?? undefined, }, orgName: org.name, - orgDomain: org.domain, orgImageUrl: org.imageUrl ?? undefined, })); + const ownerEmails = owners + .map((owner) => owner.email) + .filter((email): email is string => email !== null); + const transport = createTransport(smtpConnectionUrl); const result = await transport.sendMail({ - to: owner.email!, + to: ownerEmails, from: env.EMAIL_FROM_ADDRESS, subject: `New account request for ${org.name} on Sourcebot`, html, @@ -1072,7 +906,7 @@ export const createAccountRequest = async (userId: string, domain: string) => se const failed = result.rejected.concat(result.pending).filter(Boolean); if (failed.length > 0) { - logger.error(`Failed to send account request email to ${owner.email}: ${failed}`); + logger.error(`Failed to send account request email to ${ownerEmails.join(', ')}: ${failed}`); } } } else { @@ -1086,10 +920,10 @@ export const createAccountRequest = async (userId: string, domain: string) => se } }); -export const getMemberApprovalRequired = async (domain: string): Promise => sew(async () => { - const org = await prisma.org.findUnique({ +export const getMemberApprovalRequired = async (): Promise => sew(async () => { + const org = await __unsafePrisma.org.findUnique({ where: { - domain, + id: SINGLE_TENANT_ORG_ID, }, }); @@ -1182,7 +1016,6 @@ export const approveAccountRequest = async (requestId: string) => sew(async () = avatarUrl: request.requestedBy.image ?? undefined, }, orgName: org.name, - orgDomain: org.domain })); const transport = createTransport(smtpConnectionUrl); @@ -1191,7 +1024,7 @@ export const approveAccountRequest = async (requestId: string) => sew(async () = from: env.EMAIL_FROM_ADDRESS, subject: `Your request to join ${org.name} has been approved`, html, - text: `Your request to join ${org.name} on Sourcebot has been approved. You can now access the organization at ${env.AUTH_URL}/${org.domain}`, + text: `Your request to join ${org.name} on Sourcebot has been approved. You can now access the organization at ${env.AUTH_URL}`, }); const failed = result.rejected.concat(result.pending).filter(Boolean); @@ -1334,8 +1167,10 @@ export const getRepoImage = async (repoId: number): Promise => sew(async () => { - const org = await getOrgFromDomain(domain); +export const getAnonymousAccessStatus = async (): Promise => sew(async () => { + const org = await __unsafePrisma.org.findUnique({ + where: { id: SINGLE_TENANT_ORG_ID }, + }); if (!org) { return { statusCode: StatusCodes.NOT_FOUND, diff --git a/packages/web/src/app/(app)/CLAUDE.md b/packages/web/src/app/(app)/CLAUDE.md new file mode 100644 index 000000000..8c4793dc6 --- /dev/null +++ b/packages/web/src/app/(app)/CLAUDE.md @@ -0,0 +1,25 @@ +# (app) Route Group + +## Auth in pages + +Use `authenticatedPage` from `@/middleware/authenticatedPage` for all pages in this directory. Do NOT use `SINGLE_TENANT_ORG_ID` or direct `prisma` imports from `@/prisma` to look up the org — use the `org` and `prisma` provided by the auth context instead. + +```tsx +import { authenticatedPage } from "@/middleware/authenticatedPage"; + +export default authenticatedPage(async ({ org, role, prisma }, props) => { + // ... +}); +``` + +Options: +- `{ minRole: OrgRole.OWNER, redirectTo: '/settings' }` — gate by role +- `{ allowAnonymous: true }` — allow unauthenticated access (user may be undefined) + +## Layout + +The `layout.tsx` in this directory handles authentication, org membership, onboarding, and SSO account linking. Pages do not need to re-check these. See `README.md` for the full guard pipeline. + +## Adding new routes + +New pages automatically inherit the layout's auth/membership guard. Use `authenticatedPage` if the page needs the auth context (org, user, role, prisma) or role-based gating. diff --git a/packages/web/src/app/(app)/README.md b/packages/web/src/app/(app)/README.md new file mode 100644 index 000000000..d281a021f --- /dev/null +++ b/packages/web/src/app/(app)/README.md @@ -0,0 +1,52 @@ +# (app) Route Group + +This is a Next.js [route group](https://nextjs.org/docs/app/building-your-application/routing/route-groups). The parenthesized folder name does not affect the URL structure. Routes here are served at the root (e.g., `/search`, `/chat`, `/settings`). + +## Why this route group exists + +Routes outside `(app)/` (like `/login`, `/signup`, `/invite`, `/onboard`) are accessible without authentication. Routes inside `(app)/` go through the layout's auth and membership guards before rendering. + +## What the layout does + +The `layout.tsx` acts as a gate and app shell. It runs the following checks in order, short-circuiting if any condition is met: + +1. **Org existence** - Looks up the single-tenant org by `SINGLE_TENANT_ORG_ID`. Returns 404 if missing. +2. **Authentication** - If the user is not logged in and anonymous access is not enabled, redirects to `/login` (or renders GCP IAP auth if configured). +3. **Membership** - If the user is logged in but not a member of the org, renders one of: + - `JoinOrganizationCard` if the org does not require approval + - `SubmitJoinRequest` / `PendingApprovalCard` if the org requires approval +4. **Onboarding** - If the org has not completed onboarding, wraps children in `OnboardGuard`. +5. **SSO account linking** - If required SSO providers are not linked, renders `ConnectAccountsCard`. +6. **Mobile splash screen** - Shows an unsupported screen on mobile devices (dismissible via cookie). + +After all guards pass, the layout wraps children with shared UI: `SyntaxGuideProvider`, `PermissionSyncBanner`, `GitHubStarToast`, and `UpgradeToast`. + +## What the layout does NOT do + +- **Role-based access control** - The layout does not check `OWNER` vs `MEMBER`. Pages that require a specific role should use `authenticatedPage` with the `minRole` option. +- **Guarantee a user exists** - When anonymous access is enabled, the user may be undefined. + +## Writing pages in (app) + +Use the `authenticatedPage` HOC from `@/middleware/authenticatedPage`. It resolves the auth context (`user`, `org`, `role`, `prisma`) and handles redirects on auth failure. This avoids manual org lookups with `SINGLE_TENANT_ORG_ID` — pages inside `(app)/` should not reference that constant directly. + +```tsx +import { authenticatedPage } from "@/middleware/authenticatedPage"; + +// Basic authenticated page +export default authenticatedPage(async ({ prisma }) => { + const data = await prisma.repo.findMany(); + return ; +}); + +// Owner-only page +export default authenticatedPage(async ({ org }) => { + return ; +}, { minRole: OrgRole.OWNER, redirectTo: '/settings' }); + +// Page that allows anonymous access +export default authenticatedPage(async ({ user, prisma }) => { + // user may be undefined + return ; +}, { allowAnonymous: true }); +``` diff --git a/packages/web/src/app/[domain]/agents/page.tsx b/packages/web/src/app/(app)/agents/page.tsx similarity index 93% rename from packages/web/src/app/[domain]/agents/page.tsx rename to packages/web/src/app/(app)/agents/page.tsx index fd564268a..523198ef6 100644 --- a/packages/web/src/app/[domain]/agents/page.tsx +++ b/packages/web/src/app/(app)/agents/page.tsx @@ -13,16 +13,10 @@ const agents = [ }, ]; -export default async function AgentsPage(props: { params: Promise<{ domain: string }> }) { - const params = await props.params; - - const { - domain - } = params; - +export default async function AgentsPage() { return (
- +
{ // 1. Look up repo by owner/repo const displayName = `${owner}/${repo}`; - const existingRepo = await prisma.repo.findFirst({ + const existingRepo = await __unsafePrisma.repo.findFirst({ where: { - orgId: SINGLE_TENANT_ORG_ID, displayName: displayName, external_codeHostType: 'github', external_codeHostUrl: 'https://github.com', diff --git a/packages/web/src/app/[domain]/askgh/[owner]/[repo]/types.ts b/packages/web/src/app/(app)/askgh/[owner]/[repo]/types.ts similarity index 100% rename from packages/web/src/app/[domain]/askgh/[owner]/[repo]/types.ts rename to packages/web/src/app/(app)/askgh/[owner]/[repo]/types.ts diff --git a/packages/web/src/app/[domain]/askgh/layout.tsx b/packages/web/src/app/(app)/askgh/layout.tsx similarity index 69% rename from packages/web/src/app/[domain]/askgh/layout.tsx rename to packages/web/src/app/(app)/askgh/layout.tsx index ecf9a665a..6ac30a3b3 100644 --- a/packages/web/src/app/[domain]/askgh/layout.tsx +++ b/packages/web/src/app/(app)/askgh/layout.tsx @@ -3,11 +3,9 @@ import { env } from "@sourcebot/shared"; export default async function AskGHLayout(props: { children: React.ReactNode; - params: Promise<{ domain: string }>; }) { - const params = await props.params; if (env.EXPERIMENT_ASK_GH_ENABLED !== 'true') { - redirect(`/${params.domain}`); + redirect('/'); } return <>{props.children}; diff --git a/packages/web/src/app/[domain]/browse/README.md b/packages/web/src/app/(app)/browse/README.md similarity index 100% rename from packages/web/src/app/[domain]/browse/README.md rename to packages/web/src/app/(app)/browse/README.md diff --git a/packages/web/src/app/[domain]/browse/[...path]/components/codePreviewPanel.tsx b/packages/web/src/app/(app)/browse/[...path]/components/codePreviewPanel.tsx similarity index 98% rename from packages/web/src/app/[domain]/browse/[...path]/components/codePreviewPanel.tsx rename to packages/web/src/app/(app)/browse/[...path]/components/codePreviewPanel.tsx index 15e2f6052..6f5725783 100644 --- a/packages/web/src/app/[domain]/browse/[...path]/components/codePreviewPanel.tsx +++ b/packages/web/src/app/(app)/browse/[...path]/components/codePreviewPanel.tsx @@ -1,5 +1,5 @@ import { getRepoInfoByName } from "@/actions"; -import { PathHeader } from "@/app/[domain]/components/pathHeader"; +import { PathHeader } from "@/app/(app)/components/pathHeader"; import { Separator } from "@/components/ui/separator"; import { cn, getCodeHostInfoForRepo, isServiceError } from "@/lib/utils"; import Image from "next/image"; diff --git a/packages/web/src/app/[domain]/browse/[...path]/components/pureCodePreviewPanel.tsx b/packages/web/src/app/(app)/browse/[...path]/components/pureCodePreviewPanel.tsx similarity index 100% rename from packages/web/src/app/[domain]/browse/[...path]/components/pureCodePreviewPanel.tsx rename to packages/web/src/app/(app)/browse/[...path]/components/pureCodePreviewPanel.tsx diff --git a/packages/web/src/app/[domain]/browse/[...path]/components/pureTreePreviewPanel.tsx b/packages/web/src/app/(app)/browse/[...path]/components/pureTreePreviewPanel.tsx similarity index 93% rename from packages/web/src/app/[domain]/browse/[...path]/components/pureTreePreviewPanel.tsx rename to packages/web/src/app/(app)/browse/[...path]/components/pureTreePreviewPanel.tsx index 6b714fb99..ca6bb7985 100644 --- a/packages/web/src/app/[domain]/browse/[...path]/components/pureTreePreviewPanel.tsx +++ b/packages/web/src/app/(app)/browse/[...path]/components/pureTreePreviewPanel.tsx @@ -1,7 +1,7 @@ 'use client'; import { useRef } from "react"; -import { FileTreeItemComponent } from "@/app/[domain]/browse/components/fileTreeItemComponent"; +import { FileTreeItemComponent } from "@/app/(app)/browse/components/fileTreeItemComponent"; import { getBrowsePath } from "../../hooks/utils"; import { ScrollArea } from "@/components/ui/scroll-area"; import { useBrowseParams } from "../../hooks/useBrowseParams"; diff --git a/packages/web/src/app/[domain]/browse/[...path]/components/rangeHighlightingExtension.ts b/packages/web/src/app/(app)/browse/[...path]/components/rangeHighlightingExtension.ts similarity index 100% rename from packages/web/src/app/[domain]/browse/[...path]/components/rangeHighlightingExtension.ts rename to packages/web/src/app/(app)/browse/[...path]/components/rangeHighlightingExtension.ts diff --git a/packages/web/src/app/[domain]/browse/[...path]/components/treePreviewPanel.tsx b/packages/web/src/app/(app)/browse/[...path]/components/treePreviewPanel.tsx similarity index 96% rename from packages/web/src/app/[domain]/browse/[...path]/components/treePreviewPanel.tsx rename to packages/web/src/app/(app)/browse/[...path]/components/treePreviewPanel.tsx index 90afe2916..5762e0f50 100644 --- a/packages/web/src/app/[domain]/browse/[...path]/components/treePreviewPanel.tsx +++ b/packages/web/src/app/(app)/browse/[...path]/components/treePreviewPanel.tsx @@ -1,7 +1,7 @@ import { Separator } from "@/components/ui/separator"; import { getRepoInfoByName } from "@/actions"; -import { PathHeader } from "@/app/[domain]/components/pathHeader"; +import { PathHeader } from "@/app/(app)/components/pathHeader"; import { getFolderContents } from "@/features/git/getFolderContentsApi"; import { isServiceError } from "@/lib/utils"; import { PureTreePreviewPanel } from "./pureTreePreviewPanel"; diff --git a/packages/web/src/app/[domain]/browse/[...path]/page.tsx b/packages/web/src/app/(app)/browse/[...path]/page.tsx similarity index 99% rename from packages/web/src/app/[domain]/browse/[...path]/page.tsx rename to packages/web/src/app/(app)/browse/[...path]/page.tsx index 2cadab600..df0174432 100644 --- a/packages/web/src/app/[domain]/browse/[...path]/page.tsx +++ b/packages/web/src/app/(app)/browse/[...path]/page.tsx @@ -44,7 +44,6 @@ const parsePathForTitle = (path: string[]): string => { type Props = { params: Promise<{ - domain: string; path: string[]; }>; }; diff --git a/packages/web/src/app/[domain]/browse/browseStateProvider.tsx b/packages/web/src/app/(app)/browse/browseStateProvider.tsx similarity index 100% rename from packages/web/src/app/[domain]/browse/browseStateProvider.tsx rename to packages/web/src/app/(app)/browse/browseStateProvider.tsx diff --git a/packages/web/src/app/[domain]/browse/components/bottomPanel.tsx b/packages/web/src/app/(app)/browse/components/bottomPanel.tsx similarity index 97% rename from packages/web/src/app/[domain]/browse/components/bottomPanel.tsx rename to packages/web/src/app/(app)/browse/components/bottomPanel.tsx index abd166d40..b8004aa51 100644 --- a/packages/web/src/app/[domain]/browse/components/bottomPanel.tsx +++ b/packages/web/src/app/(app)/browse/components/bottomPanel.tsx @@ -13,7 +13,6 @@ import { ImperativePanelHandle } from "react-resizable-panels"; import { useBrowseState } from "../hooks/useBrowseState"; import { ExploreMenu } from "@/ee/features/codeNav/components/exploreMenu"; import Link from "next/link"; -import { useDomain } from "@/hooks/useDomain"; import { useRouter } from "next/navigation"; export const BOTTOM_PANEL_MIN_SIZE = 35; @@ -27,7 +26,6 @@ interface BottomPanelProps { export const BottomPanel = ({ order }: BottomPanelProps) => { const panelRef = useRef(null); const hasCodeNavEntitlement = useHasEntitlement("code-nav"); - const domain = useDomain(); const router = useRouter(); const { @@ -105,7 +103,7 @@ export const BottomPanel = ({ order }: BottomPanelProps) => {

- Code navigation is not enabled for router.push(`/${domain}/settings/license`)}>your plan. + Code navigation is not enabled for router.push(`/settings/license`)}>your plan.

{ return ( diff --git a/packages/web/src/app/[domain]/browse/hooks/useBrowseNavigation.ts b/packages/web/src/app/(app)/browse/hooks/useBrowseNavigation.ts similarity index 100% rename from packages/web/src/app/[domain]/browse/hooks/useBrowseNavigation.ts rename to packages/web/src/app/(app)/browse/hooks/useBrowseNavigation.ts diff --git a/packages/web/src/app/[domain]/browse/hooks/useBrowseParams.ts b/packages/web/src/app/(app)/browse/hooks/useBrowseParams.ts similarity index 100% rename from packages/web/src/app/[domain]/browse/hooks/useBrowseParams.ts rename to packages/web/src/app/(app)/browse/hooks/useBrowseParams.ts diff --git a/packages/web/src/app/[domain]/browse/hooks/useBrowseState.ts b/packages/web/src/app/(app)/browse/hooks/useBrowseState.ts similarity index 100% rename from packages/web/src/app/[domain]/browse/hooks/useBrowseState.ts rename to packages/web/src/app/(app)/browse/hooks/useBrowseState.ts diff --git a/packages/web/src/app/[domain]/browse/hooks/utils.test.ts b/packages/web/src/app/(app)/browse/hooks/utils.test.ts similarity index 100% rename from packages/web/src/app/[domain]/browse/hooks/utils.test.ts rename to packages/web/src/app/(app)/browse/hooks/utils.test.ts diff --git a/packages/web/src/app/[domain]/browse/hooks/utils.ts b/packages/web/src/app/(app)/browse/hooks/utils.ts similarity index 91% rename from packages/web/src/app/[domain]/browse/hooks/utils.ts rename to packages/web/src/app/(app)/browse/hooks/utils.ts index 77cbb9ab2..e48782098 100644 --- a/packages/web/src/app/[domain]/browse/hooks/utils.ts +++ b/packages/web/src/app/(app)/browse/hooks/utils.ts @@ -1,5 +1,4 @@ import { BrowseState, SET_BROWSE_STATE_QUERY_PARAM } from "../browseStateProvider"; -import { SINGLE_TENANT_ORG_DOMAIN } from "@/lib/constants"; export const HIGHLIGHT_RANGE_QUERY_PARAM = 'highlightRange'; @@ -66,7 +65,6 @@ export const getBrowseParamsFromPathParam = (pathParam: string) => { export const getBrowsePath = ({ repoName, revisionName, path, pathType, highlightRange, setBrowseState, }: GetBrowsePathProps) => { - const domain = SINGLE_TENANT_ORG_DOMAIN; const params = new URLSearchParams(); if (highlightRange) { @@ -84,7 +82,7 @@ export const getBrowsePath = ({ } const encodedPath = encodeURIComponent(path); - const browsePath = `/${domain}/browse/${repoName}${revisionName ? `@${revisionName}` : ''}/-/${pathType}/${encodedPath}${params.size > 0 ? `?${params.toString()}` : ''}`; + const browsePath = `/browse/${repoName}${revisionName ? `@${revisionName}` : ''}/-/${pathType}/${encodedPath}${params.size > 0 ? `?${params.toString()}` : ''}`; return browsePath; }; diff --git a/packages/web/src/app/[domain]/browse/layout.tsx b/packages/web/src/app/(app)/browse/layout.tsx similarity index 100% rename from packages/web/src/app/[domain]/browse/layout.tsx rename to packages/web/src/app/(app)/browse/layout.tsx diff --git a/packages/web/src/app/[domain]/browse/layoutClient.tsx b/packages/web/src/app/(app)/browse/layoutClient.tsx similarity index 93% rename from packages/web/src/app/[domain]/browse/layoutClient.tsx rename to packages/web/src/app/(app)/browse/layoutClient.tsx index 8128b5717..b69dc4ad1 100644 --- a/packages/web/src/app/[domain]/browse/layoutClient.tsx +++ b/packages/web/src/app/(app)/browse/layoutClient.tsx @@ -5,10 +5,9 @@ import { BottomPanel } from "./components/bottomPanel"; import { AnimatedResizableHandle } from "@/components/ui/animatedResizableHandle"; import { BrowseStateProvider } from "./browseStateProvider"; import { FileTreePanel } from "./components/fileTreePanel"; -import { TopBar } from "@/app/[domain]/components/topBar"; +import { TopBar } from "@/app/(app)/components/topBar"; import { useBrowseParams } from "./hooks/useBrowseParams"; import { FileSearchCommandDialog } from "./components/fileSearchCommandDialog"; -import { useDomain } from "@/hooks/useDomain"; import { SearchBar } from "../components/searchBar"; import escapeStringRegexp from "escape-string-regexp"; import { Session } from "next-auth"; @@ -25,13 +24,10 @@ export function LayoutClient({ isSearchAssistSupported, }: LayoutProps) { const { repoName, revisionName } = useBrowseParams(); - const domain = useDomain(); - return (
; } export default async function Image({ params }: ImageProps) { - const { domain, id } = await params; + const { id } = await params; - const org = await getOrgFromDomain(domain); - if (!org) { - notFound(); - } - - const chat = await prisma.chat.findUnique({ + const chat = await __unsafePrisma.chat.findUnique({ where: { id, - orgId: org.id, }, include: { createdBy: { diff --git a/packages/web/src/app/[domain]/chat/[id]/page.tsx b/packages/web/src/app/(app)/chat/[id]/page.tsx similarity index 92% rename from packages/web/src/app/[domain]/chat/[id]/page.tsx rename to packages/web/src/app/(app)/chat/[id]/page.tsx index 2fd31d7ad..9add839cc 100644 --- a/packages/web/src/app/[domain]/chat/[id]/page.tsx +++ b/packages/web/src/app/(app)/chat/[id]/page.tsx @@ -13,8 +13,7 @@ import { auth } from '@/auth'; import { AnimatedResizableHandle } from '@/components/ui/animatedResizableHandle'; import { ChatSidePanel } from '../components/chatSidePanel'; import { ResizablePanelGroup } from '@/components/ui/resizable'; -import { prisma } from '@/prisma'; -import { getOrgFromDomain } from '@/data/org'; +import { __unsafePrisma } from '@/prisma'; import { ChatVisibility } from '@sourcebot/db'; import { Metadata } from 'next'; import { SBChatMessage } from '@/features/chat/types'; @@ -23,25 +22,16 @@ import { captureEvent } from '@/lib/posthog'; interface PageProps { params: Promise<{ - domain: string; id: string; }>; } -export async function generateMetadata({ params }: PageProps): Promise { - const { domain, id } = await params; +export const generateMetadata = async ({ params }: PageProps): Promise => { + const { id } = await params; - const org = await getOrgFromDomain(domain); - if (!org) { - return { - title: 'Chat | Sourcebot', - }; - } - - const chat = await prisma.chat.findUnique({ + const chat = await __unsafePrisma.chat.findUnique({ where: { id, - orgId: org.id, }, }); @@ -154,8 +144,7 @@ export default async function Page(props: PageProps) { return (
(); - const onRenameChat = useCallback(async (newName: string): Promise => { const response = await updateChatName({ chatId: id, @@ -60,7 +57,7 @@ export const ChatName = ({ name, id, isOwner = false, isAuthenticated = false }: toast({ description: `✅ Chat deleted successfully` }); - router.push(`/${SINGLE_TENANT_ORG_DOMAIN}/chat`); + router.push(`/chat`); return true; } }, [id, toast, router]); @@ -77,10 +74,10 @@ export const ChatName = ({ name, id, isOwner = false, isAuthenticated = false }: toast({ description: `✅ Chat duplicated successfully` }); - router.push(`/${params.domain}/chat/${response.id}`); + router.push(`/chat/${response.id}`); return response.id; } - }, [id, toast, router, params.domain]); + }, [id, toast, router]); return ( <> diff --git a/packages/web/src/app/[domain]/chat/components/chatSidePanel.tsx b/packages/web/src/app/(app)/chat/components/chatSidePanel.tsx similarity index 96% rename from packages/web/src/app/[domain]/chat/components/chatSidePanel.tsx rename to packages/web/src/app/(app)/chat/components/chatSidePanel.tsx index 7d3dde922..9815eb9de 100644 --- a/packages/web/src/app/[domain]/chat/components/chatSidePanel.tsx +++ b/packages/web/src/app/(app)/chat/components/chatSidePanel.tsx @@ -24,7 +24,6 @@ import { RenameChatDialog } from "./renameChatDialog"; import { DeleteChatDialog } from "./deleteChatDialog"; import { DuplicateChatDialog } from "./duplicateChatDialog"; import Link from "next/link"; -import { SINGLE_TENANT_ORG_DOMAIN } from "@/lib/constants"; interface ChatSidePanelProps { order: number; @@ -112,7 +111,7 @@ export const ChatSidePanel = ({ // If we just deleted the current chat, navigate to new chat if (chatIdToDelete === chatId) { - router.push(`/${SINGLE_TENANT_ORG_DOMAIN}/chat`); + router.push(`/chat`); } return true; @@ -136,7 +135,7 @@ export const ChatSidePanel = ({ description: `✅ Chat duplicated successfully` }); captureEvent('wa_chat_duplicated', { chatId: chatIdToDuplicate }); - router.push(`/${SINGLE_TENANT_ORG_DOMAIN}/chat/${response.id}`); + router.push(`/chat/${response.id}`); return response.id; } }, [router, toast]); @@ -161,7 +160,7 @@ export const ChatSidePanel = ({ size="sm" className="w-full" onClick={() => { - router.push(`/${SINGLE_TENANT_ORG_DOMAIN}/chat`); + router.push(`/chat`); }} > @@ -175,7 +174,7 @@ export const ChatSidePanel = ({

Sign in @@ -193,7 +192,7 @@ export const ChatSidePanel = ({ chat.id === chatId && "bg-muted" )} onClick={() => { - router.push(`/${SINGLE_TENANT_ORG_DOMAIN}/chat/${chat.id}`); + router.push(`/chat/${chat.id}`); }} > {chat.name ?? 'Untitled chat'} diff --git a/packages/web/src/app/[domain]/chat/components/deleteChatDialog.tsx b/packages/web/src/app/(app)/chat/components/deleteChatDialog.tsx similarity index 100% rename from packages/web/src/app/[domain]/chat/components/deleteChatDialog.tsx rename to packages/web/src/app/(app)/chat/components/deleteChatDialog.tsx diff --git a/packages/web/src/app/[domain]/chat/components/demoCards.tsx b/packages/web/src/app/(app)/chat/components/demoCards.tsx similarity index 100% rename from packages/web/src/app/[domain]/chat/components/demoCards.tsx rename to packages/web/src/app/(app)/chat/components/demoCards.tsx diff --git a/packages/web/src/app/[domain]/chat/components/duplicateChatDialog.tsx b/packages/web/src/app/(app)/chat/components/duplicateChatDialog.tsx similarity index 100% rename from packages/web/src/app/[domain]/chat/components/duplicateChatDialog.tsx rename to packages/web/src/app/(app)/chat/components/duplicateChatDialog.tsx diff --git a/packages/web/src/app/[domain]/chat/components/landingPageChatBox.tsx b/packages/web/src/app/(app)/chat/components/landingPageChatBox.tsx similarity index 100% rename from packages/web/src/app/[domain]/chat/components/landingPageChatBox.tsx rename to packages/web/src/app/(app)/chat/components/landingPageChatBox.tsx diff --git a/packages/web/src/app/[domain]/chat/components/renameChatDialog.tsx b/packages/web/src/app/(app)/chat/components/renameChatDialog.tsx similarity index 100% rename from packages/web/src/app/[domain]/chat/components/renameChatDialog.tsx rename to packages/web/src/app/(app)/chat/components/renameChatDialog.tsx diff --git a/packages/web/src/app/[domain]/chat/components/shareChatPopover/ee/invitePanel.tsx b/packages/web/src/app/(app)/chat/components/shareChatPopover/ee/invitePanel.tsx similarity index 100% rename from packages/web/src/app/[domain]/chat/components/shareChatPopover/ee/invitePanel.tsx rename to packages/web/src/app/(app)/chat/components/shareChatPopover/ee/invitePanel.tsx diff --git a/packages/web/src/app/[domain]/chat/components/shareChatPopover/index.tsx b/packages/web/src/app/(app)/chat/components/shareChatPopover/index.tsx similarity index 100% rename from packages/web/src/app/[domain]/chat/components/shareChatPopover/index.tsx rename to packages/web/src/app/(app)/chat/components/shareChatPopover/index.tsx diff --git a/packages/web/src/app/[domain]/chat/components/shareChatPopover/shareSettings.tsx b/packages/web/src/app/(app)/chat/components/shareChatPopover/shareSettings.tsx similarity index 100% rename from packages/web/src/app/[domain]/chat/components/shareChatPopover/shareSettings.tsx rename to packages/web/src/app/(app)/chat/components/shareChatPopover/shareSettings.tsx diff --git a/packages/web/src/app/[domain]/chat/components/tutorialDialog.tsx b/packages/web/src/app/(app)/chat/components/tutorialDialog.tsx similarity index 100% rename from packages/web/src/app/[domain]/chat/components/tutorialDialog.tsx rename to packages/web/src/app/(app)/chat/components/tutorialDialog.tsx diff --git a/packages/web/src/app/[domain]/chat/layout.tsx b/packages/web/src/app/(app)/chat/layout.tsx similarity index 100% rename from packages/web/src/app/[domain]/chat/layout.tsx rename to packages/web/src/app/(app)/chat/layout.tsx diff --git a/packages/web/src/app/[domain]/chat/page.tsx b/packages/web/src/app/(app)/chat/page.tsx similarity index 94% rename from packages/web/src/app/[domain]/chat/page.tsx rename to packages/web/src/app/(app)/chat/page.tsx index 61b33b33f..a072bc2b7 100644 --- a/packages/web/src/app/[domain]/chat/page.tsx +++ b/packages/web/src/app/(app)/chat/page.tsx @@ -18,14 +18,7 @@ import { ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable"; import { ChatSidePanel } from "./components/chatSidePanel"; import { AnimatedResizableHandle } from "@/components/ui/animatedResizableHandle"; -interface PageProps { - params: Promise<{ - domain: string; - }>; -} - -export default async function Page(props: PageProps) { - const params = await props.params; +export default async function Page() { const languageModels = await getConfiguredLanguageModelsInfo(); const searchContexts = await getSearchContexts(); const allRepos = await getRepos(); @@ -74,9 +67,7 @@ export default async function Page(props: PageProps) { return (

- + (null); const { toast } = useToast(); const captureEvent = useCaptureEvent(); - const domain = useDomain(); - useEffect(() => { if (selection.empty) { ref.current?.classList.add('hidden'); @@ -106,7 +103,7 @@ export const EditorContextMenu = ({ const from = toLineAndColumn(selection.from); const to = toLineAndColumn(selection.to); - const basePath = `${window.location.origin}/${domain}/browse`; + const basePath = `${window.location.origin}/browse`; const url = createPathWithQueryParams(`${basePath}/${repoName}@${revisionName}/-/blob/${path}`, [HIGHLIGHT_RANGE_QUERY_PARAM, `${from?.line}:${from?.column},${to?.line}:${to?.column}`], ); @@ -127,7 +124,7 @@ export const EditorContextMenu = ({ } } ) - }, [selection.from, selection.to, domain, repoName, revisionName, path, toast, captureEvent, view]); + }, [selection.from, selection.to, repoName, revisionName, path, toast, captureEvent, view]); return (
{ - const domain = useDomain(); - return ( @@ -59,7 +56,7 @@ export const MeControlDropdownMenu = ({ - + Settings diff --git a/packages/web/src/app/[domain]/components/mobileUnsupportedSplashScreen.tsx b/packages/web/src/app/(app)/components/mobileUnsupportedSplashScreen.tsx similarity index 100% rename from packages/web/src/app/[domain]/components/mobileUnsupportedSplashScreen.tsx rename to packages/web/src/app/(app)/components/mobileUnsupportedSplashScreen.tsx diff --git a/packages/web/src/app/[domain]/components/navigationMenu/index.tsx b/packages/web/src/app/(app)/components/navigationMenu/index.tsx similarity index 95% rename from packages/web/src/app/[domain]/components/navigationMenu/index.tsx rename to packages/web/src/app/(app)/components/navigationMenu/index.tsx index b47afb67f..dc4b4b1b9 100644 --- a/packages/web/src/app/[domain]/components/navigationMenu/index.tsx +++ b/packages/web/src/app/(app)/components/navigationMenu/index.tsx @@ -16,13 +16,7 @@ import { redirect } from "next/navigation"; import { AppearanceDropdownMenu } from "../appearanceDropdownMenu"; -interface NavigationMenuProps { - domain: string; -} - -export const NavigationMenu = async ({ - domain, -}: NavigationMenuProps) => { +export const NavigationMenu = async () => { const session = await auth(); const isAuthenticated = session?.user !== undefined; @@ -89,7 +83,7 @@ export const NavigationMenu = async ({
0} isSettingsButtonNotificationDotVisible={ diff --git a/packages/web/src/app/[domain]/components/navigationMenu/navigationItems.tsx b/packages/web/src/app/(app)/components/navigationMenu/navigationItems.tsx similarity index 83% rename from packages/web/src/app/[domain]/components/navigationMenu/navigationItems.tsx rename to packages/web/src/app/(app)/components/navigationMenu/navigationItems.tsx index b5a314837..78ddbd7e4 100644 --- a/packages/web/src/app/[domain]/components/navigationMenu/navigationItems.tsx +++ b/packages/web/src/app/(app)/components/navigationMenu/navigationItems.tsx @@ -8,7 +8,6 @@ import { usePathname } from "next/navigation"; import { NotificationDot } from "../notificationDot"; interface NavigationItemsProps { - domain: string; numberOfRepos: number; isReposButtonNotificationDotVisible: boolean; isSettingsButtonNotificationDotVisible: boolean; @@ -16,7 +15,6 @@ interface NavigationItemsProps { } export const NavigationItems = ({ - domain, numberOfRepos, isReposButtonNotificationDotVisible, isSettingsButtonNotificationDotVisible, @@ -25,8 +23,8 @@ export const NavigationItems = ({ const pathname = usePathname(); const isActive = (href: string) => { - if (href === `/${domain}`) { - return pathname === `/${domain}`; + if (href === '/') { + return pathname === '/'; } return pathname.startsWith(href); }; @@ -35,27 +33,27 @@ export const NavigationItems = ({ Search - {((isActive(`/${domain}`) || isActive(`/${domain}/search`)) && )} + {((isActive('/') || isActive('/search')) && )} Ask - {isActive(`/${domain}/chat`) && } + {isActive('/chat') && } @@ -65,19 +63,19 @@ export const NavigationItems = ({ {isReposButtonNotificationDotVisible && } - {isActive(`/${domain}/repos`) && } + {isActive('/repos') && } {isAuthenticated && ( Settings {isSettingsButtonNotificationDotVisible && } - {isActive(`/${domain}/settings`) && } + {isActive('/settings') && } )} diff --git a/packages/web/src/app/[domain]/components/navigationMenu/progressIndicator.tsx b/packages/web/src/app/(app)/components/navigationMenu/progressIndicator.tsx similarity index 91% rename from packages/web/src/app/[domain]/components/navigationMenu/progressIndicator.tsx rename to packages/web/src/app/(app)/components/navigationMenu/progressIndicator.tsx index 7726f635f..80589e353 100644 --- a/packages/web/src/app/[domain]/components/navigationMenu/progressIndicator.tsx +++ b/packages/web/src/app/(app)/components/navigationMenu/progressIndicator.tsx @@ -5,8 +5,6 @@ import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Separator } from "@/components/ui/separator"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; -import { useDomain } from "@/hooks/useDomain"; -import { SINGLE_TENANT_ORG_DOMAIN } from "@/lib/constants"; import { RepositoryQuery } from "@/lib/types"; import { getCodeHostInfoForRepo, getShortenedNumberDisplayString } from "@/lib/utils"; import clsx from "clsx"; @@ -25,7 +23,6 @@ export const ProgressIndicator = ({ numberOfReposWithFirstTimeIndexingJobsInProgress: numRepos, sampleRepos, }: ProgressIndicatorProps) => { - const domain = useDomain(); const router = useRouter(); const { toast } = useToast(); @@ -38,7 +35,7 @@ export const ProgressIndicator = ({ return ( - + {numReposString} @@ -70,7 +67,7 @@ export const ProgressIndicator = ({
{numRepos > sampleRepos.length && (
- + {`View ${numRepos - sampleRepos.length} more`}
@@ -104,7 +101,7 @@ const RepoItem = ({ repo }: { repo: RepositoryQuery }) => { return ( {repoIcon} diff --git a/packages/web/src/app/[domain]/components/notFound.tsx b/packages/web/src/app/(app)/components/notFound.tsx similarity index 100% rename from packages/web/src/app/[domain]/components/notFound.tsx rename to packages/web/src/app/(app)/components/notFound.tsx diff --git a/packages/web/src/app/[domain]/components/notificationDot.tsx b/packages/web/src/app/(app)/components/notificationDot.tsx similarity index 100% rename from packages/web/src/app/[domain]/components/notificationDot.tsx rename to packages/web/src/app/(app)/components/notificationDot.tsx diff --git a/packages/web/src/app/[domain]/components/onboardGuard.tsx b/packages/web/src/app/(app)/components/onboardGuard.tsx similarity index 100% rename from packages/web/src/app/[domain]/components/onboardGuard.tsx rename to packages/web/src/app/(app)/components/onboardGuard.tsx diff --git a/packages/web/src/app/[domain]/components/pageNotFound.tsx b/packages/web/src/app/(app)/components/pageNotFound.tsx similarity index 100% rename from packages/web/src/app/[domain]/components/pageNotFound.tsx rename to packages/web/src/app/(app)/components/pageNotFound.tsx diff --git a/packages/web/src/app/[domain]/components/pathHeader.tsx b/packages/web/src/app/(app)/components/pathHeader.tsx similarity index 100% rename from packages/web/src/app/[domain]/components/pathHeader.tsx rename to packages/web/src/app/(app)/components/pathHeader.tsx diff --git a/packages/web/src/app/[domain]/components/pendingApproval.tsx b/packages/web/src/app/(app)/components/pendingApproval.tsx similarity index 100% rename from packages/web/src/app/[domain]/components/pendingApproval.tsx rename to packages/web/src/app/(app)/components/pendingApproval.tsx diff --git a/packages/web/src/app/[domain]/components/permissionSyncBanner.tsx b/packages/web/src/app/(app)/components/permissionSyncBanner.tsx similarity index 100% rename from packages/web/src/app/[domain]/components/permissionSyncBanner.tsx rename to packages/web/src/app/(app)/components/permissionSyncBanner.tsx diff --git a/packages/web/src/app/[domain]/components/repositoryCarousel.tsx b/packages/web/src/app/(app)/components/repositoryCarousel.tsx similarity index 93% rename from packages/web/src/app/[domain]/components/repositoryCarousel.tsx rename to packages/web/src/app/(app)/components/repositoryCarousel.tsx index fe929c370..26d92b5b0 100644 --- a/packages/web/src/app/[domain]/components/repositoryCarousel.tsx +++ b/packages/web/src/app/(app)/components/repositoryCarousel.tsx @@ -11,7 +11,6 @@ import clsx from "clsx"; import Autoscroll from "embla-carousel-auto-scroll"; import Image from "next/image"; import Link from "next/link"; -import { useDomain } from "@/hooks/useDomain"; interface RepositoryCarouselProps { displayRepos: RepositoryQuery[]; @@ -22,8 +21,6 @@ export function RepositoryCarousel({ displayRepos, numberOfReposWithIndex, }: RepositoryCarouselProps) { - const domain = useDomain(); - if (numberOfReposWithIndex === 0) { return (
@@ -34,7 +31,7 @@ export function RepositoryCarousel({ <> Create a{" "} - + connection {" "} to start indexing repositories @@ -51,7 +48,7 @@ export function RepositoryCarousel({ {`${numberOfReposWithIndex} `} {numberOfReposWithIndex > 1 ? 'repositories' : 'repository'} diff --git a/packages/web/src/app/[domain]/components/searchBar/constants.ts b/packages/web/src/app/(app)/components/searchBar/constants.ts similarity index 100% rename from packages/web/src/app/[domain]/components/searchBar/constants.ts rename to packages/web/src/app/(app)/components/searchBar/constants.ts diff --git a/packages/web/src/app/[domain]/components/searchBar/index.ts b/packages/web/src/app/(app)/components/searchBar/index.ts similarity index 100% rename from packages/web/src/app/[domain]/components/searchBar/index.ts rename to packages/web/src/app/(app)/components/searchBar/index.ts diff --git a/packages/web/src/app/[domain]/components/searchBar/searchAssistBox.tsx b/packages/web/src/app/(app)/components/searchBar/searchAssistBox.tsx similarity index 100% rename from packages/web/src/app/[domain]/components/searchBar/searchAssistBox.tsx rename to packages/web/src/app/(app)/components/searchBar/searchAssistBox.tsx diff --git a/packages/web/src/app/[domain]/components/searchBar/searchBar.tsx b/packages/web/src/app/(app)/components/searchBar/searchBar.tsx similarity index 98% rename from packages/web/src/app/[domain]/components/searchBar/searchBar.tsx rename to packages/web/src/app/(app)/components/searchBar/searchBar.tsx index 423d77fd1..7ce3a537c 100644 --- a/packages/web/src/app/[domain]/components/searchBar/searchBar.tsx +++ b/packages/web/src/app/(app)/components/searchBar/searchBar.tsx @@ -41,7 +41,6 @@ import { useSuggestionModeAndQuery } from "./useSuggestionModeAndQuery"; import { Separator } from "@/components/ui/separator"; import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip"; import { Toggle } from "@/components/ui/toggle"; -import { useDomain } from "@/hooks/useDomain"; import tailwind from "@/tailwind"; import React from "react"; import Link from "next/link"; @@ -110,7 +109,6 @@ export const SearchBar = ({ isSearchAssistSupported, }: SearchBarProps) => { const router = useRouter(); - const domain = useDomain(); const captureEvent = useCaptureEvent(); const suggestionBoxRef = useRef(null); const editorRef = useRef(null); @@ -227,13 +225,13 @@ export const SearchBar = ({ setActivePanel(undefined); setIsHistorySearchEnabled(false); - const url = createPathWithQueryParams(`/${domain}/search`, + const url = createPathWithQueryParams(`/search`, [SearchQueryParams.query, query], [SearchQueryParams.isRegexEnabled, isRegexEnabled ? "true" : null], [SearchQueryParams.isCaseSensitivityEnabled, isCaseSensitivityEnabled ? "true" : null], ); router.push(url); - }, [domain, router, isRegexEnabled, isCaseSensitivityEnabled]); + }, [router, isRegexEnabled, isCaseSensitivityEnabled]); return (
{ - const domain = useDomain(); const { data: repoSuggestions, isLoading: _isLoadingRepos } = useQuery({ queryKey: ["repoSuggestions", suggestionQuery], queryFn: () => unwrapServiceError(listRepos({ @@ -56,7 +54,7 @@ export const useSuggestionsData = ({ const isLoadingRepos = useMemo(() => suggestionMode === "repo" && _isLoadingRepos, [_isLoadingRepos, suggestionMode]); const { data: fileSuggestions, isLoading: _isLoadingFiles } = useQuery({ - queryKey: ["fileSuggestions", suggestionQuery, domain], + queryKey: ["fileSuggestions", suggestionQuery], queryFn: () => search({ query: `file:${suggestionQuery}`, matches: 15, @@ -76,7 +74,7 @@ export const useSuggestionsData = ({ const isLoadingFiles = useMemo(() => suggestionMode === "file" && _isLoadingFiles, [_isLoadingFiles, suggestionMode]); const { data: symbolSuggestions, isLoading: _isLoadingSymbols } = useQuery({ - queryKey: ["symbolSuggestions", suggestionQuery, domain], + queryKey: ["symbolSuggestions", suggestionQuery], queryFn: () => search({ query: `sym:${suggestionQuery.length > 0 ? suggestionQuery : ".*"}`, matches: 15, @@ -106,7 +104,7 @@ export const useSuggestionsData = ({ const isLoadingSymbols = useMemo(() => suggestionMode === "symbol" && _isLoadingSymbols, [suggestionMode, _isLoadingSymbols]); const { data: searchContextSuggestions, isLoading: _isLoadingSearchContexts } = useQuery({ - queryKey: ["searchContexts", domain], + queryKey: ["searchContexts"], queryFn: () => getSearchContexts(), select: (data): Suggestion[] => { if (isServiceError(data)) { diff --git a/packages/web/src/app/[domain]/components/searchBar/zoektLanguageExtension.ts b/packages/web/src/app/(app)/components/searchBar/zoektLanguageExtension.ts similarity index 100% rename from packages/web/src/app/[domain]/components/searchBar/zoektLanguageExtension.ts rename to packages/web/src/app/(app)/components/searchBar/zoektLanguageExtension.ts diff --git a/packages/web/src/app/[domain]/components/searchModeSelector.tsx b/packages/web/src/app/(app)/components/searchModeSelector.tsx similarity index 97% rename from packages/web/src/app/[domain]/components/searchModeSelector.tsx rename to packages/web/src/app/(app)/components/searchModeSelector.tsx index aaa8f4412..4b2ef3e95 100644 --- a/packages/web/src/app/[domain]/components/searchModeSelector.tsx +++ b/packages/web/src/app/(app)/components/searchModeSelector.tsx @@ -1,7 +1,6 @@ 'use client'; import { KeyboardShortcutHint } from "@/app/components/keyboardShortcutHint"; -import { useDomain } from "@/hooks/useDomain"; import { Select, SelectContent, SelectItemNoItemText, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Separator } from "@/components/ui/separator"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; @@ -27,13 +26,12 @@ export const SearchModeSelector = ({ searchMode, className, }: SearchModeSelectorProps) => { - const domain = useDomain(); const [focusedSearchMode, setFocusedSearchMode] = useState(searchMode); const router = useRouter(); const onSearchModeChanged = useCallback((value: SearchMode) => { - router.push(`/${domain}/${value === "precise" ? "search" : "chat"}`); - }, [domain, router]); + router.push(`/${value === "precise" ? "search" : "chat"}`); + }, [router]); useHotkeys("mod+i", (e) => { e.preventDefault(); diff --git a/packages/web/src/app/[domain]/components/submitAccountRequestButton.tsx b/packages/web/src/app/(app)/components/submitAccountRequestButton.tsx similarity index 87% rename from packages/web/src/app/[domain]/components/submitAccountRequestButton.tsx rename to packages/web/src/app/(app)/components/submitAccountRequestButton.tsx index 291e5f509..85398a7db 100644 --- a/packages/web/src/app/[domain]/components/submitAccountRequestButton.tsx +++ b/packages/web/src/app/(app)/components/submitAccountRequestButton.tsx @@ -8,19 +8,15 @@ import { createAccountRequest } from "@/actions" import { isServiceError } from "@/lib/utils" import { useRouter } from "next/navigation" -interface SubmitButtonProps { - domain: string - userId: string -} -export function SubmitAccountRequestButton({ domain, userId }: SubmitButtonProps) { +export function SubmitAccountRequestButton() { const { toast } = useToast() const router = useRouter() const [isSubmitting, setIsSubmitting] = useState(false) const handleSubmit = async () => { setIsSubmitting(true) - const result = await createAccountRequest(userId, domain) + const result = await createAccountRequest() if (!isServiceError(result)) { if (result.existingRequest) { toast({ @@ -52,7 +48,6 @@ export function SubmitAccountRequestButton({ domain, userId }: SubmitButtonProps e.preventDefault(); handleSubmit(); }}> -