Fix project-route auth gaps for domain and SERP lookups (#65)
This commit is contained in:
parent
5d093c1ebb
commit
b15b62cae6
@ -225,14 +225,20 @@ export function useSyncRouteState({
|
||||
}, [navigate]);
|
||||
}
|
||||
|
||||
export function useDomainLookupMutation() {
|
||||
export function useDomainLookupMutation(projectId: string) {
|
||||
return useMutation({
|
||||
mutationFn: (data: {
|
||||
domain: string;
|
||||
includeSubdomains: boolean;
|
||||
locationCode: number;
|
||||
languageCode: string;
|
||||
}) => getDomainOverview({ data }),
|
||||
}) =>
|
||||
getDomainOverview({
|
||||
data: {
|
||||
...data,
|
||||
projectId,
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -160,7 +160,7 @@ export function useDomainOverviewController({
|
||||
});
|
||||
|
||||
useSyncRouteState({ controlsForm, searchState, setPendingSearch, navigate });
|
||||
const domainMutation = useDomainLookupMutation();
|
||||
const domainMutation = useDomainLookupMutation(projectId);
|
||||
const saveMutation = useSaveKeywordsMutation({ projectId, queryClient });
|
||||
const dataState = useOverviewDataState({
|
||||
overview,
|
||||
|
||||
@ -4,16 +4,20 @@ import { getStandardErrorMessage } from "@/client/lib/error-messages";
|
||||
import { getLanguageCode } from "@/client/features/keywords/utils";
|
||||
import { getSerpAnalysis } from "@/serverFunctions/keywords";
|
||||
|
||||
export function useKeywordSerpAnalysis(locationCode: number) {
|
||||
export function useKeywordSerpAnalysis(
|
||||
projectId: string,
|
||||
locationCode: number,
|
||||
) {
|
||||
const [serpKeyword, setSerpKeyword] = useState<string | null>(null);
|
||||
const [serpPage, setSerpPage] = useState(0);
|
||||
const SERP_PAGE_SIZE = 10;
|
||||
|
||||
const serpQuery = useQuery({
|
||||
queryKey: ["serpAnalysis", serpKeyword, locationCode],
|
||||
queryKey: ["serpAnalysis", projectId, serpKeyword, locationCode],
|
||||
queryFn: () =>
|
||||
getSerpAnalysis({
|
||||
data: {
|
||||
projectId,
|
||||
keyword: serpKeyword!,
|
||||
locationCode,
|
||||
languageCode: getLanguageCode(locationCode),
|
||||
|
||||
@ -165,7 +165,7 @@ function useKeywordControllerState(input: KeywordResearchControllerInput) {
|
||||
activeSerpKeyword,
|
||||
serpLoading,
|
||||
serpError,
|
||||
} = useKeywordSerpAnalysis(locationCode);
|
||||
} = useKeywordSerpAnalysis(input.projectId, locationCode);
|
||||
|
||||
const {
|
||||
history,
|
||||
|
||||
@ -69,6 +69,7 @@ const domainOverviewSchema = z.object({
|
||||
|
||||
async function getOverview(
|
||||
input: {
|
||||
projectId: string;
|
||||
domain: string;
|
||||
includeSubdomains: boolean;
|
||||
locationCode: number;
|
||||
@ -80,6 +81,7 @@ async function getOverview(
|
||||
|
||||
const cacheKey = await buildCacheKey("domain:overview", {
|
||||
organizationId: billingCustomer.organizationId,
|
||||
projectId: input.projectId,
|
||||
domain,
|
||||
includeSubdomains: input.includeSubdomains,
|
||||
locationCode: input.locationCode,
|
||||
|
||||
@ -56,6 +56,7 @@ function mapOrganicSerpItems(items: SerpLiveItem[]): SerpResultItem[] {
|
||||
|
||||
async function getSerpLiveAnalysis(
|
||||
input: {
|
||||
projectId: string;
|
||||
keyword: string;
|
||||
locationCode: number;
|
||||
languageCode: string;
|
||||
@ -66,6 +67,7 @@ async function getSerpLiveAnalysis(
|
||||
|
||||
const cacheKey = await buildCacheKey("serp:analysis", {
|
||||
organizationId: billingCustomer.organizationId,
|
||||
projectId: input.projectId,
|
||||
keyword,
|
||||
locationCode: input.locationCode,
|
||||
languageCode: input.languageCode,
|
||||
|
||||
@ -1,14 +1,20 @@
|
||||
import { createServerFn } from "@tanstack/react-start";
|
||||
import { requireAuthenticatedContext } from "@/serverFunctions/middleware";
|
||||
import { requireProjectContext } from "@/serverFunctions/middleware";
|
||||
import { domainOverviewSchema } from "@/types/schemas/domain";
|
||||
import { DomainService } from "@/server/features/domain/services/DomainService";
|
||||
|
||||
export const getDomainOverview = createServerFn({ method: "POST" })
|
||||
.middleware(requireAuthenticatedContext)
|
||||
.middleware(requireProjectContext)
|
||||
.inputValidator((data: unknown) => domainOverviewSchema.parse(data))
|
||||
.handler(async ({ data, context }) =>
|
||||
DomainService.getOverview(data, {
|
||||
DomainService.getOverview(
|
||||
{
|
||||
...data,
|
||||
projectId: context.project.id,
|
||||
},
|
||||
{
|
||||
organizationId: context.organizationId,
|
||||
userEmail: context.userEmail,
|
||||
}),
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
@ -7,10 +7,7 @@ import {
|
||||
serpAnalysisSchema,
|
||||
} from "@/types/schemas/keywords";
|
||||
import { KeywordResearchService } from "@/server/features/keywords/services/KeywordResearchService";
|
||||
import {
|
||||
requireAuthenticatedContext,
|
||||
requireProjectContext,
|
||||
} from "@/serverFunctions/middleware";
|
||||
import { requireProjectContext } from "@/serverFunctions/middleware";
|
||||
|
||||
export const researchKeywords = createServerFn({ method: "POST" })
|
||||
.middleware(requireProjectContext)
|
||||
@ -58,11 +55,17 @@ export const removeSavedKeyword = createServerFn({
|
||||
});
|
||||
|
||||
export const getSerpAnalysis = createServerFn({ method: "POST" })
|
||||
.middleware(requireAuthenticatedContext)
|
||||
.middleware(requireProjectContext)
|
||||
.inputValidator((data: unknown) => serpAnalysisSchema.parse(data))
|
||||
.handler(async ({ data, context }) =>
|
||||
KeywordResearchService.getSerpAnalysis(data, {
|
||||
KeywordResearchService.getSerpAnalysis(
|
||||
{
|
||||
...data,
|
||||
projectId: context.project.id,
|
||||
},
|
||||
{
|
||||
organizationId: context.organizationId,
|
||||
userEmail: context.userEmail,
|
||||
}),
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
@ -5,6 +5,7 @@ const booleanSearchParamSchema = z
|
||||
.transform((value) => value === true || value === "true");
|
||||
|
||||
export const domainOverviewSchema = z.object({
|
||||
projectId: z.string().min(1),
|
||||
domain: z.string().min(1, "Domain is required").max(255),
|
||||
includeSubdomains: z.boolean().default(true),
|
||||
locationCode: z.number().int().positive().default(2840),
|
||||
|
||||
@ -71,6 +71,7 @@ export type ResearchKeywordsInput = z.infer<typeof researchKeywordsSchema>;
|
||||
export type SaveKeywordsInput = z.infer<typeof saveKeywordsSchema>;
|
||||
export type RemoveSavedKeywordInput = z.infer<typeof removeSavedKeywordSchema>;
|
||||
export const serpAnalysisSchema = z.object({
|
||||
projectId: z.string().min(1),
|
||||
keyword: z.string().min(1),
|
||||
locationCode: z.number().int().positive().default(2840),
|
||||
languageCode: z.string().min(2).max(8).default("en"),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user