feat: show ip info
This commit is contained in:
parent
3c7736dae7
commit
1f5506a920
12
src/api.ts
12
src/api.ts
@ -54,6 +54,11 @@ export interface VmStatus {
|
|||||||
disk_read: number;
|
disk_read: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface VmIpAssignment {
|
||||||
|
id: number;
|
||||||
|
ip: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface VmInstance {
|
export interface VmInstance {
|
||||||
id: number;
|
id: number;
|
||||||
host_id: number;
|
host_id: number;
|
||||||
@ -61,8 +66,8 @@ export interface VmInstance {
|
|||||||
image_id: number;
|
image_id: number;
|
||||||
template_id: number;
|
template_id: number;
|
||||||
ssh_key_id: number;
|
ssh_key_id: number;
|
||||||
created: Date;
|
created: string;
|
||||||
expires: Date;
|
expires: string;
|
||||||
cpu: number;
|
cpu: number;
|
||||||
memory: number;
|
memory: number;
|
||||||
disk_size: number;
|
disk_size: number;
|
||||||
@ -72,6 +77,7 @@ export interface VmInstance {
|
|||||||
template?: VmTemplate;
|
template?: VmTemplate;
|
||||||
image?: VmOsImage;
|
image?: VmOsImage;
|
||||||
ssh_key?: UserSshKey;
|
ssh_key?: UserSshKey;
|
||||||
|
ip_assignments: Array<VmIpAssignment>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VmOsImage {
|
export interface VmOsImage {
|
||||||
@ -100,7 +106,7 @@ export class LNVpsApi {
|
|||||||
constructor(
|
constructor(
|
||||||
readonly url: string,
|
readonly url: string,
|
||||||
readonly publisher: EventPublisher | undefined,
|
readonly publisher: EventPublisher | undefined,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
async listVms() {
|
async listVms() {
|
||||||
const { data } = await this.#handleResponse<ApiResponse<Array<VmInstance>>>(
|
const { data } = await this.#handleResponse<ApiResponse<Array<VmInstance>>>(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { MouseEventHandler } from "react";
|
import { MouseEventHandler } from "react";
|
||||||
|
|
||||||
import Icons from "../icons.svg";
|
import Icons from "../icons.svg?url";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -11,16 +11,25 @@ export default function VmActions({ vm }: { vm: VmInstance }) {
|
|||||||
name={state === "running" ? "stop" : "start"}
|
name={state === "running" ? "stop" : "start"}
|
||||||
className="bg-neutral-700 p-2 rounded-lg hover:bg-neutral-600"
|
className="bg-neutral-700 p-2 rounded-lg hover:bg-neutral-600"
|
||||||
size={40}
|
size={40}
|
||||||
|
onClick={e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Icon
|
<Icon
|
||||||
name="delete"
|
name="delete"
|
||||||
className="bg-neutral-700 p-2 rounded-lg hover:bg-neutral-600"
|
className="bg-neutral-700 p-2 rounded-lg hover:bg-neutral-600"
|
||||||
size={40}
|
size={40}
|
||||||
|
onClick={e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Icon
|
<Icon
|
||||||
name="refresh-1"
|
name="refresh-1"
|
||||||
className="bg-neutral-700 p-2 rounded-lg hover:bg-neutral-600"
|
className="bg-neutral-700 p-2 rounded-lg hover:bg-neutral-600"
|
||||||
size={40}
|
size={40}
|
||||||
|
onClick={e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
import { Link } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
import { VmInstance } from "../api";
|
import { VmInstance } from "../api";
|
||||||
import OsImageName from "./os-image-name";
|
import OsImageName from "./os-image-name";
|
||||||
import VpsResources from "./vps-resources";
|
import VpsResources from "./vps-resources";
|
||||||
import VmActions from "./vps-actions";
|
import VmActions from "./vps-actions";
|
||||||
|
|
||||||
export default function VpsInstanceRow({ vm }: { vm: VmInstance }) {
|
export default function VpsInstanceRow({ vm, actions }: { vm: VmInstance, actions?: boolean }) {
|
||||||
const expires = new Date(vm.expires);
|
const expires = new Date(vm.expires);
|
||||||
const isExpired = expires <= new Date();
|
const isExpired = expires <= new Date();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-between items-center rounded-xl bg-neutral-900 px-3 py-2 cursor-pointer hover:bg-neutral-800">
|
<div className="flex justify-between items-center rounded-xl bg-neutral-900 px-3 py-2 cursor-pointer hover:bg-neutral-800"
|
||||||
|
onClick={() => navigate("/vm", {
|
||||||
|
state: vm
|
||||||
|
})}>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div>
|
<div>
|
||||||
<span className="text-sm text-neutral-400">#{vm.id}</span>
|
<span className="text-sm text-neutral-400">#{vm.id}</span>
|
||||||
@ -22,7 +26,7 @@ export default function VpsInstanceRow({ vm }: { vm: VmInstance }) {
|
|||||||
</div>
|
</div>
|
||||||
<VpsResources vm={vm} />
|
<VpsResources vm={vm} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2 items-center">
|
{(actions ?? true) && <div className="flex gap-2 items-center">
|
||||||
{isExpired && (
|
{isExpired && (
|
||||||
<>
|
<>
|
||||||
<Link to="/vm/renew" className="text-red-500 text-sm" state={vm}>
|
<Link to="/vm/renew" className="text-red-500 text-sm" state={vm}>
|
||||||
@ -31,7 +35,7 @@ export default function VpsInstanceRow({ vm }: { vm: VmInstance }) {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{!isExpired && <VmActions vm={vm} />}
|
{!isExpired && <VmActions vm={vm} />}
|
||||||
</div>
|
</div>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,8 @@ export const GB = KB * 1000;
|
|||||||
export const TB = GB * 1000;
|
export const TB = GB * 1000;
|
||||||
export const PB = TB * 1000;
|
export const PB = TB * 1000;
|
||||||
|
|
||||||
//export const ApiUrl = "http://localhost:8000";
|
export const ApiUrl = "http://localhost:8000";
|
||||||
export const ApiUrl = "https://api.lnvps.net";
|
//export const ApiUrl = "https://api.lnvps.net";
|
||||||
|
|
||||||
export const NostrProfile = new NostrLink(
|
export const NostrProfile = new NostrLink(
|
||||||
NostrPrefix.Profile,
|
NostrPrefix.Profile,
|
||||||
|
@ -23,7 +23,7 @@ export default function AccountPage() {
|
|||||||
<h3>My Resources</h3>
|
<h3>My Resources</h3>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
{vms.map((a) => (
|
{vms.map((a) => (
|
||||||
<VpsInstanceRow key={a.id} vm={a} />
|
<VpsInstanceRow key={a.id} vm={a} actions={false} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
@ -6,6 +6,8 @@ import { ApiUrl } from "../const";
|
|||||||
import { EventPublisher } from "@snort/system";
|
import { EventPublisher } from "@snort/system";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import VpsPayment from "../components/vps-payment";
|
import VpsPayment from "../components/vps-payment";
|
||||||
|
import CostLabel from "../components/cost";
|
||||||
|
import { AsyncButton } from "../components/button";
|
||||||
|
|
||||||
export default function VmPage() {
|
export default function VmPage() {
|
||||||
const { state } = useLocation() as { state?: VmInstance };
|
const { state } = useLocation() as { state?: VmInstance };
|
||||||
@ -39,7 +41,24 @@ export default function VmPage() {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<VpsInstanceRow vm={state} />
|
<VpsInstanceRow vm={state} actions={false} />
|
||||||
|
{action === undefined && <>
|
||||||
|
<div className="text-xl">Renewal</div>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<div>{new Date(state.expires).toDateString()}</div>
|
||||||
|
{state.template?.cost_plan && <div><CostLabel cost={state.template?.cost_plan} /></div>}
|
||||||
|
</div>
|
||||||
|
<AsyncButton onClick={() => navigate("/vm/renew", { state })}>
|
||||||
|
Extend Now
|
||||||
|
</AsyncButton>
|
||||||
|
<div className="text-xl">Network</div>
|
||||||
|
<div className="flex gap-4">
|
||||||
|
{(state.ip_assignments?.length ?? 0) === 0 && <div className="text-sm text-red-500">No IP's assigned</div>}
|
||||||
|
{state.ip_assignments?.map(a => <div key={a.id} className="text-sm bg-neutral-900 px-3 py-1 rounded-lg">
|
||||||
|
{a.ip.split("/")[0]}
|
||||||
|
</div>)}
|
||||||
|
</div>
|
||||||
|
</>}
|
||||||
{action === "renew" && (
|
{action === "renew" && (
|
||||||
<>
|
<>
|
||||||
<h3>Renew VPS</h3>
|
<h3>Renew VPS</h3>
|
||||||
@ -56,6 +75,7 @@ export default function VmPage() {
|
|||||||
navigate("/vm", {
|
navigate("/vm", {
|
||||||
state: newState,
|
state: newState,
|
||||||
});
|
});
|
||||||
|
setPayment(undefined);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user