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