Files
MikrotikManager/lib/app-switcher-config.ts
T
DenozordecandCursor 5f29cfaf0f
Docker images / prepare-release (push) Successful in 4s
Docker images / backend-image (push) Failing after 2m36s
Docker images / frontend-image (push) Successful in 2m22s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 55s
Docker images / publish-release (push) Skipped
fix(ui): выровнять chrome Frame и убрать Card-оболочки
Подключить App Switcher, NavUser, OpsPanel и AlertDialog вместо Card-shell.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-17 15:36:56 +07:00

163 lines
4.1 KiB
TypeScript

import {
ChartBarIcon,
CloudIcon,
GlobeIcon,
LayoutDashboardIcon,
ServerIcon,
type LucideIcon,
} from "lucide-react"
import { formatAppVersionLabel, getAppVersion } from "@/lib/app-version"
export const CURRENT_APP_ID = "mm"
export type AppSwitcherIconName =
| "server"
| "cloud"
| "globe"
| "dashboard"
| "chart"
export type AppSwitcherEntry = {
id: string
name: string
subtitle?: string
url: string
icon: AppSwitcherIconName
shortcut?: string
enabled?: boolean
}
export type AppSwitcherConfig = {
menuLabel: string
apps: AppSwitcherEntry[]
}
export const APP_SWITCHER_ICONS: Record<AppSwitcherIconName, LucideIcon> = {
server: ServerIcon,
cloud: CloudIcon,
globe: GlobeIcon,
dashboard: LayoutDashboardIcon,
chart: ChartBarIcon,
}
function localAppUrl(): string {
if (typeof window !== "undefined") return window.location.origin
return process.env.NEXT_PUBLIC_APP_URL?.replace(/\/$/, "") || "http://localhost:3000"
}
export function localMikrotikEntry(): AppSwitcherEntry {
return {
id: CURRENT_APP_ID,
name: "MikrotikManager",
subtitle: formatAppVersionLabel(getAppVersion()),
url: localAppUrl(),
icon: "dashboard",
enabled: true,
}
}
/** Offline fallback when auth-portal is unreachable. */
export const DEFAULT_APP_SWITCHER_CONFIG: AppSwitcherConfig = {
menuLabel: "Приложения",
apps: [
localMikrotikEntry(),
{
id: "cfdm",
name: "CF Domain Manager",
subtitle: "Управление доменами",
url: "https://cfdm.shnt.top",
icon: "cloud",
},
{
id: "vps",
name: "VPS Tracker",
subtitle: "Учёт виртуальных серверов",
url: "https://vps.shnt.top",
icon: "server",
},
{
id: "bgp",
name: "EvoBGP",
subtitle: "BGP маршрутизация",
url: "https://bgp.shnt.top",
icon: "globe",
},
{
id: "fw",
name: "EvoFirewall",
subtitle: "Firewall controller",
url: "https://fw.shnt.top",
icon: "server",
},
{
id: "dns",
name: "Technitium DNS",
subtitle: "DNS-сервер",
url: "https://dns.shnt.top",
icon: "globe",
},
],
}
const ICON_NAMES = new Set<AppSwitcherIconName>([
"server",
"cloud",
"globe",
"dashboard",
"chart",
])
function asIcon(value: unknown): AppSwitcherIconName {
return ICON_NAMES.has(value as AppSwitcherIconName)
? (value as AppSwitcherIconName)
: "server"
}
export function parseAppSwitcherConfig(raw: unknown): AppSwitcherConfig | null {
if (!raw || typeof raw !== "object") return null
const data = raw as { menuLabel?: unknown; apps?: unknown }
if (!Array.isArray(data.apps) || data.apps.length === 0) return null
const apps: AppSwitcherEntry[] = []
for (const item of data.apps) {
if (!item || typeof item !== "object") continue
const row = item as Record<string, unknown>
if (typeof row.id !== "string" || typeof row.name !== "string" || typeof row.url !== "string") {
continue
}
if (row.enabled === false) continue
apps.push({
id: row.id,
name: row.name,
subtitle: typeof row.subtitle === "string" ? row.subtitle : undefined,
url: row.url,
icon: asIcon(row.icon),
shortcut: typeof row.shortcut === "string" ? row.shortcut : undefined,
enabled: true,
})
}
if (apps.length === 0) return null
return {
menuLabel: typeof data.menuLabel === "string" && data.menuLabel ? data.menuLabel : "Приложения",
apps,
}
}
export function mergeWithLocalApp(config: AppSwitcherConfig): AppSwitcherConfig {
const local = localMikrotikEntry()
const rest = config.apps.filter((app) => app.id !== CURRENT_APP_ID)
return { ...config, apps: [local, ...rest] }
}
export function authPortalBaseUrl(): string | null {
const raw = process.env.NEXT_PUBLIC_AUTH_PORTAL_URL?.trim()
if (!raw) return null
return raw.replace(/\/$/, "")
}
export function getCurrentApp(
config: AppSwitcherConfig = DEFAULT_APP_SWITCHER_CONFIG,
): AppSwitcherEntry {
return config.apps.find((app) => app.id === CURRENT_APP_ID) ?? localMikrotikEntry()
}