mirror of
https://github.com/idanoo/autobrr
synced 2025-07-23 08:49:13 +00:00
feat(settings): make log level configurable from UI (#704)
* feat(settings): set log level * fix: light theme colors * fix: light theme colors size unit
This commit is contained in:
parent
8cb4a0244c
commit
ac276868fb
11 changed files with 310 additions and 30 deletions
|
@ -281,7 +281,9 @@ func (c *AppConfig) UpdateConfig() error {
|
||||||
func (c *AppConfig) processLines(lines []string) []string {
|
func (c *AppConfig) processLines(lines []string) []string {
|
||||||
// keep track of not found values to append at bottom
|
// keep track of not found values to append at bottom
|
||||||
var (
|
var (
|
||||||
foundLineUpdate = false
|
foundLineUpdate = false
|
||||||
|
foundLineLogLevel = false
|
||||||
|
foundLineLogPath = false
|
||||||
)
|
)
|
||||||
|
|
||||||
for i, line := range lines {
|
for i, line := range lines {
|
||||||
|
@ -290,6 +292,18 @@ func (c *AppConfig) processLines(lines []string) []string {
|
||||||
lines[i] = fmt.Sprintf("checkForUpdates = %t", c.Config.CheckForUpdates)
|
lines[i] = fmt.Sprintf("checkForUpdates = %t", c.Config.CheckForUpdates)
|
||||||
foundLineUpdate = true
|
foundLineUpdate = true
|
||||||
}
|
}
|
||||||
|
if !foundLineLogLevel && strings.Contains(line, "logLevel =") {
|
||||||
|
lines[i] = fmt.Sprintf(`logLevel = "%s"`, c.Config.LogLevel)
|
||||||
|
foundLineLogLevel = true
|
||||||
|
}
|
||||||
|
if !foundLineLogPath && strings.Contains(line, "logPath =") {
|
||||||
|
if c.Config.LogPath == "" {
|
||||||
|
lines[i] = `#logPath = ""`
|
||||||
|
} else {
|
||||||
|
lines[i] = fmt.Sprintf("logPath = \"%s\"", c.Config.LogPath)
|
||||||
|
}
|
||||||
|
foundLineLogPath = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// append missing vars to bottom
|
// append missing vars to bottom
|
||||||
|
@ -299,5 +313,27 @@ func (c *AppConfig) processLines(lines []string) []string {
|
||||||
lines = append(lines, fmt.Sprintf("checkForUpdates = %t", c.Config.CheckForUpdates))
|
lines = append(lines, fmt.Sprintf("checkForUpdates = %t", c.Config.CheckForUpdates))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !foundLineLogLevel {
|
||||||
|
lines = append(lines, "# Log level")
|
||||||
|
lines = append(lines, "#")
|
||||||
|
lines = append(lines, `# Default: "DEBUG"`)
|
||||||
|
lines = append(lines, "#")
|
||||||
|
lines = append(lines, `# Options: "ERROR", "DEBUG", "INFO", "WARN", "TRACE"`)
|
||||||
|
lines = append(lines, "#")
|
||||||
|
lines = append(lines, fmt.Sprintf(`logLevel = "%s"`, c.Config.LogLevel))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !foundLineLogPath {
|
||||||
|
lines = append(lines, "# Log Path")
|
||||||
|
lines = append(lines, "#")
|
||||||
|
lines = append(lines, "# Optional")
|
||||||
|
lines = append(lines, "#")
|
||||||
|
if c.Config.LogPath == "" {
|
||||||
|
lines = append(lines, `#logPath = ""`)
|
||||||
|
} else {
|
||||||
|
lines = append(lines, fmt.Sprintf(`logPath = "%s"`, c.Config.LogPath))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return lines
|
return lines
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/autobrr/autobrr/internal/domain"
|
"github.com/autobrr/autobrr/internal/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,20 +26,20 @@ func TestAppConfig_processLines(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "append missing",
|
name: "append missing",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
Config: &domain.Config{CheckForUpdates: true},
|
Config: &domain.Config{CheckForUpdates: true, LogLevel: "TRACE"},
|
||||||
m: sync.Mutex{},
|
m: sync.Mutex{},
|
||||||
},
|
},
|
||||||
args: args{[]string{}},
|
args: args{[]string{}},
|
||||||
want: []string{"# Check for updates", "#", "checkForUpdates = true"},
|
want: []string{"# Check for updates", "#", "checkForUpdates = true", "# Log level", "#", "# Default: \"DEBUG\"", "#", "# Options: \"ERROR\", \"DEBUG\", \"INFO\", \"WARN\", \"TRACE\"", "#", `logLevel = "TRACE"`, "# Log Path", "#", "# Optional", "#", "#logPath = \"\""},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "update existing",
|
name: "update existing",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
Config: &domain.Config{CheckForUpdates: true},
|
Config: &domain.Config{CheckForUpdates: true, LogLevel: "TRACE"},
|
||||||
m: sync.Mutex{},
|
m: sync.Mutex{},
|
||||||
},
|
},
|
||||||
args: args{[]string{"# Check for updates", "#", "#checkForUpdates = false"}},
|
args: args{[]string{"# Check for updates", "#", "checkForUpdates = false", "# Log level", "#", "# Default: \"DEBUG\"", "#", "# Options: \"ERROR\", \"DEBUG\", \"INFO\", \"WARN\", \"TRACE\"", "#", `logLevel = "TRACE"`, "# Log Path", "#", "# Optional", "#", "#logPath = \"\""}},
|
||||||
want: []string{"# Check for updates", "#", "checkForUpdates = true"},
|
want: []string{"# Check for updates", "#", "checkForUpdates = true", "# Log level", "#", "# Default: \"DEBUG\"", "#", "# Options: \"ERROR\", \"DEBUG\", \"INFO\", \"WARN\", \"TRACE\"", "#", `logLevel = "TRACE"`, "# Log Path", "#", "# Optional", "#", "#logPath = \"\""},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
@ -47,9 +48,8 @@ func TestAppConfig_processLines(t *testing.T) {
|
||||||
Config: tt.fields.Config,
|
Config: tt.fields.Config,
|
||||||
m: tt.fields.m,
|
m: tt.fields.m,
|
||||||
}
|
}
|
||||||
if got := c.processLines(tt.args.lines); !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("processLines() = %v, want %v", got, tt.want)
|
assert.Equalf(t, tt.want, c.processLines(tt.args.lines), tt.name)
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ type configJson struct {
|
||||||
Port int `json:"port"`
|
Port int `json:"port"`
|
||||||
LogLevel string `json:"log_level"`
|
LogLevel string `json:"log_level"`
|
||||||
LogPath string `json:"log_path"`
|
LogPath string `json:"log_path"`
|
||||||
|
LogMaxSize int `json:"log_max_size"`
|
||||||
|
LogMaxBackups int `json:"log_max_backups"`
|
||||||
BaseURL string `json:"base_url"`
|
BaseURL string `json:"base_url"`
|
||||||
CheckForUpdates bool `json:"check_for_updates"`
|
CheckForUpdates bool `json:"check_for_updates"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
|
@ -49,6 +51,8 @@ func (h configHandler) getConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
Port: h.cfg.Config.Port,
|
Port: h.cfg.Config.Port,
|
||||||
LogLevel: h.cfg.Config.LogLevel,
|
LogLevel: h.cfg.Config.LogLevel,
|
||||||
LogPath: h.cfg.Config.LogPath,
|
LogPath: h.cfg.Config.LogPath,
|
||||||
|
LogMaxSize: h.cfg.Config.LogMaxSize,
|
||||||
|
LogMaxBackups: h.cfg.Config.LogMaxBackups,
|
||||||
BaseURL: h.cfg.Config.BaseURL,
|
BaseURL: h.cfg.Config.BaseURL,
|
||||||
CheckForUpdates: h.cfg.Config.CheckForUpdates,
|
CheckForUpdates: h.cfg.Config.CheckForUpdates,
|
||||||
Version: h.server.version,
|
Version: h.server.version,
|
||||||
|
@ -71,6 +75,14 @@ func (h configHandler) updateConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
h.cfg.Config.CheckForUpdates = *data.CheckForUpdates
|
h.cfg.Config.CheckForUpdates = *data.CheckForUpdates
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if data.LogLevel != nil {
|
||||||
|
h.cfg.Config.LogLevel = *data.LogLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.LogPath != nil {
|
||||||
|
h.cfg.Config.LogPath = *data.LogPath
|
||||||
|
}
|
||||||
|
|
||||||
if err := h.cfg.UpdateConfig(); err != nil {
|
if err := h.cfg.UpdateConfig(); err != nil {
|
||||||
render.Status(r, http.StatusInternalServerError)
|
render.Status(r, http.StatusInternalServerError)
|
||||||
render.JSON(w, r, errorResponse{
|
render.JSON(w, r, errorResponse{
|
||||||
|
|
|
@ -422,6 +422,9 @@ export const DownloadRuleConditionOptions: OptionBasic[] = [
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const logLevel = ["DEBUG", "INFO", "WARN", "ERROR", "TRACE"] as const;
|
||||||
|
|
||||||
|
export const LogLevelOptions = logLevel.map(v => ({ value: v, label: v, key: v }));
|
||||||
|
|
||||||
export interface SelectOption {
|
export interface SelectOption {
|
||||||
label: string;
|
label: string;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { BrowserRouter, Routes, Route } from "react-router-dom";
|
import { BrowserRouter, Route, Routes } from "react-router-dom";
|
||||||
|
|
||||||
import { Login } from "../screens/auth/login";
|
import { Login } from "../screens/auth/login";
|
||||||
import { Logout } from "../screens/auth/logout";
|
import { Logout } from "../screens/auth/logout";
|
||||||
|
@ -9,15 +9,18 @@ import { FilterDetails, Filters } from "../screens/filters";
|
||||||
import { Logs } from "../screens/Logs";
|
import { Logs } from "../screens/Logs";
|
||||||
import { Releases } from "../screens/releases";
|
import { Releases } from "../screens/releases";
|
||||||
import Settings from "../screens/Settings";
|
import Settings from "../screens/Settings";
|
||||||
import ApplicationSettings from "../screens/settings/Application";
|
import {
|
||||||
import DownloadClientSettings from "../screens/settings/DownloadClient";
|
APISettings,
|
||||||
import FeedSettings from "../screens/settings/Feed";
|
ApplicationSettings,
|
||||||
import IndexerSettings from "../screens/settings/Indexer";
|
DownloadClientSettings,
|
||||||
import { IrcSettings } from "../screens/settings/Irc";
|
FeedSettings,
|
||||||
import NotificationSettings from "../screens/settings/Notifications";
|
IndexerSettings,
|
||||||
|
IrcSettings,
|
||||||
|
LogSettings,
|
||||||
|
NotificationSettings,
|
||||||
|
ReleaseSettings
|
||||||
|
} from "../screens/settings";
|
||||||
import { RegexPlayground } from "../screens/settings/RegexPlayground";
|
import { RegexPlayground } from "../screens/settings/RegexPlayground";
|
||||||
import ReleaseSettings from "../screens/settings/Releases";
|
|
||||||
import APISettings from "../screens/settings/Api";
|
|
||||||
|
|
||||||
import { baseUrl } from "../utils";
|
import { baseUrl } from "../utils";
|
||||||
|
|
||||||
|
@ -36,6 +39,7 @@ export const LocalRouter = ({ isLoggedIn }: { isLoggedIn: boolean }) => (
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="settings" element={<Settings />}>
|
<Route path="settings" element={<Settings />}>
|
||||||
<Route index element={<ApplicationSettings />} />
|
<Route index element={<ApplicationSettings />} />
|
||||||
|
<Route path="logs" element={<LogSettings />} />
|
||||||
<Route path="api-keys" element={<APISettings />} />
|
<Route path="api-keys" element={<APISettings />} />
|
||||||
<Route path="indexers" element={<IndexerSettings />} />
|
<Route path="indexers" element={<IndexerSettings />} />
|
||||||
<Route path="feeds" element={<FeedSettings />} />
|
<Route path="feeds" element={<FeedSettings />} />
|
||||||
|
|
|
@ -6,7 +6,8 @@ import {
|
||||||
FolderArrowDownIcon,
|
FolderArrowDownIcon,
|
||||||
KeyIcon,
|
KeyIcon,
|
||||||
RectangleStackIcon,
|
RectangleStackIcon,
|
||||||
RssIcon
|
RssIcon,
|
||||||
|
Square3Stack3DIcon
|
||||||
} from "@heroicons/react/24/outline";
|
} from "@heroicons/react/24/outline";
|
||||||
|
|
||||||
import { classNames } from "../utils";
|
import { classNames } from "../utils";
|
||||||
|
@ -19,6 +20,7 @@ interface NavTabType {
|
||||||
|
|
||||||
const subNavigation: NavTabType[] = [
|
const subNavigation: NavTabType[] = [
|
||||||
{ name: "Application", href: "", icon: CogIcon },
|
{ name: "Application", href: "", icon: CogIcon },
|
||||||
|
{ name: "Logs", href: "logs", icon: Square3Stack3DIcon },
|
||||||
{ name: "Indexers", href: "indexers", icon: KeyIcon },
|
{ name: "Indexers", href: "indexers", icon: KeyIcon },
|
||||||
{ name: "IRC", href: "irc", icon: ChatBubbleLeftRightIcon },
|
{ name: "IRC", href: "irc", icon: ChatBubbleLeftRightIcon },
|
||||||
{ name: "Feeds", href: "feeds", icon: RssIcon },
|
{ name: "Feeds", href: "feeds", icon: RssIcon },
|
||||||
|
|
|
@ -11,18 +11,37 @@ interface RowItemProps {
|
||||||
label: string;
|
label: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
|
emptyText?: string;
|
||||||
newUpdate?: GithubRelease;
|
newUpdate?: GithubRelease;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RowItem = ({ label, value, title }: RowItemProps) => {
|
const RowItem = ({ label, value, title, emptyText }: RowItemProps) => {
|
||||||
if (!value)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="py-4 sm:py-5 sm:grid sm:grid-cols-4 sm:gap-4 sm:px-6">
|
<div className="py-4 sm:py-5 sm:grid sm:grid-cols-4 sm:gap-4 sm:px-6">
|
||||||
<dt className="font-medium text-gray-500 dark:text-white" title={title}>{label}:</dt>
|
<dt className="font-medium text-gray-500 dark:text-white" title={title}>{label}:</dt>
|
||||||
<dd className="mt-1 text-gray-900 dark:text-white sm:mt-0 sm:col-span-2 break-all">
|
<dd className="mt-1 text-gray-900 dark:text-white sm:mt-0 sm:col-span-2 break-all">
|
||||||
{value}
|
{value ? value : emptyText}
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface RowItemNumberProps {
|
||||||
|
label: string;
|
||||||
|
value?: string | number;
|
||||||
|
title?: string;
|
||||||
|
unit?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RowItemNumber = ({ label, value, title, unit }: RowItemNumberProps) => {
|
||||||
|
return (
|
||||||
|
<div className="py-4 sm:py-5 sm:grid sm:grid-cols-4 sm:gap-4 sm:px-6">
|
||||||
|
<dt className="font-medium text-gray-500 dark:text-white" title={title}>{label}:</dt>
|
||||||
|
<dd className="mt-1 text-gray-900 dark:text-white sm:mt-0 sm:col-span-2 break-all">
|
||||||
|
<span className="px-1 py-0.5 bg-gray-700 rounded shadow">{value}</span>
|
||||||
|
{unit &&
|
||||||
|
<span className="ml-1 text-sm text-gray-800 dark:text-gray-400">{unit}</span>
|
||||||
|
}
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -157,8 +176,6 @@ function ApplicationSettings() {
|
||||||
<RowItemVersion label="Version" value={data?.version} newUpdate={updateData ?? undefined} />
|
<RowItemVersion label="Version" value={data?.version} newUpdate={updateData ?? undefined} />
|
||||||
<RowItem label="Commit" value={data?.commit} />
|
<RowItem label="Commit" value={data?.commit} />
|
||||||
<RowItem label="Build date" value={data?.date} />
|
<RowItem label="Build date" value={data?.date} />
|
||||||
<RowItem label="Log path" value={data?.log_path} title="Set in config.toml" />
|
|
||||||
<RowItem label="Log level" value={data?.log_level} title="Set in config.toml" />
|
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
<ul className="divide-y divide-gray-200 dark:divide-gray-700">
|
<ul className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||||
|
@ -196,6 +213,7 @@ function ApplicationSettings() {
|
||||||
</div>
|
</div>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ import {
|
||||||
TrashIcon
|
TrashIcon
|
||||||
} from "@heroicons/react/24/outline";
|
} from "@heroicons/react/24/outline";
|
||||||
|
|
||||||
export const IrcSettings = () => {
|
const IrcSettings = () => {
|
||||||
const [expandNetworks, toggleExpand] = useToggle(false);
|
const [expandNetworks, toggleExpand] = useToggle(false);
|
||||||
const [addNetworkIsOpen, toggleAddNetwork] = useToggle(false);
|
const [addNetworkIsOpen, toggleAddNetwork] = useToggle(false);
|
||||||
|
|
||||||
|
@ -490,3 +490,5 @@ const ListItemDropdown = ({
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default IrcSettings;
|
||||||
|
|
190
web/src/screens/settings/Logs.tsx
Normal file
190
web/src/screens/settings/Logs.tsx
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
import { useMutation, useQuery } from "react-query";
|
||||||
|
import { APIClient } from "../../api/APIClient";
|
||||||
|
import { GithubRelease } from "../../types/Update";
|
||||||
|
import { toast } from "react-hot-toast";
|
||||||
|
import Toast from "../../components/notifications/Toast";
|
||||||
|
import { queryClient } from "../../App";
|
||||||
|
import Select, { components, ControlProps, InputProps, MenuProps, OptionProps } from "react-select";
|
||||||
|
import { LogLevelOptions, SelectOption } from "../../domain/constants";
|
||||||
|
|
||||||
|
interface RowItemProps {
|
||||||
|
label: string;
|
||||||
|
value?: string;
|
||||||
|
title?: string;
|
||||||
|
emptyText?: string;
|
||||||
|
newUpdate?: GithubRelease;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RowItem = ({ label, value, title, emptyText }: RowItemProps) => {
|
||||||
|
return (
|
||||||
|
<div className="py-4 sm:py-5 sm:grid sm:grid-cols-4 sm:gap-4 sm:px-6">
|
||||||
|
<dt className="font-medium text-gray-500 dark:text-white" title={title}>{label}:</dt>
|
||||||
|
<dd className="mt-1 text-gray-900 dark:text-white sm:mt-0 sm:col-span-2 break-all">
|
||||||
|
{value ? value : emptyText}
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface RowItemNumberProps {
|
||||||
|
label: string;
|
||||||
|
value?: string | number;
|
||||||
|
title?: string;
|
||||||
|
unit?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RowItemNumber = ({ label, value, title, unit }: RowItemNumberProps) => {
|
||||||
|
return (
|
||||||
|
<div className="py-4 sm:py-5 sm:grid sm:grid-cols-4 sm:gap-4 sm:px-6">
|
||||||
|
<dt className="font-medium text-gray-500 dark:text-white" title={title}>{label}:</dt>
|
||||||
|
<dd className="mt-1 text-gray-900 dark:text-white sm:mt-0 sm:col-span-2 break-all">
|
||||||
|
<span className="px-1 py-0.5 bg-gray-200 dark:bg-gray-700 rounded shadow">{value}</span>
|
||||||
|
{unit &&
|
||||||
|
<span className="ml-1 text-sm text-gray-700 dark:text-gray-400">{unit}</span>
|
||||||
|
}
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Input = (props: InputProps) => {
|
||||||
|
return (
|
||||||
|
<components.Input
|
||||||
|
{...props}
|
||||||
|
inputClassName="outline-none border-none shadow-none focus:ring-transparent"
|
||||||
|
className="text-gray-400 dark:text-gray-100"
|
||||||
|
children={props.children}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Control = (props: ControlProps) => {
|
||||||
|
return (
|
||||||
|
<components.Control
|
||||||
|
{...props}
|
||||||
|
className="p-1 block w-full dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:text-gray-100 sm:text-sm"
|
||||||
|
children={props.children}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Menu = (props: MenuProps) => {
|
||||||
|
return (
|
||||||
|
<components.Menu
|
||||||
|
{...props}
|
||||||
|
className="dark:bg-gray-800 border border-gray-300 dark:border-gray-700 dark:text-gray-400 rounded-md shadow-sm"
|
||||||
|
children={props.children}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Option = (props: OptionProps) => {
|
||||||
|
return (
|
||||||
|
<components.Option
|
||||||
|
{...props}
|
||||||
|
className="dark:text-gray-400 dark:bg-gray-800 dark:hover:bg-gray-900 dark:focus:bg-gray-900"
|
||||||
|
children={props.children}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const RowItemSelect = ({ id, title, label, value, options, onChange }: any) => {
|
||||||
|
return (
|
||||||
|
<div className="py-4 sm:py-5 sm:grid sm:grid-cols-4 sm:gap-4 sm:px-6">
|
||||||
|
<dt className="font-medium text-gray-500 dark:text-white" title={title}>{label}:</dt>
|
||||||
|
<dd className="mt-1 text-gray-900 dark:text-white sm:mt-0 sm:col-span-2 break-all">
|
||||||
|
<Select
|
||||||
|
id={id}
|
||||||
|
components={{ Input, Control, Menu, Option }}
|
||||||
|
placeholder="Choose a type"
|
||||||
|
styles={{
|
||||||
|
singleValue: (base) => ({
|
||||||
|
...base,
|
||||||
|
color: "unset"
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
theme={(theme) => ({
|
||||||
|
...theme,
|
||||||
|
spacing: {
|
||||||
|
...theme.spacing,
|
||||||
|
controlHeight: 30,
|
||||||
|
baseUnit: 2
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
value={value && options.find((o: any) => o.value == value)}
|
||||||
|
onChange={onChange}
|
||||||
|
options={options}
|
||||||
|
/>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
function LogSettings() {
|
||||||
|
const { isLoading, data } = useQuery(
|
||||||
|
["config"],
|
||||||
|
() => APIClient.config.get(),
|
||||||
|
{
|
||||||
|
retry: false,
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
onError: err => console.log(err)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const setLogLevelUpdateMutation = useMutation(
|
||||||
|
(value: string) => APIClient.config.update({ log_level: value }),
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.custom((t) => <Toast type="success" body={"Config successfully updated!"} t={t}/>);
|
||||||
|
|
||||||
|
queryClient.invalidateQueries(["config"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="divide-y divide-gray-200 dark:divide-gray-700 lg:col-span-9">
|
||||||
|
<div className="py-6 px-4 sm:p-6 lg:pb-8">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-lg leading-6 font-medium text-gray-900 dark:text-white">Logs</h2>
|
||||||
|
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
Set level, size etc.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||||
|
<div className="px-4 py-5 sm:p-0">
|
||||||
|
<form className="divide-y divide-gray-200 dark:divide-gray-700 lg:col-span-9" action="#" method="POST">
|
||||||
|
{!isLoading && data && (
|
||||||
|
<dl className="sm:divide-y divide-gray-200 dark:divide-gray-700">
|
||||||
|
<RowItem label="Path" value={data?.log_path} title="Set in config.toml" emptyText="Not set!" />
|
||||||
|
<RowItemSelect id="log_level" label="Level" value={data?.log_level} title="Log level" options={LogLevelOptions} onChange={(value: SelectOption) => setLogLevelUpdateMutation.mutate(value.value)} />
|
||||||
|
<RowItemNumber label="Max Size" value={data?.log_max_size} title="Set in config.toml" unit="MB" />
|
||||||
|
<RowItemNumber label="Max Backups" value={data?.log_max_backups} title="Set in config.toml" />
|
||||||
|
</dl>
|
||||||
|
)}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/*<div className="mt-4 flex justify-end py-4 px-4 sm:px-6">*/}
|
||||||
|
{/* <button*/}
|
||||||
|
{/* type="button"*/}
|
||||||
|
{/* className="inline-flex justify-center rounded-md border border-gray-300 bg-white py-2 px-4 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"*/}
|
||||||
|
{/* >*/}
|
||||||
|
{/* Cancel*/}
|
||||||
|
{/* </button>*/}
|
||||||
|
{/* <button*/}
|
||||||
|
{/* type="submit"*/}
|
||||||
|
{/* className="ml-5 inline-flex justify-center rounded-md border border-transparent bg-blue-700 py-2 px-4 text-sm font-medium text-white shadow-sm hover:bg-blue-800 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"*/}
|
||||||
|
{/* >*/}
|
||||||
|
{/* Save*/}
|
||||||
|
{/* </button>*/}
|
||||||
|
{/*</div>*/}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LogSettings;
|
9
web/src/screens/settings/index.ts
Normal file
9
web/src/screens/settings/index.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
export { default as APISettings } from "./Api";
|
||||||
|
export { default as ApplicationSettings } from "./Application";
|
||||||
|
export { default as DownloadClientSettings } from "./DownloadClient";
|
||||||
|
export { default as FeedSettings } from "./Feed";
|
||||||
|
export { default as IndexerSettings } from "./Indexer";
|
||||||
|
export { default as IrcSettings } from "./Irc";
|
||||||
|
export { default as LogSettings } from "./Logs";
|
||||||
|
export { default as NotificationSettings } from "./Notifications";
|
||||||
|
export { default as ReleaseSettings } from "./Releases";
|
8
web/src/types/Config.d.ts
vendored
8
web/src/types/Config.d.ts
vendored
|
@ -1,8 +1,12 @@
|
||||||
|
type LogLevel = "DEBUG" | "INFO" | "WARN" | "ERROR" | "TRACE";
|
||||||
|
|
||||||
interface Config {
|
interface Config {
|
||||||
host: string;
|
host: string;
|
||||||
port: number;
|
port: number;
|
||||||
log_level: string;
|
log_level: LogLevel;
|
||||||
log_path: string;
|
log_path: string;
|
||||||
|
log_max_size: number;
|
||||||
|
log_max_backups: number;
|
||||||
base_url: string;
|
base_url: string;
|
||||||
check_for_updates: boolean;
|
check_for_updates: boolean;
|
||||||
version: string;
|
version: string;
|
||||||
|
@ -16,5 +20,5 @@ interface ConfigUpdate {
|
||||||
log_level?: string;
|
log_level?: string;
|
||||||
log_path?: string;
|
log_path?: string;
|
||||||
base_url?: string;
|
base_url?: string;
|
||||||
check_for_updates: boolean;
|
check_for_updates?: boolean;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue