login i18n & chart loading & fix forgot pass page (#236)

Co-authored-by: hank <hank@henrygd.me>
This commit is contained in:
ArsFy
2024-10-30 11:22:03 +08:00
committed by GitHub
parent 062796b38c
commit 180ec83a17
16 changed files with 369 additions and 205 deletions

Binary file not shown.

View File

@@ -32,7 +32,6 @@
"cmdk": "^1.0.0",
"d3-time": "^3.1.0",
"i18next": "^23.16.4",
"i18next-browser-languagedetector": "^8.0.0",
"lucide-react": "^0.452.0",
"nanostores": "^0.11.3",
"pocketbase": "^0.21.5",

View File

@@ -16,6 +16,7 @@ import {
import { useCallback, useState } from 'react'
import { AuthMethodsList, OAuth2AuthConfig } from 'pocketbase'
import { Link } from '../router'
import { useTranslation } from 'react-i18next'
const honeypot = v.literal('')
const emailSchema = v.pipe(v.string(), v.email('Invalid email address.'))
@@ -63,6 +64,8 @@ export function UserAuthForm({
isFirstRun: boolean
authMethods: AuthMethodsList
}) {
const { t } = useTranslation()
const [isLoading, setIsLoading] = useState<boolean>(false)
const [isOauthLoading, setIsOauthLoading] = useState<boolean>(false)
const [errors, setErrors] = useState<Record<string, string | undefined>>({})
@@ -224,7 +227,7 @@ export function UserAuthForm({
) : (
<LogInIcon className="mr-2 h-4 w-4" />
)}
{isFirstRun ? 'Create account' : 'Sign in'}
{isFirstRun ? t('auth.create_account') : t('auth.sign_in')}
</button>
</div>
</form>
@@ -317,16 +320,16 @@ export function UserAuthForm({
<DialogTitle>OAuth 2 / OIDC support</DialogTitle>
</DialogHeader>
<div className="text-primary/70 text-[0.95em] contents">
<p>Beszel supports OpenID Connect and many OAuth2 authentication providers.</p>
<p>{t('auth.openid_des')}</p>
<p>
Please view the{' '}
{t('please_view_the')}{' '}
<a
href="https://github.com/henrygd/beszel/blob/main/readme.md#oauth--oidc-integration"
className={cn(buttonVariants({ variant: 'link' }), 'p-0 h-auto')}
>
GitHub README
</a>{' '}
for instructions.
{t('for_instructions')}
</p>
</div>
</DialogContent>
@@ -338,7 +341,7 @@ export function UserAuthForm({
href="/forgot-password"
className="text-sm mx-auto hover:text-brand underline underline-offset-4 opacity-70 hover:opacity-100 transition-opacity"
>
Forgot password?
{t('auth.forgot_password')}
</Link>
)}
</div>

View File

@@ -8,6 +8,7 @@ import { cn } from '@/lib/utils'
import { pb } from '@/lib/stores'
import { Dialog, DialogHeader } from '../ui/dialog'
import { DialogContent, DialogTrigger, DialogTitle } from '../ui/dialog'
import { useTranslation } from 'react-i18next'
const showLoginFaliedToast = () => {
toast({
@@ -18,6 +19,7 @@ const showLoginFaliedToast = () => {
}
export default function ForgotPassword() {
const { t } = useTranslation()
const [isLoading, setIsLoading] = useState<boolean>(false)
const [email, setEmail] = useState('')
@@ -72,26 +74,25 @@ export default function ForgotPassword() {
) : (
<SendHorizonalIcon className="mr-2 h-4 w-4" />
)}
Reset password
{t('auth.reset_password')}
</button>
</div>
</form>
<Dialog>
<DialogTrigger asChild>
<button className="text-sm mx-auto hover:text-brand underline underline-offset-4 opacity-70 hover:opacity-100 transition-opacity">
Command line instructions
{t('auth.command_line_instructions')}
</button>
</DialogTrigger>
<DialogContent className="max-w-[33em]">
<DialogHeader>
<DialogTitle>Command line instructions</DialogTitle>
<DialogTitle>{t('auth.command_line_instructions')}</DialogTitle>
</DialogHeader>
<p className="text-primary/70 text-[0.95em] leading-relaxed">
If you've lost the password to your admin account, you may reset it using the following
command.
{t('auth.command_1')}
</p>
<p className="text-primary/70 text-[0.95em] leading-relaxed">
Then log into the backend and reset your user account password in the users table.
{t('auth.command_2')}
</p>
<code className="bg-muted rounded-sm py-0.5 px-2.5 mr-auto text-sm">
beszel admin update youremail@example.com newpassword

View File

@@ -6,8 +6,11 @@ import { useStore } from '@nanostores/react'
import ForgotPassword from './forgot-pass-form'
import { $router } from '../router'
import { AuthMethodsList } from 'pocketbase'
import { useTranslation } from 'react-i18next'
export default function () {
const { t } = useTranslation()
const page = useStore($router)
const [isFirstRun, setFirstRun] = useState(false)
const [authMethods, setAuthMethods] = useState<AuthMethodsList>()
@@ -30,11 +33,11 @@ export default function () {
const subtitle = useMemo(() => {
if (isFirstRun) {
return 'Please create an admin account'
return t('auth.create')
} else if (page?.path === '/forgot-password') {
return 'Enter email address to reset password'
return t('auth.reset')
} else {
return 'Please sign in to your account'
return t('auth.login')
}
}, [isFirstRun, page])

View File

@@ -5,6 +5,7 @@ export const $router = createRouter(
home: '/',
server: '/system/:name',
settings: '/settings/:name?',
forgot_password: '/forgot-password',
},
{ links: false }
)

View File

@@ -112,6 +112,7 @@ export default function SystemDetail({ name }: { name: string }) {
const netCardRef = useRef<HTMLDivElement>(null)
const [containerFilterBar, setContainerFilterBar] = useState(null as null | JSX.Element)
const [bottomSpacing, setBottomSpacing] = useState(0)
const [chartLoading, setChartLoading] = useState(false)
const isLongerChart = chartTime !== '1h'
useEffect(() => {
@@ -178,10 +179,15 @@ export default function SystemDetail({ name }: { name: string }) {
if (!system.id || !chartTime) {
return
}
// loading: true
setChartLoading(true)
Promise.allSettled([
getStats<SystemStatsRecord>('system_stats', system, chartTime),
getStats<ContainerStatsRecord>('container_stats', system, chartTime),
]).then(([systemStats, containerStats]) => {
// loading: false
setChartLoading(false)
const { expectedInterval } = chartTimeData[chartTime]
// make new system stats
const ss_cache_key = `${system.id}_${chartTime}_system_stats`
@@ -291,6 +297,9 @@ export default function SystemDetail({ name }: { name: string }) {
return null
}
// if no data, show empty state
const dataEmpty = !chartLoading && chartData.systemStats.length === 0;
return (
<>
<div id="chartwrap" className="grid gap-4 mb-10 overflow-x-clip">
@@ -375,6 +384,7 @@ export default function SystemDetail({ name }: { name: string }) {
{/* main charts */}
<div className="grid lg:grid-cols-2 gap-4">
<ChartCard
empty={dataEmpty}
grid={grid}
title={t('monitor.total_cpu_usage')}
description={`${cpuMaxStore[0] && isLongerChart ? t('monitor.max_1_min') : t('monitor.average') } ${t('monitor.cpu_des')}`}
@@ -390,6 +400,7 @@ export default function SystemDetail({ name }: { name: string }) {
{containerFilterBar && (
<ChartCard
empty={dataEmpty}
grid={grid}
title={t('monitor.docker_cpu_usage')}
description={t('monitor.docker_cpu_des')}
@@ -400,6 +411,7 @@ export default function SystemDetail({ name }: { name: string }) {
)}
<ChartCard
empty={dataEmpty}
grid={grid}
title={t('monitor.total_memory_usage')}
description={t('monitor.memory_des')}
@@ -409,6 +421,7 @@ export default function SystemDetail({ name }: { name: string }) {
{containerFilterBar && (
<ChartCard
empty={dataEmpty}
grid={grid}
title={t('monitor.docker_memory_usage')}
description={t('monitor.docker_memory_des')}
@@ -418,7 +431,7 @@ export default function SystemDetail({ name }: { name: string }) {
</ChartCard>
)}
<ChartCard grid={grid} title={t('monitor.disk_space')} description={t('monitor.disk_des')}>
<ChartCard empty={dataEmpty} grid={grid} title={t('monitor.disk_space')} description={t('monitor.disk_des')}>
<DiskChart
chartData={chartData}
dataKey="stats.du"
@@ -427,6 +440,7 @@ export default function SystemDetail({ name }: { name: string }) {
</ChartCard>
<ChartCard
empty={dataEmpty}
grid={grid}
title={t('monitor.disk_io')}
description={t('monitor.disk_io_des')}
@@ -440,6 +454,7 @@ export default function SystemDetail({ name }: { name: string }) {
</ChartCard>
<ChartCard
empty={dataEmpty}
grid={grid}
title={t('monitor.bandwidth')}
cornerEl={isLongerChart ? <SelectAvgMax store={bandwidthMaxStore} /> : null}
@@ -460,6 +475,7 @@ export default function SystemDetail({ name }: { name: string }) {
})}
>
<ChartCard
empty={dataEmpty}
title={t('monitor.docker_network_io')}
description={t('monitor.docker_network_io_des')}
cornerEl={containerFilterBar}
@@ -471,13 +487,13 @@ export default function SystemDetail({ name }: { name: string }) {
)}
{(systemStats.at(-1)?.stats.su ?? 0) > 0 && (
<ChartCard grid={grid} title={t('monitor.swap_usage')} description={t('monitor.swap_des')}>
<ChartCard empty={dataEmpty} grid={grid} title={t('monitor.swap_usage')} description={t('monitor.swap_des')}>
<SwapChart chartData={chartData} />
</ChartCard>
)}
{systemStats.at(-1)?.stats.t && (
<ChartCard grid={grid} title={t('monitor.temperature')} description={t('monitor.temperature_des')}>
<ChartCard empty={dataEmpty} grid={grid} title={t('monitor.temperature')} description={t('monitor.temperature_des')}>
<TemperatureChart chartData={chartData} />
</ChartCard>
)}
@@ -490,6 +506,7 @@ export default function SystemDetail({ name }: { name: string }) {
return (
<div key={extraFsName} className="contents">
<ChartCard
empty={dataEmpty}
grid={grid}
title={`${extraFsName} ${t('monitor.usage')}`}
description={`${t('monitor.disk_usage_of')} ${extraFsName}`}
@@ -501,6 +518,7 @@ export default function SystemDetail({ name }: { name: string }) {
/>
</ChartCard>
<ChartCard
empty={dataEmpty}
grid={grid}
title={`${extraFsName} I/O`}
description={`${t('monitor.throughput_of')} ${extraFsName}`}
@@ -591,12 +609,14 @@ function ChartCard({
description,
children,
grid,
empty,
cornerEl,
}: {
title: string
description: string
children: React.ReactNode
grid?: boolean
empty?: boolean,
cornerEl?: JSX.Element | null
}) {
const { isIntersecting, ref } = useIntersectionObserver()
@@ -616,7 +636,7 @@ function ChartCard({
)}
</CardHeader>
<div className="pl-0 w-[calc(100%-1.6em)] h-52 relative">
{<Spinner />}
{<Spinner empty={empty} />}
{isIntersecting && children}
</div>
</Card>

View File

@@ -1,9 +1,12 @@
import { LoaderCircleIcon } from 'lucide-react'
import { useTranslation } from 'react-i18next'
export default function () {
export default function (props: { empty?: boolean }) {
const { t } = useTranslation()
return (
<div className="grid place-content-center h-full absolute inset-0">
<div className="flex flex-col items-center justify-center h-full absolute inset-0">
<LoaderCircleIcon className="animate-spin h-10 w-10 opacity-60" />
{props.empty && <p className={'opacity-60 mt-2'}>{t('monitor.waiting_for')}</p>}
</div>
)
}

View File

@@ -1,6 +1,5 @@
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import en from '../locales/en/translation.json';
import es from '../locales/es/translation.json';
@@ -10,8 +9,32 @@ import ru from '../locales/ru/translation.json';
import zhHans from '../locales/zh-CN/translation.json';
import zhHant from '../locales/zh-HK/translation.json';
// Custom language detector to use localStorage
const languageDetector: any = {
type: 'languageDetector',
async: true,
detect: (callback: (lng: string) => void) => {
const savedLanguage = localStorage.getItem('i18nextLng');
const fallbackLanguage = (()=>{
switch (navigator.language) {
case 'zh-CN': case 'zh-SG': case 'zh-MY': case 'zh': case 'zh-Hans':
return 'zh-CN';
case 'zh-HK': case 'zh-TW': case 'zh-MO': case 'zh-Hant':
return 'zh-HK';
default:
return navigator.language;
}
})();
callback(savedLanguage || fallbackLanguage);
},
init: () => {},
cacheUserLanguage: (lng: string) => {
localStorage.setItem('i18nextLng', lng);
}
};
i18n
.use(LanguageDetector)
.use(languageDetector)
.use(initReactI18next)
.init({
resources: {

View File

@@ -173,6 +173,22 @@
"temperature_des": "Temperaturen der System-Sensoren",
"usage": "Nutzung",
"disk_usage_of": "Festplattennutzung von",
"throughput_of": "Durchsatz von"
"throughput_of": "Durchsatz von",
"waiting_for": "Warten auf genügend Datensätze zur Anzeige"
},
"auth": {
"login": "Bitte melden Sie sich bei Ihrem Konto an",
"reset": "E-Mail-Adresse eingeben, um das Passwort zurückzusetzen",
"create": "Bitte erstellen Sie ein Administratorkonto",
"create_account": "Konto erstellen",
"sign_in": "Anmelden",
"openid_des": "Beszel unterstützt OpenID Connect und viele OAuth2-Authentifizierungsanbieter.",
"please_view_the": "Bitte sehen Sie sich die",
"for_instructions": "für Anweisungen an.",
"forgot_password": "Passwort vergessen?",
"reset_password": "Passwort zurücksetzen",
"command_line_instructions": "Befehlszeilenanweisungen",
"command_1": "Wenn Sie das Passwort für Ihr Administratorkonto verloren haben, können Sie es mit dem folgenden Befehl zurücksetzen.",
"command_2": "Melden Sie sich dann im Backend an und setzen Sie das Passwort Ihres Benutzerkontos in der Benutzertabelle zurück."
}
}

View File

@@ -1,178 +1,193 @@
{
"all_systems": "All Systems",
"filter": "Filter...",
"copy": "Copy",
"add": "Add",
"system": "System",
"systems": "Systems",
"cancel": "Cancel",
"continue": "Continue",
"home": {
"active_alerts": "Active Alerts",
"active_des": "Exceeds {{value}}{{unit}} average in last {{minutes}} min",
"subtitle_1": "Updated in real time. Click on a system to view information.",
"subtitle_2": "to open the command palette."
},
"systems_table": {
"system": "System",
"memory": "Memory",
"cpu": "CPU",
"disk": "Disk",
"net": "Net",
"agent": "Agent",
"no_systems_found": "No systems found.",
"open_menu": "Open menu",
"resume": "Resume",
"pause": "Pause",
"copy_host": "Copy host",
"delete": "Delete",
"delete_confirm": "Are you sure you want to delete {{name}}?",
"delete_confirm_des_1": "This action cannot be undone. This will permanently delete all current records for",
"delete_confirm_des_2": "from the database."
},
"alerts": {
"title": "Alerts",
"subtitle_1": "See",
"notification_settings": "notification settings",
"subtitle_2": "to configure how you receive alerts.",
"overwrite_existing_alerts": "Overwrite existing alerts",
"info": {
"status": "Status",
"status_des": "Triggers when status switches between up and down.",
"cpu_usage": "CPU Usage",
"cpu_usage_des": "Triggers when CPU usage exceeds a threshold.",
"memory_usage": "Memory Usage",
"memory_usage_des": "Triggers when memory usage exceeds a threshold.",
"disk_usage": "Disk Usage",
"disk_usage_des": "Triggers when usage of any disk exceeds a threshold.",
"bandwidth": "Bandwidth",
"bandwidth_des": "Triggers when combined up/down exceeds a threshold.",
"temperature": "Temperature",
"temperature_des": "Triggers when any sensor exceeds a threshold."
},
"average_exceeds": "Average exceeds",
"for": "For",
"minute": "minute",
"minutes": "minutes"
},
"settings": {
"settings": "Settings",
"subtitle": "Manage display and notification preferences.",
"save_settings": "Save Settings",
"export_configuration": "Export configuration",
"general": {
"title": "General",
"subtitle": "Change general application options.",
"language": {
"title": "Language",
"subtitle_1": "Want to help us make our translations even better? Check out",
"subtitle_2": "for more details.",
"preferred_language": "Preferred Language"
},
"chart_options": {
"title": "Chart options",
"subtitle": "Adjust display options for charts.",
"default_time_period": "Default time period",
"default_time_period_des": "Sets the default time range for charts when a system is viewed."
}
},
"notifications": {
"title": "Notifications",
"subtitle_1": "Configure how you receive alert notifications.",
"subtitle_2": "Looking instead for where to create alerts? Click the bell",
"subtitle_3": "icons in the systems table.",
"email": {
"title": "Email notifications",
"please": "Please",
"configure_an_SMTP_server": "configure an SMTP server",
"to_ensure_alerts_are_delivered": "to ensure alerts are delivered.",
"to_email_s": "To email(s)",
"enter_email_address": "Enter email address...",
"des": "Save address using enter key or comma. Leave blank to disable email notifications."
},
"webhook_push": {
"title": "Webhook / Push notifications",
"des_1": "Beszel uses",
"des_2": "to integrate with popular notification services.",
"add_url": "Add URL"
}
},
"yaml_config": {
"short_title": "YAML Config",
"title": "YAML Configuration",
"subtitle": "Export your current systems configuration.",
"des_1": "Systems may be managed in a",
"des_2": "file inside your data directory.",
"des_3": "On each restart, systems in the database will be updated to match the systems defined in the file.",
"alert": {
"title": "Caution - potential data loss",
"des_1": "Existing systems not defined in",
"des_2": "will be deleted. Please make regular backups."
}
},
"language": "Language"
},
"user_dm": {
"users": "Users",
"logs": "Logs",
"backups": "Backups",
"auth_providers": "Auth Providers",
"log_out": "Log Out"
},
"themes": {
"toggle_theme": "Toggle theme",
"light": "Light",
"dark": "Dark",
"system": "System"
},
"add_system": {
"add_new_system": "Add New System",
"binary": "Binary",
"dialog_des_1": "The agent must be running on the system to connect. Copy the",
"dialog_des_2": "for the agent below.",
"name": "Name",
"host_ip": "Host / IP",
"port": "Port",
"public_key": "Public Key",
"click_to_copy": "Click to copy",
"command": "command",
"add_system": "Add system"
},
"command": {
"search": "Search for systems or settings...",
"pages_settings": "Pages / Settings",
"dashboard": "Dashboard",
"documentation": "Documentation",
"SMTP_settings": "SMTP settings",
"page": "Page",
"admin": "Admin"
},
"monitor": {
"toggle_grid": "Toggle grid",
"average": "Average",
"max_1_min": "Max 1 min ",
"total_cpu_usage": "Total CPU Usage",
"cpu_des": "system-wide CPU utilization",
"docker_cpu_usage": "Docker CPU Usage",
"docker_cpu_des": "Average CPU utilization of containers",
"total_memory_usage": "Total Memory Usage",
"memory_des": "Precise utilization at the recorded time",
"docker_memory_usage": "Docker Memory Usage",
"docker_memory_des": "Memory usage of docker containers",
"disk_space": "Disk Space",
"disk_des": "Usage of root partition",
"disk_io": "Disk I/O",
"disk_io_des": "Throughput of root filesystem",
"bandwidth": "Bandwidth",
"bandwidth_des": "Network traffic of public interfaces",
"docker_network_io": "Docker Network I/O",
"docker_network_io_des": "Network traffic of docker containers",
"swap_usage": "Swap Usage",
"swap_des": "Swap space used by the system",
"temperature": "Temperature",
"temperature_des": "Temperatures of system sensors",
"usage": "Usage",
"disk_usage_of": "Disk usage of",
"throughput_of": "Throughput of"
}
"all_systems": "All Systems",
"filter": "Filter...",
"copy": "Copy",
"add": "Add",
"system": "System",
"systems": "Systems",
"cancel": "Cancel",
"continue": "Continue",
"home": {
"active_alerts": "Active Alerts",
"active_des": "Exceeds {{value}}{{unit}} average in last {{minutes}} minutes",
"subtitle_1": "Updated in real time. Click on a system to view information.",
},
"systems_table": {
"system": "System",
"memory": "Memory",
"cpu": "CPU",
"disk": "Disk",
"net": "Net",
"agent": "Agent",
"no_systems_found": "No systems found.",
"open_menu": "Open menu",
"resume": "Resume",
"pause": "Pause",
"copy_host": "Copy host",
"delete": "Delete",
"delete_confirm": "Are you sure you want to delete {{name}}?",
"delete_confirm_des_1": "This action cannot be undone. This will permanently delete all current records for",
"delete_confirm_des_2": "from the database."
},
"alerts": {
"title": "Alerts",
"subtitle_1": "See",
"notification_settings": "notification settings",
"subtitle_2": "to configure how you receive alerts.",
"overwrite_existing_alerts": "Overwrite existing alerts",
"info": {
"status": "Status",
"status_des": "Triggers when status switches between up and down.",
"cpu_usage": "CPU Usage",
"cpu_usage_des": "Triggers when CPU usage exceeds a threshold.",
"memory_usage": "Memory Usage",
"memory_usage_des": "Triggers when memory usage exceeds a threshold.",
"disk_usage": "Disk Usage",
"disk_usage_des": "Triggers when usage of any disk exceeds a threshold.",
"bandwidth": "Bandwidth",
"bandwidth_des": "Triggers when combined up/down exceeds a threshold.",
"temperature": "Temperature",
"temperature_des": "Triggers when any sensor exceeds a threshold."
},
"average_exceeds": "Average exceeds",
"for": "For",
"minute": "minute",
"minutes": "minutes"
},
"settings": {
"settings": "Settings",
"subtitle": "Manage display and notification preferences.",
"save_settings": "Save Settings",
"export_configuration": "Export configuration",
"general": {
"title": "General",
"subtitle": "Change general application options.",
"language": {
"title": "Language",
"subtitle_1": "Want to help us make our translations even better? Check out",
"subtitle_2": "for more details.",
"preferred_language": "Preferred Language"
},
"chart_options": {
"title": "Chart options",
"subtitle": "Adjust display options for charts.",
"default_time_period": "Default time period",
"default_time_period_des": "Sets the default time range for charts when a system is viewed."
}
},
"notifications": {
"title": "Notifications",
"subtitle_1": "Configure how you receive alert notifications.",
"subtitle_2": "Looking instead for where to create alerts? Click the bell",
"subtitle_3": "icons in the systems table.",
"email": {
"title": "Email notifications",
"please": "Please",
"configure_an_SMTP_server": "configure an SMTP server",
"to_ensure_alerts_are_delivered": "to ensure alerts are delivered.",
"to_email_s": "To email(s)",
"enter_email_address": "Enter email address...",
"des": "Save address using enter key or comma. Leave blank to disable email notifications."
},
"webhook_push": {
"title": "Webhook / Push notifications",
"des_1": "Beszel uses",
"des_2": "to integrate with popular notification services.",
"add_url": "Add URL"
}
},
"yaml_config": {
"short_title": "YAML Config",
"title": "YAML Configuration",
"subtitle": "Export your current systems configuration.",
"des_1": "Systems may be managed in a",
"des_2": "file inside your data directory.",
"des_3": "On each restart, systems in the database will be updated to match the systems defined in the file.",
"alert": {
"title": "Caution - potential data loss",
"des_1": "Existing systems not defined in",
"des_2": "will be deleted. Please make regular backups."
}
},
"language": "Language"
},
"user_dm": {
"users": "Users",
"logs": "Logs",
"backups": "Backups",
"auth_providers": "Auth Providers",
"log_out": "Log Out"
},
"themes": {
"toggle_theme": "Toggle theme",
"light": "Light",
"dark": "Dark",
"system": "System"
},
"add_system": {
"add_new_system": "Add New System",
"binary": "Binary",
"dialog_des_1": "The agent must be running on the system to connect. Copy the",
"dialog_des_2": "for the agent below.",
"name": "Name",
"host_ip": "Host / IP",
"port": "Port",
"public_key": "Public Key",
"click_to_copy": "Click to copy",
"command": "command",
"add_system": "Add system"
},
"command": {
"search": "Search for systems or settings...",
"pages_settings": "Pages / Settings",
"dashboard": "Dashboard",
"documentation": "Documentation",
"SMTP_settings": "SMTP settings",
"page": "Page",
"admin": "Admin"
},
"monitor": {
"toggle_grid": "Toggle grid",
"average": "Average",
"max_1_min": "Max 1 min ",
"total_cpu_usage": "Total CPU Usage",
"cpu_des": "system-wide CPU utilization",
"docker_cpu_usage": "Docker CPU Usage",
"docker_cpu_des": "Average CPU utilization of containers",
"total_memory_usage": "Total Memory Usage",
"memory_des": "Precise utilization at the recorded time",
"docker_memory_usage": "Docker Memory Usage",
"docker_memory_des": "Memory usage of docker containers",
"disk_space": "Disk Space",
"disk_des": "Usage of root partition",
"disk_io": "Disk I/O",
"disk_io_des": "Throughput of root filesystem",
"bandwidth": "Bandwidth",
"bandwidth_des": "Network traffic of public interfaces",
"docker_network_io": "Docker Network I/O",
"docker_network_io_des": "Network traffic of docker containers",
"swap_usage": "Swap Usage",
"swap_des": "Swap space used by the system",
"temperature": "Temperature",
"temperature_des": "Temperatures of system sensors",
"usage": "Usage",
"disk_usage_of": "Disk usage of",
"throughput_of": "Throughput of",
"waiting_for": "Waiting for enough records to display"
},
"auth": {
"login": "Please sign in to your account",
"reset": "Enter email address to reset password",
"create": "Please create an admin account",
"create_account": "Create account",
"sign_in": "Sign in",
"openid_des": "Beszel supports OpenID Connect and many OAuth2 authentication providers.",
"please_view_the": "Please view the",
"for_instructions": "for instructions.",
"forgot_password": "Forgot password?",
"reset_password": "Reset Password",
"command_line_instructions": "Command line instructions",
"command_1": "If you've lost the password to your admin account, you may reset it using the following command.",
"command_2": "Then log into the backend and reset your user account password in the users table."
}
}

View File

@@ -173,6 +173,22 @@
"temperature_des": "Temperaturas de los sensores del sistema",
"usage": "Uso",
"disk_usage_of": "Uso de disco de",
"throughput_of": "Rendimiento de"
"throughput_of": "Rendimiento de",
"waiting_for": "Esperando suficientes registros para mostrar"
},
"auth": {
"login": "Por favor, inicie sesión en su cuenta",
"reset": "Ingrese la dirección de correo electrónico para restablecer la contraseña",
"create": "Por favor, cree una cuenta de administrador",
"create_account": "Crear cuenta",
"sign_in": "Iniciar sesión",
"openid_des": "Beszel admite OpenID Connect y muchos proveedores de autenticación OAuth2.",
"please_view_the": "Por favor, consulte el",
"for_instructions": "para obtener instrucciones.",
"forgot_password": "¿Olvidó su contraseña?",
"reset_password": "Restablecer contraseña",
"command_line_instructions": "Instrucciones de línea de comandos",
"command_1": "Si ha perdido la contraseña de su cuenta de administrador, puede restablecerla usando el siguiente comando.",
"command_2": "Luego inicie sesión en el backend y restablezca la contraseña de su cuenta de usuario en la tabla de usuarios."
}
}

View File

@@ -173,6 +173,22 @@
"temperature_des": "Températures des capteurs du système",
"usage": "Utilisation",
"disk_usage_of": "Utilisation du disque de",
"throughput_of": "Débit de"
"throughput_of": "Débit de",
"waiting_for": "En attente de suffisamment d'enregistrements pour afficher"
},
"auth": {
"login": "Veuillez vous connecter à votre compte",
"reset": "Entrez l'adresse e-mail pour réinitialiser le mot de passe",
"create": "Veuillez créer un compte administrateur",
"create_account": "Créer un compte",
"sign_in": "Se connecter",
"openid_des": "Beszel prend en charge OpenID Connect et de nombreux fournisseurs d'authentification OAuth2.",
"please_view_the": "Veuillez consulter le",
"for_instructions": "pour les instructions.",
"forgot_password": "Mot de passe oublié ?",
"reset_password": "Réinitialiser le mot de passe",
"command_line_instructions": "Instructions en ligne de commande",
"command_1": "Si vous avez perdu le mot de passe de votre compte administrateur, vous pouvez le réinitialiser en utilisant la commande suivante.",
"command_2": "Ensuite, connectez-vous au backend et réinitialisez le mot de passe de votre compte utilisateur dans la table des utilisateurs."
}
}

View File

@@ -173,6 +173,22 @@
"temperature_des": "Температуры датчиков системы",
"usage": "Использование",
"disk_usage_of": "Использование диска",
"throughput_of": "Пропускная способность"
"throughput_of": "Пропускная способность",
"waiting_for": "Ожидание достаточного количества записей для отображения"
},
"auth": {
"login": "Пожалуйста, войдите в свою учетную запись",
"reset": "Введите адрес электронной почты для сброса пароля",
"create": "Пожалуйста, создайте учетную запись администратора",
"create_account": "Создать учетную запись",
"sign_in": "Войти",
"openid_des": "Beszel поддерживает OpenID Connect и многих поставщиков аутентификации OAuth2.",
"please_view_the": "Пожалуйста, ознакомьтесь с",
"for_instructions": "для получения инструкций.",
"forgot_password": "Забыли пароль?",
"reset_password": "Сбросить пароль",
"command_line_instructions": "Инструкции по командной строке",
"command_1": "Если вы потеряли пароль от своей учетной записи администратора, вы можете сбросить его, используя следующую команду.",
"command_2": "Затем войдите в бэкэнд и сбросьте пароль своей учетной записи пользователя в таблице пользователей."
}
}

View File

@@ -173,6 +173,22 @@
"temperature_des": "系统传感器的温度",
"usage": "使用率",
"disk_usage_of": "的磁盘使用率",
"throughput_of": "的吞吐量"
"throughput_of": "的吞吐量",
"waiting_for": "等待足够的记录以显示"
},
"auth": {
"login": "请登入你的账户",
"reset": "输入邮箱来重设密码",
"create": "请创建管理员账户",
"create_account": "创建账户",
"sign_in": "登入",
"openid_des": "Beszel 支持 OpenID Connect 和许多 OAuth2 认证提供商。",
"please_view_the": "请检视",
"for_instructions": "以获取更多信息。",
"forgot_password": "忘记密码?",
"reset_password": "重设密码",
"command_line_instructions": "了解命令行指令",
"command_1": "如果您忘记了管理员账户的密码,可以使用以下命令重置。",
"command_2": "然后登录到后台,在用户表中重置您的用户账户密码。"
}
}

View File

@@ -173,6 +173,22 @@
"temperature_des": "系統感應器的溫度",
"usage": "使用率",
"disk_usage_of": "的磁碟使用率",
"throughput_of": "的吞吐量"
"throughput_of": "的吞吐量",
"waiting_for": "等待足夠的記錄以顯示"
},
"auth": {
"login": "請登入你的賬戶",
"reset": "輸入電郵來重設密碼",
"create": "請創建管理員賬戶",
"create_account": "創建賬戶",
"sign_in": "登入",
"openid_des": "Beszel 支持 OpenID Connect 和許多 OAuth2 認證提供商。",
"please_view_the": "請檢視",
"for_instructions": "以獲取更多信息。",
"forgot_password": "忘記密碼?",
"reset_password": "重設密碼",
"command_line_instructions": "了解命令行指令",
"command_1": "如果您忘記了管理員賬戶的密碼,可以使用以下命令重置。",
"command_2": "然後登入到後台,在用戶表中重置您的用戶賬戶密碼。"
}
}