import { FocusedObject, lens } from "functional"; import { Gateway } from "./Gateway.js"; import { Host, IHost, TemporaryHost } from "./Host.js"; import { Machine, MachineInit, SinglePortMachineInit } from "../Independents/Machine.js"; import { Datagram, Request, RequestStub, ResponseInit, Response } from "../Independents/Datagram/datagram-types.js"; import { TGateway, THost, THostType, TVagrant, TBurpastatePersistent, newBurpaPersistent } from "../Independents/DNS/datagram-router.js" import { Vagrant } from "./Host.js"; import { HostName, HostNameBase, HostNameTemplate } from "../Independents/DNS/HostName.js"; import { RandomString } from "../Independents/RememberableNames/random-host-name.js" import { Jsonable, THostExtension } from "./types.js"; import { BurpaDriver } from "./types.js"; import { RouteDatagram } from "./datagram-routing.js"; import { CRequest, DatagramShort } from "../Independents/Datagram/datagram.js"; import { burpaFetchSubscribed } from "./fetch.js"; import { CBurpaError } from "../Independents/BurpaError.js"; export type DatagramStub = { from: string, to: string, data: string } export type RequestTrailRecord = DatagramStub | { connect: string } export type TBurpaState = { persistent: TBurpastatePersistent, transient: { inboxes: { [key: string]: Datagram[] }, todos: (() => void)[], resources: { [key: string]: any }, hostExtensions: { [key: string]: THostExtension }, host502: THost, // LocalHosts: Host[], driver: BurpaDriver, } } // const localHostName = newVagrantId(); // const localGatewayName = newGatewayId(); export function newVagrantId() { return `vg66${RandomString()}` } export function newGatewayId() { return `gw55${RandomString()}` } // export let LocalHosts: Host[] = []; export function newBurpaState(): TBurpaState { let ny: TBurpaState = { persistent: newBurpaPersistent(), transient: { todos: [], inboxes: {}, resources: {}, hostExtensions: {}, host502: newHost("vagrant"), driver: null as unknown as BurpaDriver, } } return ny; } function newHost(type: THostType): THost { if (type == "gateway") { let gw: TGateway = { type, nextReqno: 0, owned: {}, burpaport: ["burpasys"], upstream: [] } return gw; }; let vg: TVagrant = { type: type as "vagrant", nextReqno: 0, owned: {}, burpaport: ["burpasys"] } return vg; } let cnt = 0; export class CBurpastateWrapper extends FocusedObject { get localGateway(): THost { return this.self.persistent.hosts[this.self.persistent.localGateway]; } get localHost(): THost { return this.self.persistent.hosts[this.self.persistent.localHost ?? ""]; } save() { localStorage.setItem("burpastate.json", JSON.stringify(this.whole.persistent, null, 3)) } async spawnHost(hostTemplate: string, init: MachineInit, secret?: string): Promise { let templateForNewHost = this.hostNameTemplate(hostTemplate); let lgw = this.LocalGateway(); let gateway = await lgw.registerAtUpstream(templateForNewHost.nameTemplate); this.whole.persistent.aliases[gateway] = lgw.hostId; let request: Request = new CRequest({ port: "burpasys", from: this.LocalGateway().alias.name, to: templateForNewHost.parent, method: "POST", path: "/children", body: { template: templateForNewHost.reconstructed, gateway: gateway, secret: secret ?? "" } }); let response = await this.LocalGateway().sendRequest(request).current; if (response.status == 201) { updateRoots(this, response.body); let host = response.body.host as string; this.addVagrant_Old(host, init); let h = this.hostName(host); if (response.body.inbox) { this.log("GetInbox", `${host}`) await burpaFetchSubscribed(this, `${h.parent}/empty-inbox/${host}`, { method: "POST", port: "burpasys" }) } return host; } throw new CBurpaError(`Cannot spawn "${hostTemplate}" as host "${response.body.closest}" has no registration of host "${request.to}".`, { host: request.to, closest: response.body.closest }); //return Promise.resolve(null); } // processTodos() { // setTimeout(() => { // for (let todo of this.whole.transient.todos) { // console.log("Processing TODO", ++cnt); // todo(); // } // this.whole.transient.todos = []; // }, 200); // } hostNameTemplate(template: string): HostNameTemplate { let ret = new HostNameTemplate(template); return this.resolveConstants(ret) as HostNameTemplate; } resolveConstants(ret: HostNameBase): HostNameBase { ret.resolveConstants(() => { let localHost = this.whole.persistent.localHost; if (!localHost) { localHost = newVagrantId(); this.whole.persistent.localHost = localHost; this.addVagrant_Old(localHost, {}); } return localHost; }, this.whole.persistent.localGateway); return ret; } hostName(name: string): HostName { let ret = new HostName(name); return this.resolveConstants(ret) as HostName; } getHost502(action: string, responseFn: (request: Request) => ResponseInit | Promise): Host { let h = new TemporaryHost(this.whole, BurpaStateLens.transient.host502); h.action = action; let m: SinglePortMachineInit = { onRequest: responseFn } h.machine = new Machine({ ports: { "burpasys": m, "default": m } }); return h; } log(type: string, text: string) { type = (`${type} `).substring(0, 10); text = text.substring(0, 200); this.whole.persistent.log.push(`${type}:${text}`); if (this.whole.persistent.log.length > 120) { this.whole.persistent.log = ["Truncated:Log was truncated"].concat(this.whole.persistent.log.splice(-100)); console.log("loglength", this.whole.persistent.log.length); } //console.log(this.whole.persistent.log.slice(-1)[0]) } // addGateway(id: string): THost { // //let id = newGatewayId(); // let ny = newHost("gateway"); // ny.aliases = [id]; // this.self.persistent.hosts[id] = ny; // return ny; // } addGateway_Old(id: string): Gateway { //let id = newGatewayId(); let g = newHost("gateway"); g.burpaport = ["default", "burpasys"] //debugger this.whole.persistent.aliases[id] = id; this.whole.persistent.hosts[id] = g; let gw = new Gateway(this.whole, BurpaStateLens.persistent.hosts[this.whole.persistent.localGateway]); this.log("Spawned", id); return gw; } getHost(id: string): Host { let h = this.whole.persistent.hosts[id]; let ptr = BurpaStateLens.persistent.hosts[id]; if (h.type == "gateway") { return new Gateway(this.whole, ptr); } return new Vagrant(this.whole, ptr); } // addVagrant(): THost { // let id = newVagrantId(); // let ny = newHost("vagrant"); // this.self.persistent.hosts[id] = ny; // return ny; // } addVagrant_Old(id: string, init: MachineInit): Vagrant { //debugger let v1 = newHost("vagrant"); //let id = newVagrantId(); this.whole.persistent.hosts[id] = v1; this.whole.persistent.aliases[id] = id; let v = new Vagrant(this.whole, BurpaStateLens.persistent.hosts[id]); v.machine.install(init); // let transient = this.whole.transient; // transient.LocalHosts = transient.LocalHosts.reduce((arr, curr, i) => { // if (!curr.hasAlias(v.alias.name)) { // arr.push(curr); // } // return arr; // }, [] as Host[]) // transient.LocalHosts.push(v); // let info = JSON.stringify(transient.LocalHosts.map(v => v.getInfo())); // console.log("STORING VAGRANTS", info) // localStorage.setItem("Burpa.Vagrants", info); // return v; this.log("Spawned", id); return v; } LocalGateway(): Gateway { const gw = this.findLocalHost(this.whole.persistent.localGateway); if (!gw) { throw new Error("no gateway"); } return gw as Gateway; } get hosts() { let keys = Object.keys(this.whole.persistent.hosts); return keys.map(key => this.getHost(key)); } getSerialId(): string { let i = localStorage.getItem("burpa.serialId"); if (!i) { i = RandomString(); localStorage.setItem("burpa.serialId", i); } return i; } debugLocalHosts() { let hosts = this.hosts.concat([this.LocalGateway()]); return hosts.map(e => e.aliases2.join(",")).join(";"); } debugTransports() { let lgw = this.LocalGateway(); let Transports = lgw.extension.downstreamGateways.map(e => { return e.hostTemplate.name }) return Transports.join(","); } serializeTransient(): Jsonable { return JSON.parse(JSON.stringify(this.whole.transient)); } findLocalHost(host: string): Host | null { for (let local of this.hosts) { if (local.hostId == host) { return local; } for (let alias of local.aliases2) { if (alias == host) { return local; } } } return null; } isLocalOrOwnedHost(host: string): boolean { let hosts = this.whole.persistent.hosts; for (let local in hosts) { let l = hosts[local]; if (local == host) { return true; } for (let alias in l.owned) { if (alias == host) { return true; } } } return false; } } export const BurpaStateLens = lens(); //export const Burpa = new CBurpastateWrapper(Burpastate, BurpaStateLens); export function updateRoots(state: CBurpastateWrapper, body: any) { //console.log("UPDATE ROOTS", body); if (body.rootGateway) { state.whole.persistent.rootGateway = body.rootGateway; } if (body.rootHost) { state.whole.persistent.rootHost = body.rootHost; } }