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; 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>>>(

View File

@ -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;

View File

@ -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>

View File

@ -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>
); );
} }

View File

@ -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,

View File

@ -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>
</> </>

View File

@ -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);
}} }}
/> />
)} )}