Fix e621 images and the setup screen in the desktop shell

e621's CDN answers cross-site image loads that carry no Referer with a 403
(Chromium sends none from a custom-scheme page, then blocks the response as
ORB), so images never appeared in the desktop app. The main process now
attaches an e621 referrer to requests for its hosts.

The shell also answers /api, /admin, /static and /health with a 404 JSON
instead of the SPA fallback — that fallback made the setup screen's empty-URL
connection test report "Connected" against the shell itself. The setup screen
is now desktop-aware (no same-origin option, no "Use this server", clearer
copy), and the smoke test runs against a throwaway profile and covers both
regressions.
This commit is contained in:
2026-09-20 21:08:56 -05:00
parent bd2417aff8
commit c992a63b8f
3 changed files with 101 additions and 10 deletions
+29 -8
View File
@@ -17,6 +17,16 @@ import { setToken } from "@/lib/api";
type TestResult = { ok: boolean; text: string } | null;
async function testBackend(base: string): Promise<TestResult> {
// The desktop shell has no server of its own: an empty URL is not a
// same-origin backend, and /health on app://j621 is answered by the shell.
if (!base && window.j621Desktop) {
return {
ok: false,
text:
"The desktop app has no backend of its own. Enter your J621 server's " +
"URL, or continue without a backend.",
};
}
const controller = new AbortController();
const timeout = window.setTimeout(() => controller.abort(), 5_000);
try {
@@ -110,10 +120,15 @@ export function SetupPage() {
</div>
<p className="mt-3 text-sm leading-relaxed text-ctp-subtext0">
Enter the origin this app should call for its API. Leave it blank
when the app and the API are served from the same domain. Without a
backend the app runs in local mode: e621 browsing only, with
credentials kept in this browser.
{window.j621Desktop
? "Enter the origin of your J621 server (for example " +
"https://j621.example.com). Without a backend the app runs in " +
"local mode: e621 browsing only, with credentials kept on this " +
"device."
: "Enter the origin this app should call for its API. Leave it " +
"blank when the app and the API are served from the same " +
"domain. Without a backend the app runs in local mode: e621 " +
"browsing only, with credentials kept in this browser."}
</p>
<label className="mt-5 flex flex-col gap-1.5">
@@ -122,7 +137,11 @@ export function SetupPage() {
</span>
<input
className={cn(inputClass, "font-mono")}
placeholder="https://j621.example.com — blank for this server"
placeholder={
window.j621Desktop
? "https://j621.example.com"
: "https://j621.example.com — blank for this server"
}
value={value}
onChange={(event) => {
setValue(event.target.value);
@@ -161,7 +180,7 @@ export function SetupPage() {
{testing ? <Spinner className="h-3.5 w-3.5" /> : null}
{testing ? "Testing…" : "Test connection"}
</Button>
{value.trim() ? (
{value.trim() && !window.j621Desktop ? (
<Button variant="ghost" onClick={() => save("")}>
Use this server
</Button>
@@ -172,8 +191,10 @@ export function SetupPage() {
</div>
<p className="mt-4 text-xs leading-relaxed text-ctp-overlay0">
Stored in this browser only. Changing the backend signs you out of
the previous one.
{window.j621Desktop
? "Stored in this app only."
: "Stored in this browser only."}{" "}
Changing the backend signs you out of the previous one.
</p>
</div>
</div>