From 3c7736dae7d8188f449ea37dab77a30627688938 Mon Sep 17 00:00:00 2001 From: kieran Date: Tue, 26 Nov 2024 15:29:18 +0000 Subject: [PATCH] feat: backend api --- index.html | 5 +- package.json | 10 +- public/logo.jpg | Bin 0 -> 34542 bytes public/vite.svg | 1 - src/App.tsx | 111 ------- src/api.ts | 231 ++++++++++++-- src/assets/react.svg | 1 - src/components/button.tsx | 16 +- src/components/cost.tsx | 16 +- src/components/icon.tsx | 26 ++ src/components/login-button.tsx | 5 +- src/components/modal.tsx | 85 ++++++ src/components/os-image-name.tsx | 9 + src/components/pay-button.css | 75 ----- src/components/pay-button.tsx | 72 ++--- src/components/qr.tsx | 50 ++++ src/components/vps-actions.tsx | 28 ++ src/components/vps-card.tsx | 19 +- src/components/vps-instance.tsx | 37 +++ src/components/vps-payment.tsx | 58 ++++ src/components/vps-resources.tsx | 31 ++ src/const.ts | 3 + src/hooks/login.tsx | 7 +- src/icons.svg | 20 ++ src/index.css | 10 + src/login.ts | 32 +- src/main.tsx | 48 ++- src/pages/account.tsx | 31 ++ src/pages/home.tsx | 43 +++ src/pages/layout.tsx | 15 + src/pages/order.tsx | 181 +++++++++++ src/pages/vm.tsx | 66 ++++ tsconfig.app.json | 2 +- yarn.lock | 500 +++++++------------------------ 34 files changed, 1147 insertions(+), 697 deletions(-) create mode 100644 public/logo.jpg delete mode 100644 public/vite.svg delete mode 100644 src/App.tsx delete mode 100644 src/assets/react.svg create mode 100644 src/components/icon.tsx create mode 100644 src/components/modal.tsx create mode 100644 src/components/os-image-name.tsx delete mode 100644 src/components/pay-button.css create mode 100644 src/components/qr.tsx create mode 100644 src/components/vps-actions.tsx create mode 100644 src/components/vps-instance.tsx create mode 100644 src/components/vps-payment.tsx create mode 100644 src/components/vps-resources.tsx create mode 100644 src/icons.svg create mode 100644 src/pages/account.tsx create mode 100644 src/pages/home.tsx create mode 100644 src/pages/layout.tsx create mode 100644 src/pages/order.tsx create mode 100644 src/pages/vm.tsx diff --git a/index.html b/index.html index 276b23e..2d84bb8 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,7 @@ - + @@ -14,7 +14,7 @@ - + LNVPS @@ -27,6 +27,5 @@
- diff --git a/package.json b/package.json index 2809d8a..ff42ec2 100644 --- a/package.json +++ b/package.json @@ -10,11 +10,15 @@ "preview": "vite preview" }, "dependencies": { + "@scure/base": "^1.2.1", "@snort/shared": "^1.0.17", - "@snort/system": "^1.5.1", - "@snort/system-react": "^1.5.1", + "@snort/system": "^1.5.7", + "@snort/system-react": "^1.5.7", + "classnames": "^2.5.1", + "qr-code-styling": "^1.8.4", "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "react-router-dom": "^7.0.1" }, "devDependencies": { "@eslint/js": "^9.8.0", diff --git a/public/logo.jpg b/public/logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4c14a80b71036e03998291e19535094ec420eead GIT binary patch literal 34542 zcmb5VcUV(h_bs{;0tOHX2+}1q0a1}AMTCHK=`CO(uT%k*CRGAK5$RQwDhNoI2#EBc zFTIO2K?J4u-a-J+;`{Eozw^(z&vU~A>}2oky~2v^5|Y41!?b z2l_h+`=NdFCeF~M2JL1S6KF^j{0k3~+7t^a$hcyjFh z#Ptcd#_;6W-PHIdIJO1HA`bt@vGxCPY~$*Fa(yJYM)A=@&y#1JyiQtt+U~KL3HXl$ zzub@qWDIFSswe#i@4?F@3xec!Ac!jVe?GHLhM>w95X3(EKc5L^LJ(sx1by!PpU?hh zOkD4~-~Z3;5a2J|-X4NB^B{=95`xZtgCM#)|2YT#{9oFJ2B)||yIjDJ1N0EGgV2yR z^cb>%u7D#+=qe-uUHkhJx(Oj^Xim{kBTt<=MN5mMJI%synx6hN2lH7*79LLC^E{l~ z+-Sio!f5_W0^HmpvZ9x+N=Qpf^9sw~kdsurA|)+(auXOWE$wOg)9efk?2>%ke3Jja zUw^+qOh^QTfT&>n5S$4{#RU7?1Udi%RtTqp{qN3#;fJ3>Qc=?&z*&71Fi$v)j*5nw z3Whj!ii#S%U1y>~Q1eNs7~OaKL!+# z^02d#{P%<6+)8N#-u)egPJ;%+nW&f`WoWMA<>JrPJKYL%120~?E@p|pypWxX&~P30 z3)c8hQ)zSf3IC;s_r@6Zjq|OpZ1#W^kFK?bAnD8&UOOhHka2~-(Ce5aj?mc>&K#&Q zvUl+5DAdC_usZC2TiMm#Cgp1;Rq+IMz%B{H-uPDWh|?Lr2=#E2xrLwXI%jP@m%~3L zUiluB9BogLnf=cR`3kMk(@kzkjV|s6N$ibQ1&ZLtdmnzV|2({WD08S1V=v6@nmnR> zGlXmHdB8uX|Ln>IBve45bJ069qlQuvpY2_O-sVt(BE`Q^Mz zI&OISYF?mB2v_?SRV&lZ>hP^u149Rfe|-(Ebdz60t>wLMY+lk%4?!HE2gSlqD!W8l z%g@*MaDJXRcRcm09`<6x0)L(7IIoNGJXZiu%D?+E-A{vJV$Ls_GrhE0K|4@|e0Yxo zT?xF>%9v+=*l8%SymjNkej0lGqs)c#_)N!rC|hvgo^r0bD&>UE5WpAYC+^w{^NQZt{r$Z|1{ed znNX@GL0W%iM5R@EcoSO^w`CLO)+IIV99XkWfi+BI*$uX?4VNQO3*mzVLJ@IIY>B=q zP4HXnJFCb^6t9@9e+R-%cfwUvY#BLO$~MN-gx2Q7aI3T$B4P#IES@!SV+3P`qBi6R zTK6{5(H9f7sKPf1nmo5q$jNgOOJmGhjITH`Vyji-!ra&->RlZQCq^K=396kPLW6Tj zLhOEfhI{WHIx{C2y7EM2$Nw^b__92R3f<|Hd1Z3lzGu=2_7z-_$Rs|x>uQr4t0(2;+F)1od9HMZP zpV##4eCd~jhzCOn7rQi9*t^tPCf zV$1J{Wm{AtD;SqMmwhd%YmOO=QG8XIyi#n6`U(WWm>+O0)X%^RrBE;nEw2c|@e~T0 zaHUn1_;kO4>y+V?oA(T=(sAlYlA_&$AsiNU3p>52$N&n&?`>QjJP9saaRQOjG$UO`wCvK4rvk8(R7qp+OvK^>7DMR zsHX&8kB9?2hdr1kGM9L%a%tz;Mt1+Ck{UA6ZB^pL^ovr94*KsZu@ zSSKzNz$tJ_7Mk;Y!oo)?oGU3HYb{oYCvifM7KM1S=dM6fmhf(4J1p|%31kM=jrAX# zkc7#EB4knY6e5crntFntU{w}{hGD=O#n}|~mm$s!z;LQp$Z%3l@_gJ z#MTW7FQU-j5ZBUijIlJN9q=zcNf91gpB6D&IG!MBDI|(TFo!+tim1} z8VWR_kpzB9QbQrEO7n4A3TORwm^P$2w@6^}ZijItPRKykWSZ;*wDBDkB-THLl-WHY zZAi7HyYi>CN(SG&Ps%+h-~-TX$%;(P}us>y

gt8H1d5%8;q^ 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 literal 0 HcmV?d00001 diff --git a/public/vite.svg b/public/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx deleted file mode 100644 index d9f48df..0000000 --- a/src/App.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import { SnortContext } from "@snort/system-react"; -import { CostInterval, DiskType, MachineSpec } from "./api"; -import VpsCard from "./components/vps-card"; -import { GiB, NostrProfile } from "./const"; -import { NostrSystem } from "@snort/system"; -import Profile from "./components/profile"; -import LoginButton from "./components/login-button"; - -import pgp from "../public/lnvps.asc?url"; - -const Offers: Array = [ - { - id: "2x2x80", - location: "IE", - active: true, - cpu: 2, - ram: 2 * GiB, - disk: { - type: DiskType.SSD, - size: 80 * GiB, - }, - cost: { - interval: CostInterval.Month, - count: 3, - currency: "EUR", - }, - }, - { - id: "4x4x160", - location: "IE", - active: true, - cpu: 4, - ram: 4 * GiB, - disk: { - type: DiskType.SSD, - size: 160 * GiB, - }, - cost: { - interval: CostInterval.Month, - count: 5, - currency: "EUR", - }, - }, - { - id: "8x8x400", - location: "IE", - active: true, - cpu: 8, - ram: 8 * GiB, - disk: { - type: DiskType.SSD, - size: 400 * GiB, - }, - cost: { - interval: CostInterval.Month, - count: 12, - currency: "EUR", - }, - }, -]; - -const system = new NostrSystem({ - automaticOutboxModel: false, - buildFollowGraph: false, -}); -[ - "wss://relay.snort.social/", - "wss://relay.damus.io/", - "wss://relay.nostr.band/", - "wss://nos.lol/", -].forEach((a) => system.ConnectToRelay(a, { read: true, write: true })); - -export default function App() { - return ( - -

-
- LNVPS - -
- -

VPS Offers

-
-
- {Offers.map((a) => ( - - ))} -
- - - All VPS come with 1x IPv4 and 1x IPv6 address and unmetered - traffic - -
- - Please email sales after - paying the invoice with your order id, desired OS and ssh key. - - You can also find us on nostr: - - - -
- Speedtest | PGP -
-
-
-
- - ); -} diff --git a/src/api.ts b/src/api.ts index f8d1421..bc119b5 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,30 +1,215 @@ -export interface MachineSpec { - id: string; - active: boolean; +import { EventKind, EventPublisher } from "@snort/system"; +import { base64 } from "@scure/base"; + +export interface ApiResponseBase { + error?: string; +} + +export type ApiResponse = ApiResponseBase & { + data: T; +}; + +export interface VmCostPlan { + id: number; + name: string; + created: Date; + amount: number; + currency: "EUR" | "BTC"; + interval_amount: number; + interval_type: string; +} + +export interface VmHostRegion { + id: number; + name: string; + enabled: boolean; +} + +export interface VmTemplate { + id: number; + name: string; + enabled: boolean; + created: Date; + expires?: Date; cpu: number; - ram: number; - disk: { - type: DiskType; - size: number; - }; - cost: { - interval: CostInterval; - count: number; - currency: CostCurrency; - }; - location: string; + memory: number; + disk_size: number; + disk_type: string; + disk_interface: string; + cost_plan_id: number; + region_id: number; + + cost_plan?: VmCostPlan; + region?: VmHostRegion; } -export enum DiskType { - HDD, - SSD, +export interface VmStatus { + state: "running" | "stopped"; + cpu_usage: number; + mem_usage: number; + uptime: number; + net_in: number; + net_out: number; + disk_write: number; + disk_read: number; } -export enum CostInterval { - Hour, - Day, - Month, - Year, +export interface VmInstance { + id: number; + host_id: number; + user_id: number; + image_id: number; + template_id: number; + ssh_key_id: number; + created: Date; + expires: Date; + cpu: number; + memory: number; + disk_size: number; + disk_id: number; + status?: VmStatus; + + template?: VmTemplate; + image?: VmOsImage; + ssh_key?: UserSshKey; } -export type CostCurrency = "EUR" | "USD" | "BTC"; +export interface VmOsImage { + id: number; + distribution: string; + flavour: string; + version: string; + release_date: string; +} + +export interface UserSshKey { + id: number; + name: string; +} + +export interface VmPayment { + id: string; + invoice: string; + created: string; + expires: string; + amount: number; + is_paid: boolean; +} + +export class LNVpsApi { + constructor( + readonly url: string, + readonly publisher: EventPublisher | undefined, + ) {} + + async listVms() { + const { data } = await this.#handleResponse>>( + await this.#req("/api/v1/vm", "GET"), + ); + return data; + } + + async getVm(id: number) { + const { data } = await this.#handleResponse>( + await this.#req(`/api/v1/vm/${id}`, "GET"), + ); + return data; + } + + async listOffers() { + const { data } = await this.#handleResponse>>( + await this.#req("/api/v1/vm/templates", "GET"), + ); + return data; + } + + async listOsImages() { + const { data } = await this.#handleResponse>>( + await this.#req("/api/v1/image", "GET"), + ); + return data; + } + + async listSshKeys() { + const { data } = await this.#handleResponse>>( + await this.#req("/api/v1/ssh-key", "GET"), + ); + return data; + } + + async addSshKey(name: string, key: string) { + const { data } = await this.#handleResponse>( + await this.#req("/api/v1/ssh-key", "POST", { + name, + key_data: key, + }), + ); + return data; + } + + async orderVm(template_id: number, image_id: number, ssh_key_id: number) { + const { data } = await this.#handleResponse>( + await this.#req("/api/v1/vm", "POST", { + template_id, + image_id, + ssh_key_id, + }), + ); + return data; + } + + async renewVm(vm_id: number) { + const { data } = await this.#handleResponse>( + await this.#req(`/api/v1/vm/${vm_id}/renew`, "GET"), + ); + return data; + } + + async paymentStatus(id: string) { + const { data } = await this.#handleResponse>( + await this.#req(`/api/v1/payment/${id}`, "GET"), + ); + return data; + } + + async #handleResponse(rsp: Response) { + if (rsp.ok) { + return (await rsp.json()) as T; + } else { + const text = await rsp.text(); + try { + const obj = JSON.parse(text) as ApiResponseBase; + throw new Error(obj.error); + } catch { + throw new Error(text); + } + } + } + + async #req(path: string, method: "GET" | "POST" | "DELETE", body?: object) { + const auth = async (url: string, method: string) => { + const auth = await this.publisher?.generic((eb) => { + return eb + .kind(EventKind.HttpAuthentication) + .tag(["u", url]) + .tag(["method", method]); + }); + if (auth) { + return `Nostr ${base64.encode( + new TextEncoder().encode(JSON.stringify(auth)), + )}`; + } + }; + + const u = `${this.url}${path}`; + return await fetch(u, { + method, + body: body ? JSON.stringify(body) : undefined, + headers: { + accept: "application/json", + "content-type": "application/json", + authorization: (await auth(u, method)) ?? "", + }, + }); + } +} diff --git a/src/assets/react.svg b/src/assets/react.svg deleted file mode 100644 index 6c87de9..0000000 --- a/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/components/button.tsx b/src/components/button.tsx index 9662b3b..21e5882 100644 --- a/src/components/button.tsx +++ b/src/components/button.tsx @@ -1,15 +1,25 @@ +import classNames from "classnames"; import { forwardRef, HTMLProps } from "react"; export type AsyncButtonProps = { - onClick?: (e: React.MouseEvent) => Promise; + onClick?: (e: React.MouseEvent) => Promise | void; } & Omit, "type" | "ref" | "onClick">; const AsyncButton = forwardRef( - function AsyncButton(props, ref) { + function AsyncButton({ className, ...props }, ref) { + const hasBg = className?.includes("bg-"); return (