feat: implement ensure_publication for zero_publication management

- Added the ensure_publication function to create and verify the zero_publication if it is missing, ensuring idempotency during database initialization.
- Integrated ensure_publication into the create_db_and_tables function to prevent zero-cache crash loops on startup.
- Introduced a self-check script to validate the ensure_publication functionality on a create_all-bootstrapped database.
- Updated various components to reflect the transition from search space to workspace, including adjustments in imports and routing paths.
This commit is contained in:
DESKTOP-RTLN3BA\$punk 2026-07-05 23:17:13 -07:00
parent 7562bc78ee
commit a64c8205fe
164 changed files with 626 additions and 506 deletions

View file

@ -38,7 +38,7 @@ export function AutoReloadSettings() {
const pathname = usePathname();
const searchParams = useSearchParams();
const queryClient = useQueryClient();
const searchSpaceId = Number(params?.search_space_id);
const searchSpaceId = Number(params?.workspace_id);
const [enabled, setEnabled] = useState(false);
const [thresholdInput, setThresholdInput] = useState("");
@ -79,7 +79,7 @@ export function AutoReloadSettings() {
const setupMutation = useMutation({
mutationFn: () =>
stripeApiService.createAutoReloadSetupSession({ search_space_id: searchSpaceId }),
stripeApiService.createAutoReloadSetupSession({ workspace_id: searchSpaceId }),
onSuccess: (response) => {
window.location.assign(response.checkout_url);
},

View file

@ -37,7 +37,7 @@ const formatUsd = (micros: number) => {
export function BuyCreditsContent() {
const params = useParams();
const searchSpaceId = Number(params?.search_space_id);
const searchSpaceId = Number(params?.workspace_id);
const [quantity, setQuantity] = useState(1);
// Raw text of the amount field so the user can clear it while typing;
// committed back to a clamped integer on blur.
@ -176,7 +176,7 @@ export function BuyCreditsContent() {
<Button
className="w-full"
disabled={purchaseMutation.isPending}
onClick={() => purchaseMutation.mutate({ quantity, search_space_id: searchSpaceId })}
onClick={() => purchaseMutation.mutate({ quantity, workspace_id: searchSpaceId })}
>
{purchaseMutation.isPending ? (
<>

View file

@ -32,7 +32,7 @@ const formatRewardUsd = (micros: number) => {
export function EarnCreditsContent() {
const params = useParams();
const queryClient = useQueryClient();
const searchSpaceId = params?.search_space_id ?? "";
const searchSpaceId = params?.workspace_id ?? "";
useEffect(() => {
trackIncentivePageViewed();

View file

@ -8,13 +8,13 @@ import { toast } from "sonner";
import {
updateSearchSpaceApiAccessMutationAtom,
updateSearchSpaceMutationAtom,
} from "@/atoms/search-spaces/search-space-mutation.atoms";
} from "@/atoms/workspaces/workspace-mutation.atoms";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Skeleton } from "@/components/ui/skeleton";
import { Switch } from "@/components/ui/switch";
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service";
import { searchSpacesApiService } from "@/lib/apis/workspaces-api.service";
import { authenticatedFetch } from "@/lib/auth-fetch";
import { buildBackendUrl } from "@/lib/env-config";
import { cacheKeys } from "@/lib/query-client/cache-keys";
@ -57,7 +57,7 @@ export function GeneralSettingsManager({ searchSpaceId }: GeneralSettingsManager
setIsExporting(true);
try {
const response = await authenticatedFetch(
buildBackendUrl(`/api/v1/search-spaces/${searchSpaceId}/export`),
buildBackendUrl(`/api/v1/workspaces/${searchSpaceId}/export`),
{ method: "GET" }
);
if (!response.ok) {

View file

@ -148,7 +148,7 @@ export function ConnectionSettingsDialog({
base_url: data.base_url,
api_key: apiKeyForTest,
scope: "SEARCH_SPACE",
search_space_id: connection.search_space_id,
workspace_id: connection.workspace_id,
extra: connection.extra ?? {},
enabled: connection.enabled,
models: [],

View file

@ -138,7 +138,7 @@ export function ModelProviderConnectionsPanel({
base_url: draft.base_url,
api_key: draft.api_key,
scope: "SEARCH_SPACE" as const,
search_space_id: searchSpaceId,
workspace_id: searchSpaceId,
extra: draft.extra,
enabled: true,
models,
@ -171,7 +171,7 @@ export function ModelProviderConnectionsPanel({
base_url: null,
api_key: null,
scope: "SEARCH_SPACE",
search_space_id: searchSpaceId,
workspace_id: searchSpaceId,
extra: {},
enabled: true,
models: [],
@ -190,7 +190,7 @@ export function ModelProviderConnectionsPanel({
base_url: draft.base_url,
api_key: draft.api_key,
scope: "SEARCH_SPACE",
search_space_id: searchSpaceId,
workspace_id: searchSpaceId,
extra: draft.extra,
enabled: true,
models: [],

View file

@ -5,13 +5,13 @@ import { useAtomValue } from "jotai";
import { AlertTriangle, Info } from "lucide-react";
import { useEffect, useState } from "react";
import { toast } from "sonner";
import { updateSearchSpaceMutationAtom } from "@/atoms/search-spaces/search-space-mutation.atoms";
import { updateSearchSpaceMutationAtom } from "@/atoms/workspaces/workspace-mutation.atoms";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Skeleton } from "@/components/ui/skeleton";
import { Textarea } from "@/components/ui/textarea";
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service";
import { searchSpacesApiService } from "@/lib/apis/workspaces-api.service";
import { cacheKeys } from "@/lib/query-client/cache-keys";
import { Spinner } from "../ui/spinner";

View file

@ -279,7 +279,7 @@ export function RolesManager({ searchSpaceId }: { searchSpaceId: number }) {
const { data: roles = [], isLoading: rolesLoading } = useQuery({
queryKey: cacheKeys.roles.all(searchSpaceId.toString()),
queryFn: () => rolesApiService.getRoles({ search_space_id: searchSpaceId }),
queryFn: () => rolesApiService.getRoles({ workspace_id: searchSpaceId }),
enabled: !!searchSpaceId,
});
@ -311,7 +311,7 @@ export function RolesManager({ searchSpaceId }: { searchSpaceId: number }) {
}
): Promise<Role> => {
const request: UpdateRoleRequest = {
search_space_id: searchSpaceId,
workspace_id: searchSpaceId,
role_id: roleId,
data: data,
};
@ -323,7 +323,7 @@ export function RolesManager({ searchSpaceId }: { searchSpaceId: number }) {
const handleDeleteRole = useCallback(
async (roleId: number): Promise<boolean> => {
const request: DeleteRoleRequest = {
search_space_id: searchSpaceId,
workspace_id: searchSpaceId,
role_id: roleId,
};
await deleteRole(request);
@ -335,7 +335,7 @@ export function RolesManager({ searchSpaceId }: { searchSpaceId: number }) {
const handleCreateRole = useCallback(
async (roleData: CreateRoleRequest["data"]): Promise<Role> => {
const request: CreateRoleRequest = {
search_space_id: searchSpaceId,
workspace_id: searchSpaceId,
data: roleData,
};
return await createRole(request);