mirror of
https://github.com/idanoo/autobrr
synced 2025-07-23 08:49:13 +00:00
Feature: Tail live logs (#27)
* chore: add new yarn.lock * chore: add pkgs * feat: push events via server-sent-events * feat(web): tail live logs * fix: update irc network * fix: set baseurl * fix: headers
This commit is contained in:
parent
11fcf1ead9
commit
09eb0b1716
14 changed files with 213 additions and 26 deletions
|
@ -1,5 +1,5 @@
|
|||
import {Action, DownloadClient, Filter, Indexer, Network} from "../domain/interfaces";
|
||||
import {baseUrl} from "../utils/utils";
|
||||
import {baseUrl, sseBaseUrl} from "../utils/utils";
|
||||
|
||||
function baseClient(endpoint: string, method: string, { body, ...customConfig}: any = {}) {
|
||||
let baseURL = baseUrl()
|
||||
|
@ -106,6 +106,9 @@ const APIClient = {
|
|||
updateNetwork: (network: Network) => appClient.Put(`api/irc/network/${network.id}`, network),
|
||||
deleteNetwork: (id: number) => appClient.Delete(`api/irc/network/${id}`),
|
||||
},
|
||||
events: {
|
||||
logs: () => new EventSource(`${sseBaseUrl()}api/events?stream=logs`, { withCredentials: true })
|
||||
}
|
||||
}
|
||||
|
||||
export default APIClient;
|
|
@ -58,11 +58,7 @@ function IrcNetworkUpdateForm({ isOpen, toggle, network }: any) {
|
|||
};
|
||||
|
||||
const validate = (values: any) => {
|
||||
const errors = {
|
||||
nickserv: {
|
||||
account: null,
|
||||
}
|
||||
} as any;
|
||||
const errors = {} as any;
|
||||
|
||||
if (!values.name) {
|
||||
errors.name = "Required";
|
||||
|
@ -125,7 +121,7 @@ function IrcNetworkUpdateForm({ isOpen, toggle, network }: any) {
|
|||
nickserv: network.nickserv,
|
||||
pass: network.pass,
|
||||
invite_command: network.invite_command,
|
||||
connect_commands: network.connect_commands,
|
||||
// connect_commands: network.connect_commands,
|
||||
channels: network.channels
|
||||
}}
|
||||
mutators={{
|
||||
|
@ -274,7 +270,7 @@ function IrcNetworkUpdateForm({ isOpen, toggle, network }: any) {
|
|||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={pristine || invalid}
|
||||
// disabled={pristine || invalid}
|
||||
className={classNames(pristine || invalid ? "bg-indigo-300" : "bg-indigo-600 hover:bg-indigo-700", "inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500")}
|
||||
>
|
||||
Save
|
||||
|
|
|
@ -5,6 +5,7 @@ import {NavLink,Link, Route, Switch} from "react-router-dom";
|
|||
import Settings from "./Settings";
|
||||
import { Dashboard } from "./Dashboard";
|
||||
import { FilterDetails, Filters} from "./Filters";
|
||||
import Logs from './Logs';
|
||||
|
||||
const profile = ['Settings', 'Sign out']
|
||||
|
||||
|
@ -13,7 +14,7 @@ function classNames(...classes: string[]) {
|
|||
}
|
||||
|
||||
export default function Base() {
|
||||
const nav = [{name: 'Dashboard', path: "/"}, {name: 'Filters', path: "/filters"}, {name: "Settings", path: "/settings"}]
|
||||
const nav = [{name: 'Dashboard', path: "/"}, {name: 'Filters', path: "/filters"}, {name: "Settings", path: "/settings"},{name: "Logs", path: "/logs"}]
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
@ -180,6 +181,10 @@ export default function Base() {
|
|||
</Disclosure>
|
||||
|
||||
<Switch>
|
||||
<Route path="/logs">
|
||||
<Logs />
|
||||
</Route>
|
||||
|
||||
<Route path="/settings">
|
||||
<Settings/>
|
||||
</Route>
|
||||
|
|
59
web/src/screens/Logs.tsx
Normal file
59
web/src/screens/Logs.tsx
Normal file
|
@ -0,0 +1,59 @@
|
|||
import { useEffect, useRef, useState } from "react";
|
||||
import APIClient from "../api/APIClient";
|
||||
|
||||
type LogEvent = {
|
||||
time: string;
|
||||
level: string;
|
||||
message: string;
|
||||
};
|
||||
|
||||
export default function Logs() {
|
||||
const [logs, setLogs] = useState<LogEvent[]>([])
|
||||
|
||||
const messagesEndRef: any = useRef(null)
|
||||
|
||||
const scrollToBottom = () => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: "auto" })
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const es = APIClient.events.logs()
|
||||
|
||||
es.onmessage = (event) => {
|
||||
let d: LogEvent = JSON.parse(event.data)
|
||||
|
||||
setLogs(prevState => ([...prevState, d]))
|
||||
scrollToBottom()
|
||||
}
|
||||
return () => {
|
||||
es.close()
|
||||
}
|
||||
}, [setLogs]);
|
||||
|
||||
return (
|
||||
<main className="-mt-48">
|
||||
<header className="py-10">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<h1 className="text-3xl font-bold text-white capitalize">Logs</h1>
|
||||
</div>
|
||||
</header>
|
||||
<div className="max-w-7xl mx-auto pb-12 px-2 sm:px-4 lg:px-8">
|
||||
<div className="bg-white rounded-lg shadow px-2 sm:px-4 py-3 sm:py-4">
|
||||
<div className=" overflow-y-auto p-2 rounded-lg h-96 bg-gray-900">
|
||||
{logs.map((a, idx) => (
|
||||
<p key={idx}>
|
||||
<span className="font-mono text-gray-600 mr-2">{a.time}</span>
|
||||
{a.level === "TRACE" && <span className="font-mono font-semibold text-purple-300">{a.level}</span>}
|
||||
{a.level === "DEBUG" && <span className="font-mono font-semibold text-yellow-500">{a.level}</span>}
|
||||
{a.level === "INFO" && <span className="font-mono font-semibold text-green-500">{a.level} </span>}
|
||||
{a.level === "ERROR" && <span className="font-mono font-semibold text-red-500">{a.level}</span>}
|
||||
<span className="ml-2 text-gray-300">{a.message}</span>
|
||||
</p>
|
||||
))}
|
||||
<div ref={messagesEndRef} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
|
@ -21,6 +21,18 @@ export function baseUrl() {
|
|||
return baseUrl
|
||||
}
|
||||
|
||||
// get sseBaseUrl for SSE
|
||||
export function sseBaseUrl() {
|
||||
let {origin} = window.location
|
||||
|
||||
let env = process.env.NODE_ENV
|
||||
if (env === "development") {
|
||||
return `http://localhost:8989/`
|
||||
}
|
||||
|
||||
return `${origin}${baseUrl()}`
|
||||
}
|
||||
|
||||
export function buildPath(...args: string[]): string {
|
||||
const [first] = args;
|
||||
const firstTrimmed = first.trim();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue