diff --git a/Dockerfile b/Dockerfile index d60331e..5720391 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,4 @@ -# Multi stage build Dockerfile - -# There's no point in using the edge (development branch of alpine) -FROM alpine:3.17 AS build +FROM golang:1.20-alpine AS build # folder structure WORKDIR /usr/src/yt-dlp-webui # install core dependencies @@ -15,7 +12,7 @@ RUN npm install RUN npm run build # build backend + incubator WORKDIR /usr/src/yt-dlp-webui -RUN go build -o yt-dlp-webui +RUN CGO_ENABLED=0 GOOS=linux go build -o yt-dlp-webui # but here yes :) FROM alpine:edge @@ -28,8 +25,7 @@ WORKDIR /app RUN apk update && \ apk add psmisc ffmpeg yt-dlp -COPY --from=build /usr/src/yt-dlp-webui /app -RUN chmod +x /app/yt-dlp-webui +COPY --from=build /usr/src/yt-dlp-webui/yt-dlp-webui /app EXPOSE 3033 CMD [ "./yt-dlp-webui" , "--out", "/downloads" ] diff --git a/Makefile b/Makefile index 882764c..9a546fa 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,14 @@ default: - go build -o yt-dlp-webui main.go + CGO_ENABLED=0 go build -o yt-dlp-webui main.go all: cd frontend && pnpm build && cd .. - go build -o yt-dlp-webui main.go + CGO_ENABLED=0 go build -o yt-dlp-webui main.go multiarch: - GOOS=linux GOARCH=arm go build -o yt-dlp-webui_linux-arm *.go - GOOS=linux GOARCH=arm64 go build -o yt-dlp-webui_linux-arm64 *.go - GOOS=linux GOARCH=amd64 go build -o yt-dlp-webui_linux-amd64 *.go + CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -o yt-dlp-webui_linux-arm main.go + CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o yt-dlp-webui_linux-arm64 main.go + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o yt-dlp-webui_linux-amd64 main.go mkdir -p build mv yt-dlp-webui* build diff --git a/frontend/package.json b/frontend/package.json index b31fd19..621b0c1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "yt-dlp-webui", - "version": "2.0.6", + "version": "2.0.7", "description": "Frontend compontent of yt-dlp-webui", "scripts": { "dev": "vite", @@ -9,28 +9,28 @@ "author": "marcopeocchi", "license": "MPL-2.0", "dependencies": { - "@emotion/react": "^11.10.5", - "@emotion/styled": "^11.10.5", - "@mui/icons-material": "^5.11.0", - "@mui/material": "^5.11.5", - "@reduxjs/toolkit": "^1.9.1", + "@emotion/react": "^11.10.6", + "@emotion/styled": "^11.10.6", + "@mui/icons-material": "^5.11.16", + "@mui/material": "^5.12.0", + "@reduxjs/toolkit": "^1.9.3", "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": "^8.0.5", - "react-router-dom": "^6.7.0", + "react-router-dom": "^6.9.0", "rxjs": "^7.8.0", "uuid": "^9.0.0" }, "devDependencies": { "@modyfi/vite-plugin-yaml": "^1.0.4", - "@types/node": "^18.11.18", - "@types/react": "^18.0.21", + "@types/node": "^18.15.11", + "@types/react": "^18.0.35", "@types/react-dom": "^18.0.10", "@types/react-router-dom": "^5.3.3", - "@types/uuid": "^9.0.0", - "@vitejs/plugin-react": "^3.0.1", + "@types/uuid": "^9.0.1", + "@vitejs/plugin-react": "^3.1.0", "buffer": "^6.0.3", - "typescript": "^4.9.4", - "vite": "^4.0.4" + "typescript": "^5.0.4", + "vite": "^4.2.1" } } \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 86f5df6..16e907b 100755 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,36 +1,38 @@ -import { ThemeProvider } from "@emotion/react"; +import { ThemeProvider } from '@emotion/react' import { ChevronLeft, Dashboard, - // Download, - Menu, Settings as SettingsIcon, FormatListBulleted, + Menu, SettingsEthernet, + Settings as SettingsIcon, Storage -} from "@mui/icons-material"; +} from '@mui/icons-material' import { Box, CircularProgress, - createTheme, CssBaseline, + CssBaseline, Divider, IconButton, List, ListItemIcon, ListItemText, Toolbar, - Typography -} from "@mui/material"; -import { grey } from "@mui/material/colors"; -import ListItemButton from '@mui/material/ListItemButton'; -import { lazy, Suspense, useMemo, useState } from "react"; -import { Provider, useDispatch, useSelector } from "react-redux"; + Typography, + createTheme +} from '@mui/material' +import ListItemButton from '@mui/material/ListItemButton' +import { grey } from '@mui/material/colors' +import { Suspense, lazy, useMemo, useState } from 'react' +import { Provider, useDispatch, useSelector } from 'react-redux' import { - BrowserRouter as Router, Link, Route, + Link, Route, + BrowserRouter as Router, Routes -} from 'react-router-dom'; -import { AppBar } from "./components/AppBar"; -import { Drawer } from "./components/Drawer"; -import { toggleListView } from "./features/settings/settingsSlice"; -import Home from "./Home"; -import { RootState, store } from './stores/store'; -import { formatGiB, getWebSocketEndpoint } from "./utils"; +} from 'react-router-dom' +import Home from './Home' +import { AppBar } from './components/AppBar' +import { Drawer } from './components/Drawer' +import { toggleListView } from './features/settings/settingsSlice' +import { RootState, store } from './stores/store' +import { formatGiB, getWebSocketEndpoint } from './utils' function AppContent() { const [open, setOpen] = useState(false) @@ -179,7 +181,7 @@ function AppContent() { - ); + ) } export function App() { @@ -187,5 +189,5 @@ export function App() { - ); + ) } \ No newline at end of file diff --git a/frontend/src/Home.tsx b/frontend/src/Home.tsx index 82d3740..bbe1449 100644 --- a/frontend/src/Home.tsx +++ b/frontend/src/Home.tsx @@ -1,8 +1,8 @@ -import { FileUpload } from "@mui/icons-material"; +import { FileUpload } from '@mui/icons-material' import { + Alert, Backdrop, Button, - ButtonGroup, CircularProgress, Container, FormControl, @@ -15,21 +15,21 @@ import { Select, Snackbar, styled, - TextField, - Typography -} from "@mui/material"; -import { Buffer } from 'buffer'; -import { useEffect, useMemo, useState } from "react"; -import { useDispatch, useSelector } from "react-redux"; -import { DownloadsCardView } from "./components/DownloadsCardView"; -import { DownloadsListView } from "./components/DownloadsListView"; -import { CliArguments } from "./features/core/argsParser"; -import I18nBuilder from "./features/core/intl"; -import { RPCClient } from "./features/core/rpcClient"; -import { connected, setFreeSpace } from "./features/status/statusSlice"; -import { RootState } from "./stores/store"; -import { IDLMetadata, RPCResult } from "./types"; -import { isValidURL, toFormatArgs } from "./utils"; + TextField +} from '@mui/material' +import { Buffer } from 'buffer' +import { useEffect, useMemo, useRef, useState } from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { DownloadsCardView } from './components/DownloadsCardView' +import { DownloadsListView } from './components/DownloadsListView' +import FormatsGrid from './components/FormatsGrid' +import { CliArguments } from './features/core/argsParser' +import I18nBuilder from './features/core/intl' +import { RPCClient } from './features/core/rpcClient' +import { connected, setFreeSpace } from './features/status/statusSlice' +import { RootState } from './stores/store' +import type { DLMetadata, RPCResult } from './types' +import { isValidURL, toFormatArgs } from './utils' type Props = { socket: WebSocket @@ -42,30 +42,35 @@ export default function Home({ socket }: Props) { const dispatch = useDispatch() // ephemeral state - const [activeDownloads, setActiveDownloads] = useState>(); - const [downloadFormats, setDownloadFormats] = useState(); - const [pickedVideoFormat, setPickedVideoFormat] = useState(''); - const [pickedAudioFormat, setPickedAudioFormat] = useState(''); - const [pickedBestFormat, setPickedBestFormat] = useState(''); + const [activeDownloads, setActiveDownloads] = useState>() + const [downloadFormats, setDownloadFormats] = useState() + const [pickedVideoFormat, setPickedVideoFormat] = useState('') + const [pickedAudioFormat, setPickedAudioFormat] = useState('') + const [pickedBestFormat, setPickedBestFormat] = useState('') - const [customArgs, setCustomArgs] = useState(''); - const [downloadPath, setDownloadPath] = useState(0); - const [availableDownloadPaths, setAvailableDownloadPaths] = useState([]); + const [customArgs, setCustomArgs] = useState('') + const [downloadPath, setDownloadPath] = useState(0) + const [availableDownloadPaths, setAvailableDownloadPaths] = useState([]) - const [fileNameOverride, setFilenameOverride] = useState(''); + const [fileNameOverride, setFilenameOverride] = useState('') - const [url, setUrl] = useState(''); - const [workingUrl, setWorkingUrl] = useState(''); + const [url, setUrl] = useState('') + const [workingUrl, setWorkingUrl] = useState('') - const [showBackdrop, setShowBackdrop] = useState(true); - const [showToast, setShowToast] = useState(true); + const [showBackdrop, setShowBackdrop] = useState(true) + const [showToast, setShowToast] = useState(true) // memos const i18n = useMemo(() => new I18nBuilder(settings.language), [settings.language]) const client = useMemo(() => new RPCClient(socket), [settings.serverAddr, settings.serverPort]) const cliArgs = useMemo(() => new CliArguments().fromString(settings.cliArgs), [settings.cliArgs]) + // refs + const urlInputRef = useRef(null) + const customFilenameInputRef = useRef(null) + /* -------------------- Effects -------------------- */ + /* WebSocket connect event handler*/ useEffect(() => { socket.onopen = () => { @@ -84,8 +89,7 @@ export default function Home({ socket }: Props) { }, [status.connected]) useEffect(() => { - client.freeSpace() - .then(bytes => dispatch(setFreeSpace(bytes.result))) + client.freeSpace().then(bytes => dispatch(setFreeSpace(bytes.result))) }, []) useEffect(() => { @@ -118,7 +122,19 @@ export default function Home({ socket }: Props) { }) }, []) - /* -------------------- component functions -------------------- */ + const [socketHasError, setSocketHasError] = useState(false) + + useEffect(() => { + socket.onerror = () => { + setSocketHasError(true) + setShowBackdrop(false) + } + return () => { + socket.onerror = null + } + }, [socket]) + + /* -------------------- callbacks-------------------- */ /** * Retrive url from input, cli-arguments from checkboxes and emits via WebSocket @@ -222,12 +238,9 @@ export default function Home({ socket }: Props) { } const resetInput = () => { - const input = document.getElementById('urlInput') as HTMLInputElement; - input.value = ''; - - const filename = document.getElementById('customFilenameInput') as HTMLInputElement; - if (filename) { - filename.value = ''; + urlInputRef.current!.value = ''; + if (customFilenameInputRef.current) { + customFilenameInputRef.current!.value = ''; } } @@ -257,7 +270,7 @@ export default function Home({ socket }: Props) { { - settings.enableCustomArgs ? - - - : - null + settings.enableCustomArgs && + + + } { - settings.fileRenaming ? - - - : - null + settings.fileRenaming && + + + } { - settings.pathOverriding ? - - - {i18n.t('customPath')} - - - : - null + settings.pathOverriding && + + + {i18n.t('customPath')} + + + } @@ -351,120 +360,31 @@ export default function Home({ socket }: Props) { {/* Format Selection grid */} - { - downloadFormats ? - - - - - - {downloadFormats.title} - - {/* */} - - - - - {/* video only */} - - - Best quality - - - - - - {/* video only */} - {downloadFormats.formats.filter(format => format.acodec === 'none' && format.vcodec !== 'none').length ? - - - Video data {downloadFormats.formats[1].acodec} - - - : null - } - {downloadFormats.formats - .filter(format => format.acodec === 'none' && format.vcodec !== 'none') - .map((format, idx) => ( - - - - )) - } - {downloadFormats.formats.filter(format => format.acodec === 'none' && format.vcodec !== 'none').length ? - - - Audio data - - - : null - } - {downloadFormats.formats - .filter(format => format.acodec !== 'none' && format.vcodec === 'none') - .map((format, idx) => ( - - - - )) - } - - - - - - - - - - : null - } + {downloadFormats && { + setPickedBestFormat(id) + setPickedVideoFormat('') + setPickedAudioFormat('') + }} + onVideoSelected={(id) => { + setPickedVideoFormat(id) + setPickedBestFormat('') + }} + onAudioSelected={(id) => { + setPickedAudioFormat(id) + setPickedBestFormat('') + }} + onClear={() => { + setPickedAudioFormat(''); + setPickedVideoFormat(''); + setPickedBestFormat(''); + }} + onSubmit={sendUrl} + pickedBestFormat={pickedBestFormat} + pickedVideoFormat={pickedVideoFormat} + pickedAudioFormat={pickedAudioFormat} + />} { settings.listView ? : @@ -473,9 +393,17 @@ export default function Home({ socket }: Props) { setShowToast(false)} - /> - + > + + {`Connected to (${settings.serverAddr}:${settings.serverPort})`} + + + + + {`${i18n.t('rpcConnErr')} (${settings.serverAddr}:${settings.serverPort})`} + + + ); } \ No newline at end of file diff --git a/frontend/src/Settings.tsx b/frontend/src/Settings.tsx index 5e2faad..c32fdf1 100644 --- a/frontend/src/Settings.tsx +++ b/frontend/src/Settings.tsx @@ -16,15 +16,16 @@ import { Switch, TextField, Typography -} from "@mui/material"; -import { useMemo, useState } from "react"; -import { useDispatch, useSelector } from "react-redux"; -import { debounceTime, distinctUntilChanged, map, of, takeWhile } from "rxjs"; -import { CliArguments } from "./features/core/argsParser"; -import I18nBuilder from "./features/core/intl"; -import { RPCClient } from "./features/core/rpcClient"; +} from '@mui/material' +import { useEffect, useMemo, useState } from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { Subject, debounceTime, distinctUntilChanged, map, takeWhile } from 'rxjs' +import { CliArguments } from './features/core/argsParser' +import I18nBuilder from './features/core/intl' +import { RPCClient } from './features/core/rpcClient' import { LanguageUnion, + ThemeUnion, setCliArgs, setEnableCustomArgs, setFileRenaming, @@ -33,32 +34,31 @@ import { setPathOverriding, setServerAddr, setServerPort, - setTheme, - ThemeUnion -} from "./features/settings/settingsSlice"; -import { updated } from "./features/status/statusSlice"; -import { RootState } from "./stores/store"; -import { validateDomain, validateIP } from "./utils"; + setTheme +} from './features/settings/settingsSlice' +import { updated } from './features/status/statusSlice' +import { RootState } from './stores/store' +import { validateDomain, validateIP } from './utils' export default function Settings({ socket }: { socket: WebSocket }) { - const settings = useSelector((state: RootState) => state.settings) - const status = useSelector((state: RootState) => state.status) const dispatch = useDispatch() + const status = useSelector((state: RootState) => state.status) + const settings = useSelector((state: RootState) => state.settings) + const [invalidIP, setInvalidIP] = useState(false); const i18n = useMemo(() => new I18nBuilder(settings.language), [settings.language]) - const client = useMemo(() => new RPCClient(socket), [settings.serverAddr, settings.serverPort]) - const cliArgs = useMemo(() => new CliArguments().fromString(settings.cliArgs), [settings.cliArgs]) - /** - * Update the server ip address state and localstorage whenever the input value changes. - * Validate the ip-addr then set.s - * @param event Input change event - */ - const handleAddrChange = (event: any) => { - const $serverAddr = of(event) + + const client = useMemo(() => new RPCClient(socket), []) + const cliArgs = useMemo(() => new CliArguments().fromString(settings.cliArgs), []) + + const serverAddr$ = useMemo(() => new Subject(), []) + const serverPort$ = useMemo(() => new Subject(), []) + + useEffect(() => { + const sub = serverAddr$ .pipe( - map(event => event.target.value), debounceTime(500), distinctUntilChanged() ) @@ -73,24 +73,20 @@ export default function Settings({ socket }: { socket: WebSocket }) { setInvalidIP(true) } }) - return $serverAddr.unsubscribe() - } + return () => sub.unsubscribe() + }, [serverAddr$]) - /** - * Set server port - */ - const handlePortChange = (event: any) => { - const $port = of(event) + useEffect(() => { + const sub = serverPort$ .pipe( - map(event => event.target.value), map(val => Number(val)), takeWhile(val => isFinite(val) && val <= 65535), ) .subscribe(port => { dispatch(setServerPort(port.toString())) }) - return $port.unsubscribe() - } + return () => sub.unsubscribe() + }, []) /** * Language toggler handler @@ -107,7 +103,7 @@ export default function Settings({ socket }: { socket: WebSocket }) { } /** - * Send via WebSocket a message in order to update the yt-dlp binary from server + * Send via WebSocket a message to update yt-dlp binary */ const updateBinary = () => { client.updateExecutable().then(() => dispatch(updated())) @@ -136,7 +132,7 @@ export default function Settings({ socket }: { socket: WebSocket }) { label={i18n.t('serverAddressTitle')} defaultValue={settings.serverAddr} error={invalidIP} - onChange={handleAddrChange} + onChange={(e) => serverAddr$.next(e.currentTarget.value)} InputProps={{ startAdornment: ws://, }} @@ -148,7 +144,7 @@ export default function Settings({ socket }: { socket: WebSocket }) { fullWidth label={i18n.t('serverPortTitle')} defaultValue={settings.serverPort} - onChange={handlePortChange} + onChange={(e) => serverPort$.next(e.currentTarget.value)} error={isNaN(Number(settings.serverPort)) || Number(settings.serverPort) > 65535} sx={{ mb: 2 }} /> @@ -186,16 +182,6 @@ export default function Settings({ socket }: { socket: WebSocket }) { - {/* - 65535} - sx={{ mb: 2 }} - /> - */} void + onVideoSelected: (format: string) => void + onBestQualitySelected: (format: string) => void + onSubmit: () => void + onClear: () => void + pickedBestFormat: string + pickedAudioFormat: string + pickedVideoFormat: string +} + +export default function FormatsGrid({ + downloadFormats, + onAudioSelected, + onVideoSelected, + onBestQualitySelected, + onSubmit, + onClear, + pickedBestFormat, + pickedAudioFormat, + pickedVideoFormat, +}: Props) { + return ( + + + + + + + {downloadFormats.title} + + {/* */} + + + + + {/* video only */} + + + Best quality + + + + + + {/* video only */} + {downloadFormats.formats.filter(format => format.acodec === 'none' && format.vcodec !== 'none').length && + + + Video data {downloadFormats.formats[1].acodec} + + + } + {downloadFormats.formats + .filter(format => format.acodec === 'none' && format.vcodec !== 'none') + .map((format, idx) => ( + + + + )) + } + {downloadFormats.formats.filter(format => format.acodec === 'none' && format.vcodec !== 'none').length && + + + Audio data + + + } + {downloadFormats.formats + .filter(format => format.acodec !== 'none' && format.vcodec === 'none') + .map((format, idx) => ( + + + + )) + } + + + + + + + + + + + ) +} \ No newline at end of file diff --git a/frontend/src/features/core/rpcClient.ts b/frontend/src/features/core/rpcClient.ts index 4632771..ca0928f 100644 --- a/frontend/src/features/core/rpcClient.ts +++ b/frontend/src/features/core/rpcClient.ts @@ -1,4 +1,4 @@ -import type { RPCRequest, RPCResponse, IDLMetadata } from "../../types" +import type { RPCRequest, RPCResponse, DLMetadata } from "../../types" import { getHttpRPCEndpoint } from '../../utils' @@ -19,18 +19,17 @@ export class RPCClient { this.socket.send(JSON.stringify(req)) } - private sendHTTP(req: RPCRequest) { - return new Promise>((resolve) => { - fetch(getHttpRPCEndpoint(), { - method: 'POST', - body: JSON.stringify({ - id: this.incrementSeq(), - ...req - }) + private async sendHTTP(req: RPCRequest) { + const res = await fetch(getHttpRPCEndpoint(), { + method: 'POST', + body: JSON.stringify({ + ...req, + id: this.incrementSeq(), }) - .then(res => res.json()) - .then(data => resolve(data)) }) + const data: RPCResponse = await res.json() + + return data } public download(url: string, args: string, pathOverride = '', renameTo = '') { @@ -50,7 +49,7 @@ export class RPCClient { public formats(url: string) { if (url) { - return this.sendHTTP({ + return this.sendHTTP({ id: this.incrementSeq(), method: 'Service.Formats', params: [{ diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index be257af..bfbb735 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -1,10 +1,10 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' import { App } from './App' -const root = ReactDOM.createRoot(document.getElementById('root')!) +const root = createRoot(document.getElementById('root')!) root.render( - - - + + + ) diff --git a/frontend/src/types.d.ts b/frontend/src/types.d.ts index 79f0ae7..f807ad6 100644 --- a/frontend/src/types.d.ts +++ b/frontend/src/types.d.ts @@ -45,14 +45,14 @@ export type RPCParams = { Params?: string } -export interface IDLMetadata { - formats: Array, - best: IDLFormat, +export interface DLMetadata { + formats: Array, + best: DLFormat, thumbnail: string, title: string, } -export interface IDLFormat { +export interface DLFormat { format_id: string, format_note: string, fps: number, diff --git a/go.mod b/go.mod index 200f5c6..35f9947 100644 --- a/go.mod +++ b/go.mod @@ -3,25 +3,28 @@ module github.com/marcopeocchi/yt-dlp-web-ui go 1.19 require ( - github.com/goccy/go-json v0.10.0 - github.com/gofiber/fiber/v2 v2.41.0 - github.com/gofiber/websocket/v2 v2.1.2 + github.com/goccy/go-json v0.10.2 + github.com/gofiber/fiber/v2 v2.43.0 + github.com/gofiber/websocket/v2 v2.1.5 github.com/google/uuid v1.3.0 - github.com/marcopeocchi/fazzoletti v0.0.0-20221114144444-1e802380a7db - golang.org/x/sys v0.4.0 + github.com/marcopeocchi/fazzoletti v0.0.0-20230308161120-c545580f79fa + golang.org/x/sys v0.7.0 gopkg.in/yaml.v3 v3.0.1 ) require ( - github.com/andybalholm/brotli v1.0.4 // indirect - github.com/fasthttp/websocket v1.5.0 // indirect - github.com/klauspost/compress v1.15.14 // indirect + github.com/andybalholm/brotli v1.0.5 // indirect + github.com/fasthttp/websocket v1.5.2 // indirect + github.com/klauspost/compress v1.16.4 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-isatty v0.0.18 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect - github.com/rivo/uniseg v0.4.3 // indirect - github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d // indirect + github.com/philhofer/fwd v1.1.2 // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect + github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect + github.com/tinylib/msgp v1.1.8 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.43.0 // indirect + github.com/valyala/fasthttp v1.45.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect ) diff --git a/go.sum b/go.sum index 9b0b8e4..74e695e 100644 --- a/go.sum +++ b/go.sum @@ -1,61 +1,130 @@ github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/fasthttp/websocket v1.5.0 h1:B4zbe3xXyvIdnqjOZrafVFklCUq5ZLo/TqCt5JA1wLE= github.com/fasthttp/websocket v1.5.0/go.mod h1:n0BlOQvJdPbTuBkZT0O5+jk/sp/1/VCzquR1BehI2F4= +github.com/fasthttp/websocket v1.5.2 h1:KdCb0EpLpdJpfE3IPA5YLK/aYBO3dhZcvwxz6tXe2LQ= +github.com/fasthttp/websocket v1.5.2/go.mod h1:S0KC1VBlx1SaXGXq7yi1wKz4jMub58qEnHQG9oHuqBw= github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gofiber/fiber/v2 v2.40.1/go.mod h1:Gko04sLksnHbzLSRBFWPFdzM9Ws9pRxvvIaohJK1dsk= github.com/gofiber/fiber/v2 v2.41.0 h1:YhNoUS/OTjEz+/WLYuQ01xI7RXgKEFnGBKMagAu5f0M= github.com/gofiber/fiber/v2 v2.41.0/go.mod h1:RdebcCuCRFp4W6hr3968/XxwJVg0K+jr9/Ae0PFzZ0Q= +github.com/gofiber/fiber/v2 v2.43.0 h1:yit3E4kHf178B60p5CQBa/3v+WVuziWMa/G2ZNyLJB0= +github.com/gofiber/fiber/v2 v2.43.0/go.mod h1:mpS1ZNE5jU+u+BA4FbM+KKnUzJ4wzTK+FT2tG3tU+6I= github.com/gofiber/websocket/v2 v2.1.2 h1:EulKyLB/fJgui5+6c8irwEnYQ9FRsrLZfkrq9OfTDGc= github.com/gofiber/websocket/v2 v2.1.2/go.mod h1:S+sKWo0xeC7Wnz5h4/8f6D/NxsrLFIdWDYB3SyVO9pE= +github.com/gofiber/websocket/v2 v2.1.5 h1:2weAMr0Shb2ubhZ3+P4bkeWL+uCZ/NlgjSa1siEcvFM= +github.com/gofiber/websocket/v2 v2.1.5/go.mod h1:BZZEk+XsjjF0V6/sAw00iGcB69dFb6Hb85ER9gr/xaU= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/klauspost/compress v1.14.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.15.14 h1:i7WCKDToww0wA+9qrUZ1xOjp218vfFo3nTU6UHp+gOc= github.com/klauspost/compress v1.15.14/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/compress v1.16.4 h1:91KN02FnsOYhuunwU4ssRe8lc2JosWmizWa91B5v1PU= +github.com/klauspost/compress v1.16.4/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/marcopeocchi/fazzoletti v0.0.0-20221114144444-1e802380a7db h1:SmKRgCLsImPxBTIzmUpbQyv+7FembiZaq/QTwtDqar4= github.com/marcopeocchi/fazzoletti v0.0.0-20221114144444-1e802380a7db/go.mod h1:RvfVo/6Sbnfra9kkvIxDW8NYOOaYsHjF0DdtMCs9cdo= +github.com/marcopeocchi/fazzoletti v0.0.0-20230308161120-c545580f79fa h1:uaAQLGhN4SesB9inOQ1Q6EH+BwTWHQOvwhR0TIJvnYc= +github.com/marcopeocchi/fazzoletti v0.0.0-20230308161120-c545580f79fa/go.mod h1:RvfVo/6Sbnfra9kkvIxDW8NYOOaYsHjF0DdtMCs9cdo= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= +github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 h1:rmMl4fXJhKMNWl+K+r/fq4FbbKI+Ia2m9hYBLm2h4G4= +github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94/go.mod h1:90zrgN3D/WJsDd1iXHT96alCoN2KJo6/4x1DZC3wZs8= github.com/savsgio/gotils v0.0.0-20211223103454-d0aaa54c5899/go.mod h1:oejLrk1Y/5zOF+c/aHtXqn3TFlzzbAgPWg8zBiAHDas= github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d h1:Q+gqLBOPkFGHyCJxXMRqtUgUbTjI8/Ze8vu8GGyNFwo= github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4= +github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= +github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= +github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw= +github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= +github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.33.0/go.mod h1:KJRK/MXx0J+yd0c5hlR+s1tIHD72sniU8ZJjl97LIw4= github.com/valyala/fasthttp v1.41.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY= github.com/valyala/fasthttp v1.43.0 h1:Gy4sb32C98fbzVWZlTM1oTMdLWGyvxR03VhM6cBIU4g= github.com/valyala/fasthttp v1.43.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY= +github.com/valyala/fasthttp v1.45.0 h1:zPkkzpIn8tdHZUrVa6PzYd0i5verqiPSkgTd3bSUcpA= +github.com/valyala/fasthttp v1.45.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/main.go b/main.go index 7a1283d..0feadaa 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,6 @@ package main import ( - "context" "embed" "flag" "io/fs" @@ -11,8 +10,6 @@ import ( "github.com/marcopeocchi/yt-dlp-web-ui/server/config" ) -type ContextKey interface{} - var ( port int downloadPath string @@ -48,9 +45,5 @@ func main() { cfg.DownloadPath(downloadPath) cfg.DownloaderPath(downloaderPath) - ctx := context.Background() - ctx = context.WithValue(ctx, ContextKey("port"), port) - ctx = context.WithValue(ctx, ContextKey("frontend"), frontend) - - server.RunBlocking(ctx) + server.RunBlocking(port, frontend) } diff --git a/server/config/config_singleton.go b/server/config/parser.go similarity index 100% rename from server/config/config_singleton.go rename to server/config/parser.go diff --git a/server/server.go b/server/server.go index 9e3f419..4a262b3 100644 --- a/server/server.go +++ b/server/server.go @@ -1,7 +1,6 @@ package server import ( - "context" "fmt" "io" "io/fs" @@ -17,10 +16,7 @@ import ( var db MemoryDB -func RunBlocking(ctx context.Context) { - fe := ctx.Value("frontend").(fs.SubFS) - port := ctx.Value("port").(int) - +func RunBlocking(port int, frontend fs.FS) { service := new(Service) rpc.Register(service) @@ -28,9 +24,13 @@ func RunBlocking(ctx context.Context) { app.Use(cors.New()) app.Use("/", filesystem.New(filesystem.Config{ - Root: http.FS(fe), + Root: http.FS(frontend), })) + app.Get("/settings", func(c *fiber.Ctx) error { + return c.Redirect("/") + }) + // RPC handlers // websocket app.Get("/ws-rpc", websocket.New(func(c *websocket.Conn) { @@ -52,8 +52,10 @@ func RunBlocking(ctx context.Context) { app.Post("/http-rpc", func(c *fiber.Ctx) error { reader := c.Context().RequestBodyStream() writer := c.Response().BodyWriter() + res := NewRPCRequest(reader).Call() io.Copy(writer, res) + return nil })