feat: show ip info

This commit is contained in:
kieran 2024-11-26 19:14:07 +00:00
parent 3c7736dae7
commit 1f5506a920
No known key found for this signature in database
GPG Key ID: DE71CEB3925BE941
7 changed files with 52 additions and 13 deletions

View File

@ -54,6 +54,11 @@ export interface VmStatus {
disk_read: number;
}
export interface VmIpAssignment {
id: number;
ip: string;
}
export interface VmInstance {
id: number;
host_id: number;
@ -61,8 +66,8 @@ export interface VmInstance {
image_id: number;
template_id: number;
ssh_key_id: number;
created: Date;
expires: Date;
created: string;
expires: string;
cpu: number;
memory: number;
disk_size: number;
@ -72,6 +77,7 @@ export interface VmInstance {
template?: VmTemplate;
image?: VmOsImage;
ssh_key?: UserSshKey;
ip_assignments: Array<VmIpAssignment>;
}
export interface VmOsImage {
@ -100,7 +106,7 @@ export class LNVpsApi {
constructor(
readonly url: string,
readonly publisher: EventPublisher | undefined,
) {}
) { }
async listVms() {
const { data } = await this.#handleResponse<ApiResponse<Array<VmInstance>>>(

View File

@ -1,6 +1,6 @@
import { MouseEventHandler } from "react";
import Icons from "../icons.svg";
import Icons from "../icons.svg?url";
type Props = {
name: string;

View File

@ -11,16 +11,25 @@ export default function VmActions({ vm }: { vm: VmInstance }) {
name={state === "running" ? "stop" : "start"}
className="bg-neutral-700 p-2 rounded-lg hover:bg-neutral-600"
size={40}
onClick={e => {
e.stopPropagation();
}}
/>
<Icon
name="delete"
className="bg-neutral-700 p-2 rounded-lg hover:bg-neutral-600"
size={40}
onClick={e => {
e.stopPropagation();
}}
/>
<Icon
name="refresh-1"
className="bg-neutral-700 p-2 rounded-lg hover:bg-neutral-600"
size={40}
onClick={e => {
e.stopPropagation();
}}
/>
</div>
</div>

View File

@ -1,15 +1,19 @@
import { Link } from "react-router-dom";
import { Link, useNavigate } from "react-router-dom";
import { VmInstance } from "../api";
import OsImageName from "./os-image-name";
import VpsResources from "./vps-resources";
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 isExpired = expires <= new Date();
const navigate = useNavigate();
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>
<span className="text-sm text-neutral-400">#{vm.id}</span>
@ -22,7 +26,7 @@ export default function VpsInstanceRow({ vm }: { vm: VmInstance }) {
</div>
<VpsResources vm={vm} />
</div>
<div className="flex gap-2 items-center">
{(actions ?? true) && <div className="flex gap-2 items-center">
{isExpired && (
<>
<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} />}
</div>
</div>}
</div>
);
}

View File

@ -12,8 +12,8 @@ export const GB = KB * 1000;
export const TB = GB * 1000;
export const PB = TB * 1000;
//export const ApiUrl = "http://localhost:8000";
export const ApiUrl = "https://api.lnvps.net";
export const ApiUrl = "http://localhost:8000";
//export const ApiUrl = "https://api.lnvps.net";
export const NostrProfile = new NostrLink(
NostrPrefix.Profile,

View File

@ -23,7 +23,7 @@ export default function AccountPage() {
<h3>My Resources</h3>
<div className="flex flex-col gap-2">
{vms.map((a) => (
<VpsInstanceRow key={a.id} vm={a} />
<VpsInstanceRow key={a.id} vm={a} actions={false} />
))}
</div>
</>

View File

@ -6,6 +6,8 @@ import { ApiUrl } from "../const";
import { EventPublisher } from "@snort/system";
import { useCallback, useEffect, useState } from "react";
import VpsPayment from "../components/vps-payment";
import CostLabel from "../components/cost";
import { AsyncButton } from "../components/button";
export default function VmPage() {
const { state } = useLocation() as { state?: VmInstance };
@ -39,7 +41,24 @@ export default function VmPage() {
}
return (
<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" && (
<>
<h3>Renew VPS</h3>
@ -56,6 +75,7 @@ export default function VmPage() {
navigate("/vm", {
state: newState,
});
setPayment(undefined);
}}
/>
)}