From a17e461bf4bcb7a30b0229937f0d2f5a02833e76 Mon Sep 17 00:00:00 2001 From: Michael Quigley Date: Mon, 3 Mar 2025 13:44:07 -0500 Subject: [PATCH] basic share/unshare sdk functions (#893) --- sdk/nodejs1/examples/share/src/index.ts | 12 +++ sdk/nodejs1/sdk/src/environment.ts | 59 ++++++++++- sdk/nodejs1/sdk/src/share.ts | 133 ++++++++++++++++++++++++ 3 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 sdk/nodejs1/sdk/src/share.ts diff --git a/sdk/nodejs1/examples/share/src/index.ts b/sdk/nodejs1/examples/share/src/index.ts index 2345a714..cf76d714 100644 --- a/sdk/nodejs1/examples/share/src/index.ts +++ b/sdk/nodejs1/examples/share/src/index.ts @@ -1,5 +1,6 @@ import {Command} from "commander"; import {loadRoot} from "@openziti/zrok/dist/environment"; +import {createShare, deleteShare, PROXY_BACKEND_MODE, PUBLIC_SHARE_MODE, ShareRequest} from "@openziti/zrok/dist/share"; const program = new Command(); @@ -11,6 +12,17 @@ program .action(() => { let root = loadRoot(); console.log("root.isEnabled", root.isEnabled()); + let req = new ShareRequest(PUBLIC_SHARE_MODE, PROXY_BACKEND_MODE, "http://localhost:8000"); + req.frontends = ["public"]; + createShare(root, req) + .then(shr => { + console.log(shr); + deleteShare(root, shr); + }) + .catch(ex => { + console.log("exception", ex); + }); + }); program.parse(process.argv); \ No newline at end of file diff --git a/sdk/nodejs1/sdk/src/environment.ts b/sdk/nodejs1/sdk/src/environment.ts index 248db9cf..dbeed03e 100644 --- a/sdk/nodejs1/sdk/src/environment.ts +++ b/sdk/nodejs1/sdk/src/environment.ts @@ -1,19 +1,58 @@ import {environmentFile, metadataFile} from "./paths"; import * as fs from "node:fs"; +import {Configuration} from "./api"; const ENVIRONMENT_V = "v0.4"; export class Root { metadata: Metadata; + config: Config|undefined; environment: Environment|undefined; constructor(metadata: Metadata, environment: Environment|undefined) { this.metadata = metadata; + this.config = undefined; this.environment = environment; } + public apiConfiguration = (): Configuration => { + let apiEndpoint = this.apiEndpoint(); + if(this.isEnabled()) { + return new Configuration({basePath: apiEndpoint.endpoint + "/api/v1", apiKey: this.environment?.accountToken}) + } else { + return new Configuration({basePath: apiEndpoint.endpoint + "/api/v1"}); + } + } + + public apiEndpoint = (): ApiEndpoint => { + let endpoint = "https://api-v1.zrok.io"; + let from = "binary"; + + if(this.config?.apiEndpoint !== "") { + endpoint = this.config?.apiEndpoint!; + from = "config"; + } + + let env = process.env.ZROK_API_ENDPOINT; + if(env != null) { + endpoint = env; + from = "ZROK_API_ENDPOINT"; + } + + if(this.isEnabled()) { + endpoint = this.environment?.apiEndpoint!; + from = "env"; + } + + return new ApiEndpoint(endpoint, from); + } + + public hasConfig = (): boolean => { + return this.config !== undefined; + } + public isEnabled = (): boolean => { - return this.environment != undefined; + return this.environment !== undefined; } } @@ -39,6 +78,24 @@ export class Environment { } } +export class Config { + apiEndpoint: string; + + constructor(apiEndpoint: string) { + this.apiEndpoint = apiEndpoint; + } +} + +export class ApiEndpoint { + endpoint: string; + from: string; + + constructor(endpoint: string, from: string) { + this.endpoint = endpoint; + this.from = from; + } +} + export const loadRoot = (): Root => { if(rootExists()) { let metadata = loadMetadata(); diff --git a/sdk/nodejs1/sdk/src/share.ts b/sdk/nodejs1/sdk/src/share.ts new file mode 100644 index 00000000..74f1f592 --- /dev/null +++ b/sdk/nodejs1/sdk/src/share.ts @@ -0,0 +1,133 @@ +import {Root} from "./environment"; +import { + ShareApi, + ShareRequest as ApiShareRequest, + ShareRequestBackendModeEnum, + ShareRequestShareModeEnum, + UnshareRequest +} from "./api"; + +export type ShareMode = string; +export const PRIVATE_SHARE_MODE: ShareMode = "private"; +export const PUBLIC_SHARE_MODE: ShareMode = "public"; + +export type BackendMode = string; +export const PROXY_BACKEND_MODE: BackendMode = "proxy"; +export const TCP_TUNNEL_BACKEND_MODE: BackendMode = "tcpTunnel"; +export const UDP_TUNNEL_BACKEND_MODE: BackendMode = "udpTunnel"; + +export type PermissionMode = string; +export const OPEN_PERMISSION_MODE = "open"; +export const CLOSED_PERMISSION_MODE = "closed"; + +export class ShareRequest { + reserved: boolean; + uniqueName: string|undefined; + backendMode: BackendMode; + shareMode: ShareMode; + target: string; + frontends: string[]|undefined; + basicAuth: string[]|undefined; + oauthProvider: string|undefined; + oauthEmailAddressPatterns: string[]|undefined; + oauthAuthorizationCheckInterval: string|undefined; + permissionMode: PermissionMode; + accessGrants: string[]|undefined; + + constructor(shareMode: ShareMode, backendMode: BackendMode, target: string) { + this.reserved = false; + this.uniqueName = undefined; + this.backendMode = backendMode; + this.shareMode = shareMode; + this.target = target; + this.frontends = undefined; + this.basicAuth = undefined; + this.oauthProvider = undefined; + this.oauthEmailAddressPatterns = undefined; + this.oauthAuthorizationCheckInterval = undefined; + this.permissionMode = CLOSED_PERMISSION_MODE; + this.accessGrants = undefined; + } +} + +export class Share { + shareToken: string; + frontendEndpoints: string[]|undefined; + + constructor(shareToken: string, frontendEndpoints: string[]|undefined) { + this.shareToken = shareToken; + this.frontendEndpoints = frontendEndpoints; + } +} + +export const createShare = async (root: Root, request: ShareRequest): Promise => { + if(!root.isEnabled()) { + throw new Error("environment is not enabled; enable with 'zrok enable' first!"); + } + + let req: ApiShareRequest; + switch(request.shareMode) { + case PRIVATE_SHARE_MODE: + req = toPrivateApiShareRequest(root, request); + break; + case PUBLIC_SHARE_MODE: + req = toPublicApiShareRequest(root, request); + break; + default: + throw new Error("unknown share mode '" + request.shareMode + "'"); + } + + let shr = await new ShareApi(root.apiConfiguration()).share({body: req}) + .catch(resp => { + throw new Error("unable to create share: " + resp); + }); + + return new Share(shr.shareToken!, shr.frontendProxyEndpoints); +} + +export const deleteShare = (root: Root, shr: Share): void => { + if(!root.isEnabled()) { + throw new Error("environment is not enable; enable with 'zrok enable' first!"); + } + let req: UnshareRequest = { + envZId: root.environment?.zId!, + shareToken: shr.shareToken + }; + new ShareApi(root.apiConfiguration()).unshare({body: req}) + .catch(resp => { + throw new Error("unable to delete share: " + resp); + }); +} + +const toPrivateApiShareRequest = (root: Root, request: ShareRequest): ApiShareRequest => { + return { + envZId: root.environment?.zId, + shareMode: ShareRequestShareModeEnum.Private, + backendMode: toApiBackendMode(request.backendMode), + backendProxyEndpoint: request.target, + }; +} + +const toPublicApiShareRequest = (root: Root, request: ShareRequest): ApiShareRequest => { + return { + envZId: root.environment?.zId, + shareMode: ShareRequestShareModeEnum.Public, + frontendSelection: request.frontends, + backendMode: toApiBackendMode(request.backendMode), + backendProxyEndpoint: request.target, + authScheme: "none", + }; +} + +const toApiBackendMode = (mode: BackendMode): ShareRequestBackendModeEnum|undefined => { + switch(mode) { + case PROXY_BACKEND_MODE: + return ShareRequestBackendModeEnum.Proxy; + case TCP_TUNNEL_BACKEND_MODE: + return ShareRequestBackendModeEnum.TcpTunnel; + case UDP_TUNNEL_BACKEND_MODE: + return ShareRequestBackendModeEnum.UdpTunnel; + default: + return undefined; + } +} \ No newline at end of file