mirror of
https://github.com/moku-project/Moku.git
synced 2026-06-13 01:09:56 -05:00
Fix: Linked CORS Bypass to UI-LOGIN
This commit is contained in:
@@ -35,8 +35,5 @@ In-Progress:
|
||||
|
||||
- Wire Series-Detail Refresh to Fix Manga-Metadata (MAJOR)
|
||||
|
||||
- Add Disable Auto-Completed Feature to Library
|
||||
- Cap ReaderSettings Zoom (100)
|
||||
- Fix SeriesDetail Chapter Amount (Link to Scanlator Filtering)
|
||||
|
||||
- UI LOGIN DOES NOT WORK OFFLINE
|
||||
Notes from last time:
|
||||
|
||||
+35
-3
@@ -1,10 +1,10 @@
|
||||
import { store } from "@store/state.svelte";
|
||||
import { fetchAuthenticated, AuthRequiredError } from "../core/auth";
|
||||
import { fetchAuthenticated, AuthRequiredError, uiAuth } from "../core/auth";
|
||||
import { boot } from "@store/boot.svelte";
|
||||
|
||||
const DEFAULT_URL = "http://127.0.0.1:4567";
|
||||
|
||||
function getServerUrl(): string {
|
||||
export function getServerUrl(): string {
|
||||
const url = store.settings.serverUrl;
|
||||
return typeof url === "string" && url.trim() ? url.replace(/\/$/, "") : DEFAULT_URL;
|
||||
}
|
||||
@@ -12,7 +12,20 @@ function getServerUrl(): string {
|
||||
export function plainThumbUrl(path: string): string {
|
||||
if (!path) return "";
|
||||
if (path.startsWith("http")) return path;
|
||||
return `${getServerUrl()}${path}`;
|
||||
|
||||
const base = `${getServerUrl()}${path}`;
|
||||
const mode = store.settings.serverAuthMode ?? "NONE";
|
||||
|
||||
if (mode === "UI_LOGIN") {
|
||||
const token = uiAuth.getToken();
|
||||
if (token) {
|
||||
const url = new URL(base);
|
||||
url.searchParams.set("authorization", token);
|
||||
return url.toString();
|
||||
}
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
export const thumbUrl = plainThumbUrl;
|
||||
@@ -58,6 +71,25 @@ async function fetchWithRetry(
|
||||
throw new Error("unreachable");
|
||||
}
|
||||
|
||||
export async function fetchImage(
|
||||
path: string,
|
||||
signal?: AbortSignal,
|
||||
): Promise<{ src: string; revoke: () => void }> {
|
||||
if (!path) return { src: "", revoke: () => {} };
|
||||
|
||||
const url = path.startsWith("http") ? path : `${getServerUrl()}${path}`;
|
||||
const mode = store.settings.serverAuthMode ?? "NONE";
|
||||
|
||||
if (mode === "NONE") return { src: url, revoke: () => {} };
|
||||
|
||||
const res = await fetchWithRetry(url, { method: "GET" }, signal);
|
||||
if (!res.ok) throw new Error(`Image fetch failed: ${res.status}`);
|
||||
|
||||
const blob = await res.blob();
|
||||
const src = URL.createObjectURL(blob);
|
||||
return { src, revoke: () => URL.revokeObjectURL(src) };
|
||||
}
|
||||
|
||||
export async function gql<T>(
|
||||
query: string,
|
||||
variables?: Record<string, unknown>,
|
||||
|
||||
+4
-3
@@ -9,12 +9,13 @@ export class AuthRequiredError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
let _accessToken: string | null = null;
|
||||
const TOKEN_KEY = "moku_access_token";
|
||||
let _accessToken: string | null = sessionStorage.getItem(TOKEN_KEY);
|
||||
|
||||
export const uiAuth = {
|
||||
getToken: () => _accessToken,
|
||||
setToken: (t: string) => { _accessToken = t; },
|
||||
clearToken: () => { _accessToken = null; },
|
||||
setToken: (t: string) => { _accessToken = t; sessionStorage.setItem(TOKEN_KEY, t); },
|
||||
clearToken: () => { _accessToken = null; sessionStorage.removeItem(TOKEN_KEY); },
|
||||
};
|
||||
|
||||
export const authSession = {
|
||||
|
||||
Vendored
+12
-3
@@ -1,5 +1,6 @@
|
||||
import { fetch as tauriFetch } from "@tauri-apps/plugin-http";
|
||||
import { store } from "@store/state.svelte";
|
||||
import { uiAuth } from "@core/auth";
|
||||
|
||||
const cache = new Map<string, string>();
|
||||
const inflight = new Map<string, Promise<string>>();
|
||||
@@ -17,9 +18,17 @@ interface QueueEntry {
|
||||
const queue: QueueEntry[] = [];
|
||||
|
||||
function getAuthHeaders(): Record<string, string> {
|
||||
const user = store.settings.serverAuthUser?.trim() ?? "";
|
||||
const pass = store.settings.serverAuthPass?.trim() ?? "";
|
||||
return user && pass ? { Authorization: `Basic ${btoa(`${user}:${pass}`)}` } : {};
|
||||
const mode = store.settings.serverAuthMode ?? "NONE";
|
||||
if (mode === "UI_LOGIN") {
|
||||
const token = uiAuth.getToken();
|
||||
return token ? { Authorization: `Bearer ${token}` } : {};
|
||||
}
|
||||
if (mode === "BASIC_AUTH") {
|
||||
const user = store.settings.serverAuthUser?.trim() ?? "";
|
||||
const pass = store.settings.serverAuthPass?.trim() ?? "";
|
||||
return user && pass ? { Authorization: `Basic ${btoa(`${user}:${pass}`)}` } : {};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
async function doFetch(url: string): Promise<string> {
|
||||
|
||||
Vendored
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { gql, plainThumbUrl } from "@api/client";
|
||||
import { gql, getServerUrl } from "@api/client";
|
||||
import { getBlobUrl, preloadBlobUrls } from "@core/cache/imageCache";
|
||||
import { dedupeRequest } from "@core/async/batchRequests";
|
||||
import { FETCH_CHAPTER_PAGES } from "@api/mutations/chapters";
|
||||
@@ -29,7 +29,7 @@ export function fetchPages(
|
||||
const p = dedupeRequest(`chapter-pages:${chapterId}`, () =>
|
||||
gql<{ fetchChapterPages: { pages: string[] } }>(FETCH_CHAPTER_PAGES, { chapterId })
|
||||
.then(d => {
|
||||
const urls = d.fetchChapterPages.pages.map(p => plainThumbUrl(p));
|
||||
const urls = d.fetchChapterPages.pages.map(p => p.startsWith("http") ? p : `${getServerUrl()}${p}`);
|
||||
if (useBlob) {
|
||||
if (urls[priorityPage]) getBlobUrl(urls[priorityPage], urls.length + 999);
|
||||
preloadBlobUrls(urls.filter((_, i) => i !== priorityPage), urls.length);
|
||||
|
||||
@@ -97,7 +97,10 @@
|
||||
const path = heroThumbSrc;
|
||||
const mode = store.settings.serverAuthMode ?? "NONE";
|
||||
if (!path) { heroThumb = ""; return; }
|
||||
if (mode !== "BASIC_AUTH") { heroThumb = thumbUrl(path); return; }
|
||||
|
||||
const needsBlob = mode === "BASIC_AUTH" || mode === "UI_LOGIN";
|
||||
if (!needsBlob) { heroThumb = thumbUrl(path); return; }
|
||||
|
||||
getBlobUrl(thumbUrl(path))
|
||||
.then(url => { heroThumb = url; })
|
||||
.catch(() => { heroThumb = ""; });
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { thumbUrl, plainThumbUrl } from "@api/client";
|
||||
import { thumbUrl, getServerUrl } from "@api/client";
|
||||
import { store } from "@store/state.svelte";
|
||||
import { getBlobUrl } from "@core/cache/imageCache";
|
||||
|
||||
@@ -23,7 +23,10 @@
|
||||
[key: string]: any;
|
||||
} = $props();
|
||||
|
||||
const isAuth = $derived(store.settings.serverAuthMode === "BASIC_AUTH");
|
||||
const isAuth = $derived(
|
||||
store.settings.serverAuthMode === "BASIC_AUTH" ||
|
||||
store.settings.serverAuthMode === "UI_LOGIN"
|
||||
);
|
||||
|
||||
let blobUrl = $state("");
|
||||
let reqId = 0;
|
||||
@@ -36,7 +39,8 @@
|
||||
if (!_isAuth || !_src) { blobUrl = ""; return; }
|
||||
|
||||
const id = ++reqId;
|
||||
getBlobUrl(plainThumbUrl(_src), _priority)
|
||||
const bareUrl = _src.startsWith("http") ? _src : `${getServerUrl()}${_src}`;
|
||||
getBlobUrl(bareUrl, _priority)
|
||||
.then(u => { if (id === reqId) blobUrl = u; })
|
||||
.catch(() => { if (id === reqId) blobUrl = ""; });
|
||||
});
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ export default defineConfig({
|
||||
envPrefix: ["VITE_", "TAURI_"],
|
||||
build: {
|
||||
target: ["es2021", "chrome100", "safari13"],
|
||||
minify: !process.env.TAURI_DEBUG ? "esbuild" : false,
|
||||
minify: !process.env.TAURI_DEBUG ? "oxc" : false,
|
||||
sourcemap: !!process.env.TAURI_DEBUG,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user