Added LXC resource page
This commit is contained in:
parent
4a997778e6
commit
c66d035a1c
@ -1,16 +1,16 @@
|
|||||||
import { Link, useSearchParams } from "expo-router";
|
import { Link, useSearchParams } from "expo-router";
|
||||||
import { View, Text, SafeAreaView, ScrollView, Button } from "react-native";
|
import { View, Text, ScrollView } from "react-native";
|
||||||
import { NodeResource, useNode } from "../../../hooks/useNode";
|
import { NodeResource, ResourceType, useNode } from "../../../hooks/useNode";
|
||||||
import Icon from "@expo/vector-icons/Feather";
|
import Icon from "@expo/vector-icons/Feather";
|
||||||
import tw from "twrnc";
|
import tw from "twrnc";
|
||||||
import { formatBytes, formatPercentage } from "../../../lib/helper/format";
|
import { formatBytes } from "../../../lib/helper/format";
|
||||||
import { useEffect, useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { Gauge } from "../../../components/Gauge";
|
import { Gauge } from "../../../components/Gauge";
|
||||||
import ProgressBar from "../../../components/ProgressBar";
|
import ProgressBar from "../../../components/ProgressBar";
|
||||||
import Card from "../../../components/Card";
|
import Card from "../../../components/Card";
|
||||||
|
|
||||||
interface ResourceListItemProps {
|
interface ResourceListItemProps {
|
||||||
type: "LXC" | "QEMU";
|
type: ResourceType;
|
||||||
resource: NodeResource;
|
resource: NodeResource;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ export function ResourceListItem({ type, resource }: ResourceListItemProps) {
|
|||||||
resource.status === "running" ? "bg-green-200" : "bg-slate-200"
|
resource.status === "running" ? "bg-green-200" : "bg-slate-200"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{type === "LXC" ? (
|
{type === "lxc" ? (
|
||||||
<Icon name="package" size={22} />
|
<Icon name="package" size={22} />
|
||||||
) : (
|
) : (
|
||||||
<Icon name="monitor" size={22} />
|
<Icon name="monitor" size={22} />
|
||||||
@ -72,24 +72,28 @@ export default function NodePage() {
|
|||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
{node.rdd.isSuccess && (
|
{node.rddData.isSuccess && (
|
||||||
<View>
|
<View>
|
||||||
<View style={tw.style("mb-2")}>
|
<View style={tw.style("mb-2")}>
|
||||||
<ProgressBar label="CPU" value={node.rdd.data.cpu} max={1} />
|
<ProgressBar
|
||||||
|
label="CPU"
|
||||||
|
value={node.rddData.data.cpu}
|
||||||
|
max={1}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={tw.style("mb-2")}>
|
<View style={tw.style("mb-2")}>
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
label="Memory"
|
label="Memory"
|
||||||
value={node.rdd.data.memused}
|
value={node.rddData.data.memused}
|
||||||
max={node.rdd.data.memtotal}
|
max={node.rddData.data.memtotal}
|
||||||
formatFn={formatBytes}
|
formatFn={formatBytes}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={tw.style("mb-2")}>
|
<View style={tw.style("mb-2")}>
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
label="Storage"
|
label="Storage"
|
||||||
value={node.rdd.data.rootused}
|
value={node.rddData.data.rootused}
|
||||||
max={node.rdd.data.roottotal}
|
max={node.rddData.data.roottotal}
|
||||||
formatFn={formatBytes}
|
formatFn={formatBytes}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
@ -101,13 +105,13 @@ export default function NodePage() {
|
|||||||
<View style={tw.style("flex-1")}>
|
<View style={tw.style("flex-1")}>
|
||||||
<Gauge
|
<Gauge
|
||||||
label="Up"
|
label="Up"
|
||||||
value={`${formatBytes(node.rdd.data.netout)}/s`}
|
value={`${formatBytes(node.rddData.data.netout)}/s`}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={tw.style("flex-1")}>
|
<View style={tw.style("flex-1")}>
|
||||||
<Gauge
|
<Gauge
|
||||||
label="Down"
|
label="Down"
|
||||||
value={`${formatBytes(node.rdd.data.netin)}/s`}
|
value={`${formatBytes(node.rddData.data.netin)}/s`}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@ -120,12 +124,12 @@ export default function NodePage() {
|
|||||||
<Link
|
<Link
|
||||||
key={lxc.vmid}
|
key={lxc.vmid}
|
||||||
href={{
|
href={{
|
||||||
pathname: "/nodes/[name]/lxc/[vmid]",
|
pathname: "/nodes/[node]/lxc/[vmid]",
|
||||||
params: { name: nodeName, vmid: lxc.vmid },
|
params: { node: nodeName, vmid: lxc.vmid },
|
||||||
}}
|
}}
|
||||||
style={tw.style("m-2")}
|
style={tw.style("m-2")}
|
||||||
>
|
>
|
||||||
<ResourceListItem type="LXC" resource={lxc} />
|
<ResourceListItem type="lxc" resource={lxc} />
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</Card>
|
</Card>
|
||||||
@ -134,12 +138,12 @@ export default function NodePage() {
|
|||||||
<Link
|
<Link
|
||||||
key={vm.vmid}
|
key={vm.vmid}
|
||||||
href={{
|
href={{
|
||||||
pathname: "/nodes/[node]/lxc/[vmid]",
|
pathname: "/nodes/[node]/qemu/[vmid]",
|
||||||
params: { node: nodeName, vmid: vm.vmid },
|
params: { node: nodeName, vmid: vm.vmid },
|
||||||
}}
|
}}
|
||||||
style={tw.style("m-2")}
|
style={tw.style("m-2")}
|
||||||
>
|
>
|
||||||
<ResourceListItem type="QEMU" resource={vm} />
|
<ResourceListItem type="qemu" resource={vm} />
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</Card>
|
</Card>
|
||||||
|
@ -14,21 +14,20 @@ function buildConsoleUrl(domain: string, node: string, vmid: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function QEMUResourceConsolePage() {
|
export default function QEMUResourceConsolePage() {
|
||||||
const { name, vmid } = useSearchParams<{ name: string; vmid: string }>();
|
const { node, vmid } = useSearchParams<{ node: string; vmid: string }>();
|
||||||
const { domain, ticketData } = useAuthStore();
|
const { domain, ticketData } = useAuthStore();
|
||||||
|
console.log({ ticketData }, buildConsoleUrl(domain, node, vmid));
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={{ flex: 1 }}>
|
<SafeAreaView style={{ flex: 1 }}>
|
||||||
<WebView
|
<WebView
|
||||||
source={{
|
source={{
|
||||||
uri: buildConsoleUrl(domain, name, vmid),
|
uri: buildConsoleUrl(domain, node, vmid),
|
||||||
headers: {
|
headers: {
|
||||||
Cookie: `PVEAuthCookie=${ticketData.data.ticket}`,
|
Cookie: `PVEAuthCookie=${ticketData.data.ticket}`,
|
||||||
CSRFPreventionToken: ticketData.data.CSRFPreventionToken,
|
|
||||||
Accept:
|
Accept:
|
||||||
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
|
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
allowsFullscreenVideo={true}
|
|
||||||
scalesPageToFit={false}
|
scalesPageToFit={false}
|
||||||
injectedJavaScript={`
|
injectedJavaScript={`
|
||||||
const meta = document.createElement('meta');
|
const meta = document.createElement('meta');
|
||||||
|
@ -1,23 +1,128 @@
|
|||||||
import { View, Text } from "react-native";
|
import { useRouter, useSearchParams } from "expo-router";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import { View, Text, TouchableHighlight, ScrollView } from "react-native";
|
||||||
|
import Icon from "@expo/vector-icons/Feather";
|
||||||
import tw from "twrnc";
|
import tw from "twrnc";
|
||||||
|
import Card from "../../../../../components/Card";
|
||||||
|
import { Gauge } from "../../../../../components/Gauge";
|
||||||
|
import ProgressBar from "../../../../../components/ProgressBar";
|
||||||
|
import { useNode } from "../../../../../hooks/useNode";
|
||||||
|
import { useResource } from "../../../../../hooks/useResource";
|
||||||
|
import { formatBytes } from "../../../../../lib/helper/format";
|
||||||
|
|
||||||
export default function LXCResourcePage() {
|
export default function LXCResourcePage() {
|
||||||
return (
|
const router = useRouter();
|
||||||
<View>
|
const { node, vmid } = useSearchParams<{ node: string; vmid: string }>();
|
||||||
<View
|
const { rddData, config } = useResource(node, "lxc", vmid);
|
||||||
style={tw.style("bg-white m-2 p-3 rounded-lg border border-slate-200")}
|
const { lxc: lxcs } = useNode(node);
|
||||||
>
|
|
||||||
<Text>Currently not supported</Text>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<Text style={tw.style("ml-6 mt-4")}>Config</Text>
|
const status = useMemo(() => {
|
||||||
<View
|
if (lxcs.isSuccess) {
|
||||||
style={tw.style("bg-white m-2 p-1 rounded-lg border border-slate-200")}
|
return lxcs.data.find((lxc) => lxc.vmid.toString() === vmid);
|
||||||
|
}
|
||||||
|
}, [lxcs]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScrollView>
|
||||||
|
<Card>
|
||||||
|
<View style={tw.style("p-1")}>
|
||||||
|
<View style={tw.style("px-1 mb-4")}>
|
||||||
|
<View style={tw.style("flex flex-row items-center")}>
|
||||||
|
<View
|
||||||
|
style={tw.style(
|
||||||
|
"h-12 w-12 rounded-lg flex flex-row justify-center items-center",
|
||||||
|
status.status === "running" ? "bg-green-200" : "bg-slate-200"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Icon name="monitor" size={22} />
|
||||||
|
</View>
|
||||||
|
<View style={tw.style("mx-2 flex-1")}>
|
||||||
|
<Text style={tw.style("text-xl")}>
|
||||||
|
{vmid}: {status.name}
|
||||||
|
</Text>
|
||||||
|
<Text style={tw.style("text-base")}>Node: {node}</Text>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
style={tw.style(
|
||||||
|
"h-12 w-12 flex flex-row justify-center items-center"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Icon name="power" size={22} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
{rddData.isSuccess && (
|
||||||
|
<View>
|
||||||
|
<View style={tw.style("mb-2")}>
|
||||||
|
<ProgressBar label="CPU" value={rddData.data.cpu} max={1} />
|
||||||
|
</View>
|
||||||
|
<View style={tw.style("mb-2")}>
|
||||||
|
<ProgressBar
|
||||||
|
label="Memory"
|
||||||
|
value={rddData.data.mem ?? 1}
|
||||||
|
max={rddData.data.maxmem ?? 1}
|
||||||
|
formatFn={formatBytes}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View style={tw.style("mb-2")}>
|
||||||
|
<ProgressBar
|
||||||
|
label="Storage"
|
||||||
|
value={rddData.data.disk ?? 1}
|
||||||
|
max={rddData.data.maxdisk ?? 1}
|
||||||
|
formatFn={formatBytes}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
style={tw.style(
|
||||||
|
"flex flex-row justify-evenly items-center p-2"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<View style={tw.style("flex-1")}>
|
||||||
|
<Gauge
|
||||||
|
label="Up"
|
||||||
|
value={`${formatBytes(rddData.data.netout)}/s`}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View style={tw.style("flex-1")}>
|
||||||
|
<Gauge
|
||||||
|
label="Down"
|
||||||
|
value={`${formatBytes(rddData.data.netin)}/s`}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<TouchableHighlight
|
||||||
|
style={tw.style("m-2 rounded-lg bg-slate-800")}
|
||||||
|
onPress={() =>
|
||||||
|
router.push({
|
||||||
|
pathname: "/nodes/[node]/lxc/[vmid]/console",
|
||||||
|
params: { node, vmid },
|
||||||
|
})
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Text style={tw.style("px-2 py-7 flex flex-row text-center")}>
|
<View style={tw.style("flex flex-row m-4 items-center justify-center")}>
|
||||||
Currently not supported
|
<Icon name="terminal" size={22} style={tw.style("mr-2 text-white")} />
|
||||||
</Text>
|
<Text style={tw.style("text-white")}>Open terminal</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</TouchableHighlight>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<View style={tw.style("m-2")}>
|
||||||
|
{config.isSuccess &&
|
||||||
|
Object.entries(config.data).map(([key, val]) => {
|
||||||
|
return (
|
||||||
|
<View key={key} style={tw.style("mb-1")}>
|
||||||
|
<Text style={tw.style("text-lg")}>{key}:</Text>
|
||||||
|
<Text>{val}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
</Card>
|
||||||
|
</ScrollView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Link } from "expo-router";
|
import { Link } from "expo-router";
|
||||||
import { View, Text, SafeAreaView } from "react-native";
|
import { View, Text } from "react-native";
|
||||||
import { ProxmoxNode, useNodes } from "../../hooks/useNodes";
|
import { ProxmoxNode, useNodes } from "../../hooks/useNodes";
|
||||||
import Icon from "@expo/vector-icons/Feather";
|
import Icon from "@expo/vector-icons/Feather";
|
||||||
import tw from "twrnc";
|
import tw from "twrnc";
|
||||||
|
@ -9,7 +9,7 @@ interface CardProps {
|
|||||||
export default function Card({ label, children }: CardProps) {
|
export default function Card({ label, children }: CardProps) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{label && <Text style={tw.style("ml-6 mt-4")}>{label}</Text>}
|
{label && <Text style={tw.style("ml-6 mt-2")}>{label}</Text>}
|
||||||
<View
|
<View
|
||||||
style={tw.style("bg-white m-2 p-1 rounded-lg border border-slate-200")}
|
style={tw.style("bg-white m-2 p-1 rounded-lg border border-slate-200")}
|
||||||
>
|
>
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
TouchableOpacity,
|
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
SafeAreaView,
|
SafeAreaView,
|
||||||
TextInputProps,
|
TextInputProps,
|
||||||
|
TouchableHighlight,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import tw from "twrnc";
|
import tw from "twrnc";
|
||||||
@ -92,14 +92,14 @@ export default function Login() {
|
|||||||
secureTextEntry
|
secureTextEntry
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<TouchableOpacity
|
<TouchableHighlight
|
||||||
style={tw.style(
|
style={tw.style(
|
||||||
"flex bg-slate-700 rounded-md flex flex-row items-center justify-center"
|
"flex bg-slate-700 rounded-md flex flex-row items-center justify-center"
|
||||||
)}
|
)}
|
||||||
onPress={() => ticketMut.mutate({ domain, username, password })}
|
onPress={() => ticketMut.mutate({ domain, username, password })}
|
||||||
>
|
>
|
||||||
<Text style={tw.style("text-white font-semibold py-3")}>Sign In</Text>
|
<Text style={tw.style("text-white font-semibold py-3")}>Sign In</Text>
|
||||||
</TouchableOpacity>
|
</TouchableHighlight>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useQueries, useQuery } from "react-query";
|
import { useQueries, useQuery } from "react-query";
|
||||||
import useAuthStore from "../stores/useAuthStore";
|
import useAuthStore from "../stores/useAuthStore";
|
||||||
|
|
||||||
export type ResourceType = "LXC" | "QEMU";
|
export type ResourceType = "lxc" | "qemu";
|
||||||
|
|
||||||
export interface NodeResource {
|
export interface NodeResource {
|
||||||
status: "stopped" | "running";
|
status: "stopped" | "running";
|
||||||
@ -18,7 +18,7 @@ export interface NodeStatus {
|
|||||||
kversion: string;
|
kversion: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NodeRDD {
|
export interface NodeRddData {
|
||||||
netin: number;
|
netin: number;
|
||||||
netout: number;
|
netout: number;
|
||||||
rootused: number;
|
rootused: number;
|
||||||
@ -30,17 +30,17 @@ export interface NodeRDD {
|
|||||||
|
|
||||||
export function useNode(name: string) {
|
export function useNode(name: string) {
|
||||||
const http = useAuthStore((state) => state.http);
|
const http = useAuthStore((state) => state.http);
|
||||||
const [rdd, status, lxc, qemu] = useQueries([
|
const [rddData, status, lxc, qemu] = useQueries([
|
||||||
{
|
{
|
||||||
queryKey: ["nodes", name, "rdd"],
|
queryKey: ["nodes", name, "rddData"],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
http.get<{ data: NodeRDD[] }>(`/api2/json/nodes/${name}/rrddata`, {
|
http.get<{ data: NodeRddData[] }>(`/api2/json/nodes/${name}/rrddata`, {
|
||||||
params: {
|
params: {
|
||||||
timeframe: "hour",
|
timeframe: "hour",
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
enabled: !!name,
|
enabled: !!name,
|
||||||
select: (data): NodeRDD => data.data.data.at(-1),
|
select: (data): NodeRddData => data.data.data.at(-1),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
queryKey: ["nodes", name, "status"],
|
queryKey: ["nodes", name, "status"],
|
||||||
@ -66,7 +66,7 @@ export function useNode(name: string) {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
rdd,
|
rddData,
|
||||||
status,
|
status,
|
||||||
lxc,
|
lxc,
|
||||||
qemu,
|
qemu,
|
||||||
|
@ -21,22 +21,6 @@ export function useNodes() {
|
|||||||
["nodes"],
|
["nodes"],
|
||||||
async () => {
|
async () => {
|
||||||
return http.get<GetNodeResp>("/api2/json/nodes");
|
return http.get<GetNodeResp>("/api2/json/nodes");
|
||||||
// return {
|
|
||||||
// data: {
|
|
||||||
// data: [
|
|
||||||
// {
|
|
||||||
// cpu: 0.0166442953020134,
|
|
||||||
// mem: 21713018880,
|
|
||||||
// maxmem: 29306216448,
|
|
||||||
// uptime: 4854322,
|
|
||||||
// status: "online",
|
|
||||||
// maxcpu: 12,
|
|
||||||
// node: "pve",
|
|
||||||
// },
|
|
||||||
// ] as ProxmoxNode[],
|
|
||||||
// },
|
|
||||||
// isSuccess: true,
|
|
||||||
// };
|
|
||||||
},
|
},
|
||||||
{ select: (data) => data.data.data, refetchInterval: 6000 }
|
{ select: (data) => data.data.data, refetchInterval: 6000 }
|
||||||
);
|
);
|
||||||
|
52
hooks/useResource.ts
Normal file
52
hooks/useResource.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { useQueries } from "react-query";
|
||||||
|
import useAuthStore from "../stores/useAuthStore";
|
||||||
|
import { ResourceType } from "./useNode";
|
||||||
|
|
||||||
|
interface ResourceRddData {
|
||||||
|
cpu: number;
|
||||||
|
disk: number;
|
||||||
|
maxdisk: number;
|
||||||
|
mem: number;
|
||||||
|
maxmem: number;
|
||||||
|
netin: number;
|
||||||
|
netout: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useResource(
|
||||||
|
node: string,
|
||||||
|
type: ResourceType,
|
||||||
|
vmid: string | number
|
||||||
|
) {
|
||||||
|
console.log({ node, type, vmid });
|
||||||
|
const http = useAuthStore((state) => state.http);
|
||||||
|
const [rddData, config] = useQueries([
|
||||||
|
{
|
||||||
|
queryKey: ["nodes", node, type, vmid, "rdd"],
|
||||||
|
queryFn: () =>
|
||||||
|
http.get<{ data: ResourceRddData[] }>(
|
||||||
|
`/api2/json/nodes/${node}/${type}/${vmid}/rrddata`,
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
timeframe: "hour",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
),
|
||||||
|
enabled: !!(node && vmid),
|
||||||
|
select: (data): ResourceRddData => data.data.data.at(-1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
queryKey: ["nodes", node, type, vmid, "config"],
|
||||||
|
queryFn: () =>
|
||||||
|
http.get<{ data: object }>(
|
||||||
|
`/api2/json/nodes/${node}/${type}/${vmid}/config`
|
||||||
|
),
|
||||||
|
enabled: !!(node && vmid),
|
||||||
|
select: (data): object => data.data.data,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
rddData,
|
||||||
|
config,
|
||||||
|
};
|
||||||
|
}
|
@ -19,7 +19,7 @@ export async function createTicket({
|
|||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
}: CreateTicketOpts) {
|
}: CreateTicketOpts) {
|
||||||
const url = `${domain}/api2/json/access/ticket`;
|
const url = new URL("/api2/json/access/ticket", domain).toString();
|
||||||
const headers = { "Content-Type": "application/json" };
|
const headers = { "Content-Type": "application/json" };
|
||||||
return axios.post<CreateTicketResp>(url, { username, password }, { headers });
|
return axios.post<CreateTicketResp>(url, { username, password }, { headers });
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user