Skip to content

Commit 8fdef4e

Browse files
UN-2946 [MISC] Restore Prompt Studio public sharing after lookups V2 wiring (#1963)
* UN-2946 [FIX] Restore Prompt Studio public sharing after lookups V2 wiring Three issues broke /promptStudio/share/* after the lookups V2 PR: - The global useAxiosPrivate response interceptor calls logout() on any 401. An authenticated probe leaking into the anonymous share viewer (e.g. the new useLookupDirtySeed hook on first mount) returned 401 and redirected the share page through /api/v1/logout. Skip the logout when the current path is /promptStudio/share/* — the viewer has no session to expire. - The local Traefik router only forwarded /api/v1 and /deployment to the backend; /public/share/* fell through to the Vite dev server and the share endpoints returned the SPA index instead of JSON. Add /public to the backend rule (and to the frontend negative match) in both the compose labels and the sample proxy override. - The Combined Output JSON view defaulted to the Raw tab even when an enriched lookup output existed. For anonymous share viewers the enriched value is the point of the project; default activeView to Enriched when isPublicSource. The existing useEffect already falls back to Raw when no enriched data is present, so projects without lookups are unaffected. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * UN-2946 [DOCS] Tighten public-share guard comments Drop incident/context references; keep one-line WHYs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * UN-2946 [FIX] Tighten path match and re-sync enriched default (review) - useAxiosPrivate: trailing slash so the prefix doesn't match unintended siblings of /promptStudio/share/. - JsonView: re-sync activeView when isPublicSource or enrichedOutput changes, not just on first mount, to survive store hydration order. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * UN-2946 [FIX] Use globalThis over window in axios 401 guard Resolves SonarCloud S7764 findings on useAxiosPrivate.js. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * UN-2946 [FIX] Address review on public-share 401 + Enriched default - Log a console.warn breadcrumb when a 401 is suppressed on the public share route so stray authenticated probes don't go silent. - Gate the JsonView Enriched default behind a first-load ref so the default fires once and a manual Raw toggle isn't stomped on re-renders. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent a65d017 commit 8fdef4e

4 files changed

Lines changed: 36 additions & 9 deletions

File tree

docker/docker-compose.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ services:
3232
- APPLICATION_NAME=unstract-backend
3333
labels:
3434
- traefik.enable=true
35-
- traefik.http.routers.backend.rule=Host(`frontend.unstract.localhost`) && (PathPrefix(`/api/v1`) || PathPrefix(`/deployment`))
35+
- traefik.http.routers.backend.rule=Host(`frontend.unstract.localhost`) && (PathPrefix(`/api/v1`) || PathPrefix(`/deployment`) || PathPrefix(`/public`))
3636
- traefik.http.services.backend.loadbalancer.server.port=8000
3737
extra_hosts:
3838
# "host-gateway" is a special string that translates to host docker0 i/f IP.
@@ -134,7 +134,7 @@ services:
134134
- ENVIRONMENT=development
135135
labels:
136136
- traefik.enable=true
137-
- traefik.http.routers.frontend.rule=Host(`frontend.unstract.localhost`) && !PathPrefix(`/api/v1`) && !PathPrefix(`/deployment`)
137+
- traefik.http.routers.frontend.rule=Host(`frontend.unstract.localhost`) && !PathPrefix(`/api/v1`) && !PathPrefix(`/deployment`) && !PathPrefix(`/public`)
138138
- traefik.http.services.frontend.loadbalancer.server.port=80
139139

140140
platform-service:

docker/sample.proxy_overrides.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ http:
55
rule: Host(`frontend.unstract.localhost`)
66
backend:
77
service: backend
8-
rule: Host(`frontend.unstract.localhost`) && (PathPrefix(`/api/v1`) || PathPrefix(`/deployment`))
8+
rule: Host(`frontend.unstract.localhost`) && (PathPrefix(`/api/v1`) || PathPrefix(`/deployment`) || PathPrefix(`/public`))
99

1010
services:
1111
frontend:

frontend/src/components/custom-tools/combined-output/JsonView.jsx

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import { Tabs } from "antd";
22
import TabPane from "antd/es/tabs/TabPane";
33
import Prism from "prismjs";
44
import PropTypes from "prop-types";
5-
import { useEffect, useState } from "react";
5+
import { useEffect, useRef, useState } from "react";
66

7+
import { useCustomToolStore } from "../../../store/custom-tool-store";
78
import { JsonViewBody } from "./JsonViewBody";
89

910
let EnrichedOutputToggle;
@@ -27,17 +28,31 @@ function JsonView({
2728
isSinglePass,
2829
isLoading,
2930
}) {
30-
const [activeView, setActiveView] = useState("Raw");
31+
// Read-only viewers default to the enriched value.
32+
const isPublicSource = useCustomToolStore((s) => s.isPublicSource);
33+
const [activeView, setActiveView] = useState(
34+
isPublicSource ? "Enriched" : "Raw",
35+
);
36+
const didInitTab = useRef(false);
3137

3238
useEffect(() => {
3339
Prism.highlightAll();
3440
}, [combinedOutput, enrichedOutput, activeView]);
3541

3642
useEffect(() => {
37-
if (!enrichedOutput || Object.keys(enrichedOutput).length === 0) {
43+
const hasEnriched =
44+
enrichedOutput && Object.keys(enrichedOutput).length > 0;
45+
if (!hasEnriched) {
3846
setActiveView("Raw");
47+
didInitTab.current = false;
48+
return;
49+
}
50+
// Public viewer default fires once so a manual Raw toggle isn't stomped on re-renders.
51+
if (isPublicSource && !didInitTab.current) {
52+
setActiveView("Enriched");
53+
didInitTab.current = true;
3954
}
40-
}, [enrichedOutput]);
55+
}, [enrichedOutput, isPublicSource]);
4156

4257
const displayOutput =
4358
activeView === "Enriched" &&

frontend/src/hooks/useAxiosPrivate.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,20 @@ function useAxiosPrivate() {
1414
},
1515
async (error) => {
1616
if (error?.response?.status === 401) {
17-
// TODO: Implement Session Expired Modal
18-
logout();
17+
// Skip logout on routes that intentionally render without a session.
18+
const onPublicShare =
19+
typeof globalThis !== "undefined" &&
20+
globalThis.location?.pathname.startsWith("/promptStudio/share/");
21+
if (onPublicShare) {
22+
// Keep a breadcrumb so a stray authenticated probe doesn't go silent.
23+
console.warn("[useAxiosPrivate] Suppressed 401 on public share", {
24+
url: error?.config?.url,
25+
path: globalThis.location?.pathname,
26+
});
27+
} else {
28+
// TODO: Implement Session Expired Modal
29+
logout();
30+
}
1931
}
2032
return Promise.reject(error);
2133
},

0 commit comments

Comments
 (0)