mirror of
https://github.com/maotoumao/MusicFreeDesktop.git
synced 2026-09-03 07:18:13 +08:00
feat: 请求转发
This commit is contained in:
@@ -91,7 +91,7 @@ const config: ForgeConfig = {
|
||||
js: "./src/webworkers/db-worker.ts",
|
||||
name: "db",
|
||||
nodeIntegration: true,
|
||||
},
|
||||
}
|
||||
],
|
||||
},
|
||||
}),
|
||||
|
||||
105
res/.service/request-forwarder.js
Normal file
105
res/.service/request-forwarder.js
Normal file
@@ -0,0 +1,105 @@
|
||||
const http = require("http");
|
||||
const https = require("https");
|
||||
|
||||
|
||||
const defaultPort = 52735;
|
||||
const maxRetries = 20;
|
||||
|
||||
let retryCount = 0;
|
||||
|
||||
function forwardRequest(clientRes, url, method, headers) {
|
||||
const options = {
|
||||
method: method,
|
||||
headers: headers,
|
||||
};
|
||||
|
||||
const protocol = url.startsWith("https") ? https : http;
|
||||
|
||||
const req = protocol.request(url, options, (targetRes) => {
|
||||
// 将目标响应的状态码和头部转发到客户端
|
||||
clientRes.writeHead(targetRes.statusCode, targetRes.headers);
|
||||
|
||||
// 将目标响应的数据流转发到客户端
|
||||
targetRes.pipe(clientRes, {
|
||||
end: true,
|
||||
});
|
||||
});
|
||||
|
||||
req.on("error", (error) => {
|
||||
console.error("Error forwarding request:", error);
|
||||
clientRes.writeHead(500, {"Content-Type": "text/plain"});
|
||||
clientRes.end("Internal Server Error");
|
||||
});
|
||||
|
||||
// 结束目标请求
|
||||
req.end();
|
||||
}
|
||||
|
||||
|
||||
function safeParse(data) {
|
||||
try {
|
||||
return JSON.parse(data) || {};
|
||||
} catch (e) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function startServer(port) {
|
||||
|
||||
// 创建一个 HTTP 服务器
|
||||
const server = http.createServer((req, res) => {
|
||||
if (req.method !== "GET") {
|
||||
res.writeHead(405, {"Content-Type": "text/plain"});
|
||||
return res.end("Only GET requests are allowed");
|
||||
}
|
||||
|
||||
if (req.url === "/heartbeat") {
|
||||
res.writeHead(200, {"Content-Type": "text/plain"});
|
||||
return res.end("OK");
|
||||
}
|
||||
|
||||
const query = new URLSearchParams(req.url.slice(1));
|
||||
|
||||
|
||||
const url = query.get("url");
|
||||
const method = query.get("method") || "GET"; // 默认使用 GET 方法
|
||||
const headers = safeParse(query.get("headers"));
|
||||
|
||||
res.setHeader("Access-Control-Allow-Origin", "*"); // 允许所有源
|
||||
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); // 允许的方法
|
||||
|
||||
if (!url) {
|
||||
res.writeHead(400, {"Content-Type": "text/plain"});
|
||||
return res.end("Bad Request: Missing URL");
|
||||
}
|
||||
|
||||
forwardRequest(res, url, method, {
|
||||
...(req.headers || {}),
|
||||
...(headers || {})
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(port, () => {
|
||||
process.send?.({
|
||||
type: "port",
|
||||
port
|
||||
});
|
||||
console.log(`Proxy server is running on http://localhost:${port}`);
|
||||
});
|
||||
|
||||
server.on("error", (err) => {
|
||||
console.error("Server error:", err);
|
||||
if (retryCount < maxRetries) {
|
||||
retryCount++;
|
||||
const newPort = port + 1; // 尝试下一个端口
|
||||
console.log(`Retrying on port: ${newPort} (attempt ${retryCount})`);
|
||||
startServer(newPort);
|
||||
} else {
|
||||
process.send?.({type: "error", error: "Max retries reached"});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
startServer(defaultPort);
|
||||
@@ -33,7 +33,7 @@ async function registerGlobalShortCut() {
|
||||
const globalShortCuts = AppConfig.getConfig("shortCut.shortcuts");
|
||||
for (const shortCutKey of shortCutKeys) {
|
||||
|
||||
const globalShortCutConfig = globalShortCuts[shortCutKey]?.global;
|
||||
const globalShortCutConfig = globalShortCuts?.[shortCutKey]?.global;
|
||||
|
||||
if (globalShortCutConfig?.length) {
|
||||
await registerSingleShortCut(shortCutKey, globalShortCutConfig);
|
||||
@@ -46,7 +46,7 @@ async function registerSingleShortCut(key: IShortCutKeys, shortCut: string[]) {
|
||||
if (shortCut.length) {
|
||||
const prevConfig = AppConfig.getConfig("shortCut.shortcuts");
|
||||
|
||||
if (prevConfig[key].global?.length) {
|
||||
if (prevConfig[key]?.global?.length) {
|
||||
globalShortcut.unregister(prevConfig[key].global.join("+"));
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import WindowDrag from "@shared/window-drag/main";
|
||||
import {IAppConfig} from "@/types/app-config";
|
||||
import axios from "axios";
|
||||
import {HttpsProxyAgent} from "https-proxy-agent";
|
||||
|
||||
import ServiceManager from "@shared/service-manager/main";
|
||||
|
||||
// portable
|
||||
if (process.platform === "win32") {
|
||||
@@ -162,6 +162,8 @@ app.whenReady().then(async () => {
|
||||
});
|
||||
|
||||
async function bootstrap() {
|
||||
ServiceManager.setup(windowManager);
|
||||
|
||||
const downloadPath = AppConfig.getConfig("download.path");
|
||||
if (!downloadPath) {
|
||||
AppConfig.setConfig({
|
||||
|
||||
@@ -2,4 +2,5 @@
|
||||
import "./common-preload";
|
||||
// https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts
|
||||
|
||||
import "@/shared/message-hub/preload/main";
|
||||
import "@shared/message-hub/preload/main";
|
||||
import "@shared/service-manager/preload";
|
||||
|
||||
@@ -2,6 +2,7 @@ import SvgAsset from "@/renderer/components/SvgAsset";
|
||||
import "./index.scss";
|
||||
import trackPlayer from "@/renderer/core/track-player";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { PlayerState } from "@/common/constant";
|
||||
|
||||
export default function Controller() {
|
||||
const playerState = trackPlayer.usePlayerState();
|
||||
@@ -20,7 +21,7 @@ export default function Controller() {
|
||||
<div
|
||||
className="play-or-pause controller-btn primary-btn"
|
||||
onClick={() => {
|
||||
if(playerState === trackPlayer.PlayerState.Playing) {
|
||||
if(playerState === PlayerState.Playing) {
|
||||
trackPlayer.pause();
|
||||
} else {
|
||||
trackPlayer.resumePlay();
|
||||
@@ -29,7 +30,7 @@ export default function Controller() {
|
||||
>
|
||||
<SvgAsset
|
||||
iconName={
|
||||
playerState !== trackPlayer.PlayerState.Playing ? "play" : "pause"
|
||||
playerState !== PlayerState.Playing ? "play" : "pause"
|
||||
}
|
||||
></SvgAsset>
|
||||
</div>
|
||||
@@ -37,7 +38,7 @@ export default function Controller() {
|
||||
className="skip controller-btn"
|
||||
title={t("music_bar.next_music")}
|
||||
onClick={() => {
|
||||
|
||||
|
||||
trackPlayer.skipToNext();
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -2,7 +2,6 @@ import SvgAsset from "@/renderer/components/SvgAsset";
|
||||
import "./index.scss";
|
||||
import SwitchCase from "@/renderer/components/SwitchCase";
|
||||
import trackPlayer from "@/renderer/core/track-player";
|
||||
import {RepeatMode} from "@/renderer/core/track-player/enum";
|
||||
import {useRef, useState} from "react";
|
||||
import Condition from "@/renderer/components/Condition";
|
||||
import Slider from "rc-slider";
|
||||
@@ -14,6 +13,7 @@ import {useTranslation} from "react-i18next";
|
||||
import AppConfig from "@shared/app-config/renderer";
|
||||
import {isCN} from "@/shared/i18n/renderer";
|
||||
import useAppConfig from "@/hooks/useAppConfig";
|
||||
import {RepeatMode} from "@/common/constant";
|
||||
|
||||
export default function Extra() {
|
||||
const repeatMode = trackPlayer.useRepeatMode();
|
||||
|
||||
@@ -1,26 +1,6 @@
|
||||
import LyricParser, { IParsedLrcItem } from "@/renderer/utils/lyric-parser";
|
||||
import {PlayerState, RepeatMode} from "@/common/constant";
|
||||
|
||||
/** 播放器状态 */
|
||||
export enum PlayerState {
|
||||
/** 无音频 */
|
||||
None,
|
||||
/** 播放中 */
|
||||
Playing,
|
||||
/** 暂停 */
|
||||
Paused,
|
||||
/** 缓冲中 */
|
||||
Buffering,
|
||||
}
|
||||
|
||||
/** 播放模式 */
|
||||
export enum RepeatMode {
|
||||
/** 随机 */
|
||||
Shuffle = "shuffle",
|
||||
/** 播放队列 */
|
||||
Queue = "queue-repeat",
|
||||
/** 单曲循环 */
|
||||
Loop = "loop",
|
||||
}
|
||||
|
||||
/** 错误信息 */
|
||||
export enum ErrorReason {
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
* 播放音乐
|
||||
*/
|
||||
import {encodeUrlHeaders} from "@/common/normalize-util";
|
||||
import {ErrorReason, PlayerState, TrackPlayerEvent} from "./enum";
|
||||
import {ErrorReason, TrackPlayerEvent} from "./enum";
|
||||
import trackPlayerEventsEmitter from "./event";
|
||||
import albumImg from "@/assets/imgs/album-cover.jpg";
|
||||
import getUrlExt from "@/renderer/utils/get-url-ext";
|
||||
import Hls from "hls.js";
|
||||
import {isSameMedia} from "@/common/media-util";
|
||||
import {PlayerState} from "@/common/constant";
|
||||
import ServiceManager from "@shared/service-manager/renderer";
|
||||
|
||||
class TrackPlayerInternal {
|
||||
private audioContext: AudioContext;
|
||||
@@ -29,6 +31,8 @@ class TrackPlayerInternal {
|
||||
});
|
||||
|
||||
this.registerEvents();
|
||||
// @ts-ignore
|
||||
window.ad = this.audio;
|
||||
}
|
||||
|
||||
private throwError(reason: ErrorReason) {
|
||||
@@ -92,15 +96,46 @@ class TrackPlayerInternal {
|
||||
trackSource: IMusic.IMusicSource,
|
||||
musicItem: IMusic.IMusicItem
|
||||
) {
|
||||
// 1. original url
|
||||
let url = trackSource.url;
|
||||
if (trackSource.headers || trackSource.userAgent) {
|
||||
const trackSourceHeaders = trackSource.headers ?? {};
|
||||
if (trackSource.userAgent) {
|
||||
trackSourceHeaders["user-agent"] = trackSource.userAgent;
|
||||
}
|
||||
const urlObj = new URL(trackSource.url);
|
||||
let headers: Record<string, any> | null = null;
|
||||
|
||||
url = encodeUrlHeaders(url, trackSourceHeaders);
|
||||
if (trackSource.headers || trackSource.userAgent) {
|
||||
headers = {...(trackSource.headers ?? {})};
|
||||
if (trackSource.userAgent) {
|
||||
headers["user-agent"] = trackSource.userAgent;
|
||||
}
|
||||
}
|
||||
|
||||
if (urlObj.username && urlObj.password) {
|
||||
const authHeader = `Basic ${btoa(
|
||||
`${decodeURIComponent(urlObj.username)}:${decodeURIComponent(
|
||||
urlObj.password
|
||||
)}`
|
||||
)}`;
|
||||
urlObj.username = "";
|
||||
urlObj.password = "";
|
||||
headers = {
|
||||
...(headers || {}),
|
||||
Authorization: authHeader,
|
||||
}
|
||||
url = urlObj.toString();
|
||||
}
|
||||
|
||||
// hack URL
|
||||
if (headers) {
|
||||
const forwardedUrl = ServiceManager.RequestForwarderService.forwardRequest(url, "GET", headers);
|
||||
if (forwardedUrl) {
|
||||
url = forwardedUrl;
|
||||
headers = null;
|
||||
} else if (!headers["Authorization"]) {
|
||||
url = encodeUrlHeaders(url, headers);
|
||||
headers = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!url) {
|
||||
this.throwError(ErrorReason.EmptyResource);
|
||||
return;
|
||||
@@ -118,36 +153,25 @@ class TrackPlayerInternal {
|
||||
],
|
||||
});
|
||||
// 拓展播放功能
|
||||
if (getUrlExt(url) === ".m3u8" && Hls.isSupported()) {
|
||||
if (getUrlExt(trackSource.url) === ".m3u8" && Hls.isSupported()) {
|
||||
// Todo: headers
|
||||
this.hls.loadSource(url);
|
||||
} else if (headers) {
|
||||
fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
...trackSource.headers,
|
||||
},
|
||||
})
|
||||
.then(async (res) => {
|
||||
console.log("response", res.headers.get("content-type"));
|
||||
const blob = await res.blob();
|
||||
if (isSameMedia(this.currentMusic, musicItem)) {
|
||||
this.audio.src = URL.createObjectURL(blob);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const urlObj = new URL(trackSource.url);
|
||||
if (urlObj.username && urlObj.password) {
|
||||
// TODO: 这部分逻辑需要抽离出来 特殊逻辑
|
||||
const authHeader = `Basic ${btoa(
|
||||
`${decodeURIComponent(urlObj.username)}:${decodeURIComponent(
|
||||
urlObj.password
|
||||
)}`
|
||||
)}`;
|
||||
urlObj.username = "";
|
||||
urlObj.password = "";
|
||||
fetch(urlObj.toString(), {
|
||||
method: "GET",
|
||||
headers: {
|
||||
...trackSource.headers,
|
||||
Authorization: authHeader,
|
||||
},
|
||||
})
|
||||
.then(async (res) => {
|
||||
const blob = await res.blob();
|
||||
if (isSameMedia(this.currentMusic, musicItem)) {
|
||||
this.audio.src = URL.createObjectURL(blob);
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
this.audio.src = url;
|
||||
}
|
||||
this.audio.src = url;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,6 +201,7 @@ class TrackPlayerInternal {
|
||||
seekTo(seconds: number) {
|
||||
if (this.hasSource() && isFinite(seconds)) {
|
||||
const duration = this.audio.duration;
|
||||
console.log(duration);
|
||||
this.audio.currentTime = Math.min(
|
||||
seconds,
|
||||
isNaN(duration) ? Infinity : duration
|
||||
|
||||
@@ -2,8 +2,6 @@ import Store from "@/common/store";
|
||||
import trackPlayer from "./internal";
|
||||
import {
|
||||
ICurrentLyric,
|
||||
PlayerState,
|
||||
RepeatMode,
|
||||
TrackPlayerEvent,
|
||||
} from "./enum";
|
||||
import trackPlayerEventsEmitter from "./event";
|
||||
@@ -15,7 +13,7 @@ import {
|
||||
isSameMedia,
|
||||
sortByTimestampAndIndex,
|
||||
} from "@/common/media-util";
|
||||
import { timeStampSymbol, sortIndexSymbol } from "@/common/constant";
|
||||
import {timeStampSymbol, sortIndexSymbol, PlayerState, RepeatMode} from "@/common/constant";
|
||||
import { callPluginDelegateMethod } from "../plugin-delegate";
|
||||
import LyricParser from "@/renderer/utils/lyric-parser";
|
||||
import {
|
||||
@@ -571,7 +569,7 @@ async function playIndex(nextIndex: number, options: IPlayOptions = {}) {
|
||||
if (!mediaSource?.url) {
|
||||
throw new Error("Empty Source");
|
||||
}
|
||||
console.log("MEDIA SOURCE", mediaSource, musicItem);
|
||||
console.log("MEDIA SOURCE", JSON.stringify(mediaSource), musicItem);
|
||||
if (isSameMedia(musicItem, musicQueueStore.getValue()[currentIndex])) {
|
||||
setCurrentQuality(quality);
|
||||
setCurrentMusic(musicItem);
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
setupRecentlyPlaylist,
|
||||
} from "../core/recently-playlist";
|
||||
import {TrackPlayerEvent} from "../core/track-player/enum";
|
||||
import ServiceManager from "@shared/service-manager/renderer";
|
||||
|
||||
|
||||
setAutoFreeze(false);
|
||||
@@ -46,6 +47,7 @@ export default async function () {
|
||||
localMusic.setupLocalMusic();
|
||||
await Downloader.setupDownloader();
|
||||
setupRecentlyPlaylist();
|
||||
ServiceManager.setup();
|
||||
|
||||
// 自动更新插件
|
||||
if (AppConfig.getConfig("plugin.autoUpdatePlugin")) {
|
||||
|
||||
3
src/shared/service-manager/common.ts
Normal file
3
src/shared/service-manager/common.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export enum ServiceName {
|
||||
RequestForwarder = "request-forwarder",
|
||||
}
|
||||
132
src/shared/service-manager/main.ts
Normal file
132
src/shared/service-manager/main.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import {ChildProcess, fork} from "child_process";
|
||||
import {ipcMain} from "electron";
|
||||
import {IWindowManager} from "@/types/main/window-manager";
|
||||
import {ServiceName} from "@shared/service-manager/common";
|
||||
import getResourcePath from "@/utils/main/get-resource-path";
|
||||
|
||||
|
||||
class ServiceInstance {
|
||||
private serviceProcess: ChildProcess = null;
|
||||
private retryTimeOut = 6000;
|
||||
private started = false;
|
||||
private subprocessName: string;
|
||||
|
||||
private hostChangeCallback: (host: string | null) => void;
|
||||
|
||||
public serviceName: string;
|
||||
|
||||
constructor(serviceName: string, subprocessPath: string) {
|
||||
this.serviceName = serviceName;
|
||||
this.subprocessName = subprocessPath;
|
||||
}
|
||||
|
||||
|
||||
onHostChange(callback: (host: string | null) => void) {
|
||||
this.hostChangeCallback = callback;
|
||||
}
|
||||
|
||||
|
||||
start() {
|
||||
if (this.started) {
|
||||
return;
|
||||
}
|
||||
this.started = true;
|
||||
const servicePath = getResourcePath(".service/" + this.subprocessName + ".js");
|
||||
this.serviceProcess = fork(servicePath);
|
||||
|
||||
interface IMessage {
|
||||
type: "port",
|
||||
port: number
|
||||
}
|
||||
|
||||
this.serviceProcess.on("message", (msg: IMessage) => {
|
||||
const host = "http://127.0.0.1:" + msg.port;
|
||||
this.hostChangeCallback(host);
|
||||
})
|
||||
|
||||
this.serviceProcess.on("error", () => {
|
||||
if (this.started) {
|
||||
setTimeout(() => {
|
||||
this.start(); // 自动重启子进程
|
||||
}, this.retryTimeOut);
|
||||
|
||||
this.retryTimeOut = this.retryTimeOut > 300000 ? 300000 : this.retryTimeOut * 2;
|
||||
}
|
||||
})
|
||||
|
||||
this.serviceProcess.on("exit", (code) => {
|
||||
if (this.started) {
|
||||
console.error(`Service exited with code ${code}. Restarting...`);
|
||||
setTimeout(() => {
|
||||
this.start(); // 自动重启子进程
|
||||
}, this.retryTimeOut);
|
||||
|
||||
this.retryTimeOut = this.retryTimeOut > 300000 ? 300000 : this.retryTimeOut * 2;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.started = false;
|
||||
this.serviceProcess.removeAllListeners();
|
||||
this.serviceProcess.kill();
|
||||
this.serviceProcess = null;
|
||||
this.retryTimeOut = 6000;
|
||||
this.hostChangeCallback(null);
|
||||
}
|
||||
}
|
||||
|
||||
interface IServiceData {
|
||||
instance: ServiceInstance;
|
||||
host: string | null;
|
||||
}
|
||||
|
||||
class ServiceManager {
|
||||
private windowManager: IWindowManager;
|
||||
private serviceMap = new Map<ServiceName, IServiceData>();
|
||||
|
||||
|
||||
private addService(serviceName: ServiceName) {
|
||||
const instance = new ServiceInstance(serviceName, serviceName);
|
||||
this.serviceMap.set(serviceName, {instance, host: null});
|
||||
instance.onHostChange((host) => {
|
||||
const mainWindow = this.windowManager?.mainWindow;
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send("@shared/service-manager/host-changed", serviceName, host);
|
||||
}
|
||||
this.serviceMap.get(serviceName).host = host;
|
||||
});
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
startService(serviceName: ServiceName) {
|
||||
this.serviceMap.get(serviceName)?.instance?.start?.();
|
||||
}
|
||||
|
||||
stopService(serviceName: ServiceName) {
|
||||
this.serviceMap.get(serviceName)?.instance?.stop?.();
|
||||
}
|
||||
|
||||
setup(windowManager: IWindowManager) {
|
||||
this.windowManager = windowManager;
|
||||
// put services here
|
||||
this.addService(ServiceName.RequestForwarder).start();
|
||||
|
||||
|
||||
ipcMain.handle("@shared/service-manager/get-service-hosts", () => {
|
||||
const serviceHosts: Record<string, string> = {};
|
||||
this.serviceMap.forEach((val, key) => {
|
||||
if (val.host) {
|
||||
serviceHosts[key] = val.host;
|
||||
}
|
||||
})
|
||||
return serviceHosts;
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default new ServiceManager();
|
||||
34
src/shared/service-manager/preload.ts
Normal file
34
src/shared/service-manager/preload.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import {contextBridge, ipcRenderer} from "electron";
|
||||
import {ServiceName} from "@shared/service-manager/common";
|
||||
|
||||
const serviceHostMap = new Map<ServiceName, string>();
|
||||
|
||||
ipcRenderer.on("@shared/service-manager/host-changed", (_evt, serviceName: ServiceName, host: string | null) => {
|
||||
if (host) {
|
||||
serviceHostMap.set(serviceName, host);
|
||||
} else {
|
||||
serviceHostMap.delete(serviceName)
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
async function setup() {
|
||||
const hosts = (await ipcRenderer.invoke("@shared/service-manager/get-service-hosts")) || {};
|
||||
const serviceNames = Object.keys(hosts);
|
||||
for (const serviceName of serviceNames) {
|
||||
serviceHostMap.set(serviceName as any, hosts[serviceName]);
|
||||
}
|
||||
}
|
||||
|
||||
function getServiceHost(serviceName: ServiceName) {
|
||||
return serviceHostMap.get(serviceName);
|
||||
}
|
||||
|
||||
const mod = {
|
||||
setup,
|
||||
getServiceHost
|
||||
}
|
||||
|
||||
contextBridge.exposeInMainWorld("@shared/service-manager", mod);
|
||||
|
||||
38
src/shared/service-manager/renderer.ts
Normal file
38
src/shared/service-manager/renderer.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import {ServiceName} from "@shared/service-manager/common";
|
||||
|
||||
interface IMod {
|
||||
setup: () => Promise<void>;
|
||||
getServiceHost: (serviceName: ServiceName) => string | null;
|
||||
}
|
||||
|
||||
const mod = window["@shared/service-manager" as any] as unknown as IMod;
|
||||
|
||||
|
||||
class RequestForwarderService {
|
||||
|
||||
static forwardRequest(url: string, method?: string, headers?: Record<any, any>): string | null {
|
||||
const host = mod.getServiceHost(ServiceName.RequestForwarder);
|
||||
if (!host) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const fUrl = new URL(host)
|
||||
fUrl.searchParams.set("url", url);
|
||||
if (method) {
|
||||
fUrl.searchParams.set("method", method);
|
||||
}
|
||||
if (headers) {
|
||||
fUrl.searchParams.set("headers", JSON.stringify(headers));
|
||||
}
|
||||
return fUrl.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const ServiceManager = {
|
||||
setup: mod.setup,
|
||||
RequestForwarderService
|
||||
}
|
||||
|
||||
export default ServiceManager;
|
||||
Reference in New Issue
Block a user