Release of v1.0.0

The first release of the 1.0.0 series.
This commit is contained in:
Michael Quigley 2025-03-14 10:11:36 -04:00 committed by GitHub
commit 6640c32b20
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1052 changed files with 71324 additions and 73367 deletions

View File

@ -1,6 +1,5 @@
[flake8]
max-line-length = 120
exclude =
./sdk/python/sdk/zrok/zrok_api/**,
./build/**
sdk/python/src/zrok_api/*
sdk/python/src/test/*

View File

@ -1,4 +1,4 @@
name: build wheels
name: Publish Python Distributions
on:
release:
@ -23,83 +23,86 @@ jobs:
exit 1
fi
build_wheels:
build_distributions:
name: Building Python Distributions
needs: enforce_stable_semver
runs-on: ubuntu-24.04
defaults:
run:
working-directory: sdk/python/sdk/zrok
strategy:
fail-fast: false
matrix:
spec:
- { name: 'linux x86_64', runner: ubuntu-20.04, target: manylinux_2_27_x86_64 }
- { name: 'macOS x86_64', runner: macos-13, target: macosx_10_14_x86_64 }
- { name: 'Windows x86_64', runner: windows-2019, target: win_amd64 }
name: building ${{ matrix.spec.name }}
runs-on: ${{ matrix.spec.runner }}
working-directory: sdk/python/src
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
python-version: '3.13'
cache: 'pip'
- name: Install Python Tools
run: python -m pip install -U pip setuptools
- name: Build distro
- name: Build Python distributions
env:
ZROK_VERSION: ${{ github.event.release.tag_name }}
ZROK_PY_NAME: ${{ vars.ZROK_PY_NAME || null }}
shell: bash
run: |
python setup.py sdist
set -o pipefail
set -o xtrace
# Install build requirements
pip install --upgrade pip
pip install -r build-requirements.txt
# Build source distribution and wheel
python -m build
# List built distributions
ls -lAR ./dist
- uses: actions/upload-artifact@v4
if: startsWith(matrix.spec.name, 'linux')
with:
name: zrok_sdk_${{ matrix.spec.target }}
path: ${{ github.workspace }}/sdk/python/sdk/zrok/dist/*
name: zrok_sdk_distributions
path: sdk/python/src/dist/*
publish-testpypi:
runs-on: ubuntu-20.04
needs: [ build_wheels ]
publish_testpypi:
name: Publish TestPyPI
runs-on: ubuntu-24.04
needs: [ build_distributions ]
permissions:
id-token: write
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: ./dist
path: sdk/python/src/dist
merge-multiple: true
pattern: zrok_sdk_*
pattern: zrok_sdk_distributions
- name: Publish wheels (TestPYPI)
- name: Publish Distributions to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
packages-dir: dist
packages-dir: sdk/python/src/dist
skip-existing: true
verbose: true
publish-pypi:
runs-on: ubuntu-20.04
needs: [ publish-testpypi ]
publish_pypi:
name: Publish PyPI
runs-on: ubuntu-24.04
needs: [ publish_testpypi ]
permissions:
id-token: write
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: ./dist
path: sdk/python/src/dist
merge-multiple: true
pattern: zrok_sdk_*
pattern: zrok_sdk_distributions
- name: Publish wheels (PyPI)
- name: Publish Distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist
packages-dir: sdk/python/src/dist
verbose: true

View File

@ -17,7 +17,7 @@ concurrency:
jobs:
ubuntu-build:
name: Build Linux AMD64 CLI
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
@ -29,20 +29,30 @@ jobs:
- name: setup-node
uses: actions/setup-node@v4
with:
node-version: 18.x
node-version: 20.x
- name: install ui node modules
shell: bash
run: npm install
working-directory: ui
- name: build node ui
- name: build ui
shell: bash
run: npm run build
working-directory: ui
env:
CI: "true"
- name: install agent ui node modules
shell: bash
run: npm install
working-directory: agent/agentUi
- name: build agent ui
shell: bash
run: npm run build
working-directory: agent/agentUi
- name: go install
shell: bash
run: go install -ldflags "-X github.com/openziti/zrok/build.Version=${{ github.ref }} -X github.com/openziti/zrok/build.Hash=${{ github.sha }}" ./...
@ -51,19 +61,6 @@ jobs:
shell: bash
run: go test -v ./...
- name: setup python
uses: actions/setup-python@v3
with:
python-version: '3.10'
- name: python deps
shell: bash
run: python -m pip install -U pip flake8
- name: python lint
shell: bash
run: flake8 sdk/python/sdk/zrok
- name: solve GOBIN
id: solve_go_bin
shell: bash
@ -78,12 +75,61 @@ jobs:
path: ${{ steps.solve_go_bin.outputs.go_bin }}/zrok
if-no-files-found: error
pytest:
name: Test the Python SDK
runs-on: ubuntu-24.04
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
defaults:
run:
working-directory: sdk/python
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
shell: bash
run: |
set -o pipefail
set -o xtrace
python -m pip install --upgrade pip
pip install -r src/requirements.txt
pip install -r src/test-requirements.txt
pip install -r src/build-requirements.txt
pip install -e src/
- name: Test with pytest
shell: bash
run: |
set -o pipefail
set -o xtrace
pytest --cov=zrok_api --verbose src/
- name: Lint the Python SDK
shell: bash
run: |
set -o pipefail
set -o xtrace
flake8 .
# build a release candidate container image for branches named "main" or like "v*"
rc-container-build:
needs: ubuntu-build
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/v')
name: Build Release Candidate Container Image
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- name: Set a container image tag from the branch name
id: slug

View File

@ -12,8 +12,6 @@ jobs:
name: Require Stable Release Semver
if: github.event.action == 'released'
runs-on: ubuntu-24.04
outputs:
version: ${{ steps.parse.outputs.version }}
steps:
- name: Parse Release Version
id: parse
@ -32,7 +30,6 @@ jobs:
if: always()
name: Build for Node-${{ matrix.node_ver }} ${{ matrix.config.target }}/${{ matrix.config.arch }}
runs-on: ${{ matrix.config.os }}
env:
BUILD_NUMBER: ${{ github.run_number }}
AWS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
@ -40,7 +37,7 @@ jobs:
strategy:
matrix:
config:
- { os: ubuntu-20.04, target: "linux", arch: "x64" }
- { os: ubuntu-24.04, target: "linux", arch: "x64" }
node_ver: [ 20 ]
fail-fast: false
@ -55,20 +52,22 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: 'recursive'
- name: Get current zrok repo tag
if: github.event.action == 'released'
id: tag
run: echo "TAG=$(git describe --tags --abbrev=0)" | tee -a $GITHUB_OUTPUT
shell: bash
run: echo "TAG=$(git describe --tags --always)" | tee -a $GITHUB_OUTPUT
- name: Update zrok NodeJS-SDK's package.json version based on current zrok repo git tag
if: github.ref_type == 'tag'
if: github.event.action == 'released'
shell: bash
run: |
cd ${{ runner.workspace }}/${{ github.event.repository.name }}/sdk/nodejs/sdk
cd sdk/nodejs/sdk
npm version ${{ steps.tag.outputs.TAG }} --no-git-tag-version --allow-same-version
- name: Setup .npmrc
if: github.ref_type == 'tag'
if: github.event.action == 'released'
# Setup .npmrc file to prepare for possible publish to npm
uses: actions/setup-node@v4
with:
@ -76,17 +75,26 @@ jobs:
registry-url: 'https://registry.npmjs.org'
- name: Build the zrok NodeJS-SDK
shell: bash
run: |
cd ${{ runner.workspace }}/${{ github.event.repository.name }}/sdk/nodejs/sdk
cd sdk/nodejs/sdk
npm install
npm run build
env:
BUILD_DATE: ${{ steps.date.outputs.date }}
- name: NPM Publish
if: github.ref_type == 'tag'
if: github.event.action == 'released'
shell: bash
run: |
cd ${{ runner.workspace }}/${{ github.event.repository.name }}/sdk/nodejs/sdk
cd sdk/nodejs/sdk
# Check if this is the official repository
if [[ "${{ github.repository_owner }}" == "openziti" ]]; then
echo "Publishing from official repository with @latest tag"
npm publish --access public
else
echo "Publishing from fork/test repository with @canary tag"
npm publish --access public --tag canary
fi
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@ -51,7 +51,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 18.x
node-version: 20.x
- run: npm install
working-directory: ui
@ -61,6 +61,14 @@ jobs:
env:
CI: "true"
- run: npm install
working-directory: agent/agentUi
- run: npm run build
working-directory: agent/agentUi
env:
CI: "true"
- uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
@ -177,7 +185,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 18.x
node-version: 20.x
- run: npm install
working-directory: ui
@ -187,6 +195,14 @@ jobs:
env:
CI: "true"
- run: npm install
working-directory: agent/agentUi
- run: npm run build
working-directory: agent/agentUi
env:
CI: "true"
- uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
@ -222,7 +238,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 18.x
node-version: 20.x
- run: npm install
working-directory: ui
@ -232,6 +248,14 @@ jobs:
env:
CI: "true"
- run: npm install
working-directory: agent/agentUi
- run: npm run build
working-directory: agent/agentUi
env:
CI: "true"
- uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser

10
.gitignore vendored
View File

@ -16,8 +16,6 @@ node_modules/
.docusaurus
.cache-loader
sdk/python/sdk/build/
# Misc
.DS_Store
.env.local
@ -35,3 +33,11 @@ yarn-debug.log*
yarn-error.log*
.obsidian
sdk/nodejs/sdk/dist
# py module artifacts
/sdk/python/src/**/*.egg-info/
/sdk/python/src/**/__pycache__/
/.coverage
/sdk/python/src/dist/

View File

@ -1,5 +1,37 @@
# CHANGELOG
## v1.0.0
MAJOR RELEASE: zrok reaches version 1.0.0!
FEATURE: Completely redesigned web interface ("API Console"). New implementation provides a dual-mode interface supporting an improved visual network navigator and also a "tabular" view, which provides a more traditional "data" view. New stack built using vite, React, and TypeScript (https://github.com/openziti/zrok/issues/724)
FEATURE: New "zrok Agent", a background manager process for your zrok environments, which allows you to easily manage and work with multiple `zrok share` and `zrok access` processes. New `--subordinate` flag added to `zrok share [public|private|reserved]` and `zrok access private` to operate in a mode that allows an Agent to manage shares and accesses (https://github.com/openziti/zrok/issues/463)
FEATURE: New "zrok Agent UI" a web-based user interface for the zrok Agent, which allows creating and releasing shares and accesses through a web browser. This is just an initial chunk of the new Agent UI, and is considered a "minimum viable" version of this interface (https://github.com/openziti/zrok/issues/221)
FEATURE: `zrok share [public|private|reserved]` and `zrok access private` now auto-detect if the zrok Agent is running in an environment and will automatically service share and access requests through the Agent, rather than in-process if the Agent is running. If the Agent is not running, operation remains as it was in `v0.4.x` and the share or access is handled in-process. New `--force-agent` and `--force-local` flags exist to skip Agent detection and manually select an operating mode (https://github.com/openziti/zrok/issues/751)
FEATURE: `zrok access private` supports a new `--auto` mode, which can automatically find an available open address/port to bind the frontend listener on. Also includes `--auto-address`, `--auto-start-port`, and `--auto-end-port` features with sensible defaults. Supported by both the agent and local operating modes (https://github.com/openziti/zrok/issues/780)
FEATURE: `zrok rebase` commands (`zrok rebase apiEndpoint` and `zrok rebase accountToken`) allows "rebasing" an enabled environment onto a different API endpoint or a different account token. This is useful for migrating already-enabled environments between endpoints supporting different zrok versions, and is also useful when regenerating an account token (https://github.com/openziti/zrok/issues/869, https://github.com/openziti/zrok/issues/897)
FEATURE: `zrok test canary` CLI tree replaces the old `zrok test loop` tree; new `zrok test canary public-proxy` and `zrok test canary private-proxy` provide modernized, updated versions of what the `zrok test loop` commands used to do. This new approach will serve as the foundation for all future zrok testing infrastructure (https://github.com/openziti/zrok/issues/771)
FEATURE: New `/api/v1/versions` endpoint to return comprehensive, full stack version information about the deployed service instance. Currently only returns a single `controllerVersion` property (https://github.com/openziti/zrok/issues/881)
CHANGE: The default API URL for `v1.0.x` zrok clients is now `https://api-v1.zrok.io` (instead of the older `https://api.zrok.io`). The zrok.io deployment will now be maintaining version-specific DNS for versioned API endpoints.
CHANGE: Refactored API implementation. Cleanup, lint removal, additional data elements added, unused data removed (https://github.com/openziti/zrok/issues/834)
CHANGE: Deprecated the `passwords` configuration stanza. The zrok controller and API console now use a hard-coded set of (what we believe to be) reasonable assumptions about password quality (https://github.com/openziti/zrok/issues/834)
CHANGE: The protocol for determining valid client versions has been changed. Previously a zrok client would do a `GET` against the `/api/v1/version` endpoint and do a local version string comparison (as a normal precondition to any API call) to see if the controller version matched. The protocol has been amended so that any out-of-date client using the old protocol will receive a version string indicating that they need to uprade their client. New clients will do a `POST` against the `/api/v1/clientVersionCheck` endpoint, posting their client version, and the server will check for compatibility. Does not change the security posture in any significant way, but gives more flexibility on the server side for managing client compatibility. Provides a better, cleared out-of-date error message for old clients when accessing `v1.0.0`+ (https://github.com/openziti/zrok/issues/859)
CHANGE: The Node.js SDK is now generated by `openapi-generator` using the `typescript-fetch` template. Examples and SDK components updated to use the `v1.0.0` API and generated client (https://github.com/openziti/zrok/issues/893)
CHANGE: The Python SDK is now generated by `openapi-generator` and requires a newer `urllib3` version 2.1.0. The published Python module, `zrok`, inherits the dependencies of the generated packages (https://github.com/openziti/zrok/issues/894)
## v0.4.49
FIX: Release artifacts now include a reproducible source archive. The archive's download URL is now used by the Homebrew formula when building from source instead of the archive generated on-demand by GitHub (https://github.com/openziti/zrok/issues/858).

View File

@ -1,12 +1,12 @@
![zrok](docs/images/zrok_cover.png)
![zrok logo](docs/images/zrok_cover.png)
## Your Secure Internet Sharing Perimeter
`zrok` is a next-generation, peer-to-peer sharing platform built on top of [OpenZiti](https://docs.openziti.io/docs/learn/introduction/), a programmable zero-trust network overlay. `zrok` is a _Ziti Native Application_.
`zrok` facilitates sharing resources both publicly and privately. Public sharing allows you to share `zrok` resources with non-`zrok` users over the public internet. Private sharing allows you to directly share your resources peer-to-peer with other `zrok` users without changing your security or firewall settings.
`zrok` facilitates both public and private sharing. Public sharing allows you to share securely with non-`zrok` users over the public internet. Private sharing allows you to directly share peer-to-peer with other `zrok` users. No security or firewall changes are required for either type of sharing. No inbound connectivity is required. The OpenZiti overlay provides peer-to-peer connectivity without IP addresses, and employs end-to-end encryption for world-class security.
Like other offerings in this space, `zrok` allows users to share tunnels for HTTP, TCP and UDP network resources. `zrok` additionally allows users to easily and rapidly share files, web content, and custom resources in a peer-to-peer manner.
`zrok` is an extensible platform for sharing. Initially we're targeting technical users. Super-simple sharing for end users is planned and in the backlog.
Like other offerings in this space, `zrok` allows users to create tunnels for HTTP, TCP and UDP network resources. `zrok` additionally allows users to easily and rapidly share files, web content, and custom resources in a peer-to-peer manner.
![zrok Web Console](docs/images/zrok_web_console.png)
@ -14,8 +14,8 @@ Like other offerings in this space, `zrok` allows users to share tunnels for HTT
You can be up and sharing using the `zrok.io` service in minutes. Here is a synopsis of what's involved:
* Download the binary for your platform [here](https://github.com/openziti/zrok/releases/latest)
* `zrok invite` to create an account with the service
* [Install the package or download the binary for your platform](https://docs.zrok.io/docs/guides/install/).
* `zrok invite` to create an account with the service (use the [NetFoundry hosted zrok.io service](https://docs.zrok.io/docs/getting-started/))
* `zrok enable` to enable your shell environment for sharing with the service
### And then... sharing...

30
agent/access.go Normal file
View File

@ -0,0 +1,30 @@
package agent
import (
"github.com/michaelquigley/pfxlog"
"github.com/openziti/zrok/agent/proctree"
"github.com/openziti/zrok/cmd/zrok/subordinate"
)
type access struct {
frontendToken string
token string
bindAddress string
autoMode bool
autoAddress string
autoStartPort uint16
autoEndPort uint16
responseHeaders []string
process *proctree.Child
sub *subordinate.MessageHandler
agent *Agent
}
func (a *access) monitor() {
if err := proctree.WaitChild(a.process); err != nil {
pfxlog.ChannelLogger(a.token).Error(err)
}
a.agent.rmAccess <- a
}

93
agent/accessPrivate.go Normal file
View File

@ -0,0 +1,93 @@
package agent
import (
"context"
"errors"
"fmt"
"github.com/openziti/zrok/agent/agentGrpc"
"github.com/openziti/zrok/agent/proctree"
"github.com/openziti/zrok/cmd/zrok/subordinate"
"github.com/openziti/zrok/environment"
"github.com/sirupsen/logrus"
"os"
)
func (i *agentGrpcImpl) AccessPrivate(_ context.Context, req *agentGrpc.AccessPrivateRequest) (*agentGrpc.AccessPrivateResponse, error) {
root, err := environment.LoadRoot()
if err != nil {
return nil, err
}
if !root.IsEnabled() {
return nil, errors.New("unable to load environment; did you 'zrok enable'?")
}
accCmd := []string{os.Args[0], "access", "private", "--subordinate", "-b", req.BindAddress, req.Token}
if req.AutoMode {
accCmd = append(accCmd, "--auto", "--auto-address", req.AutoAddress, "--auto-start-port", fmt.Sprintf("%v", req.AutoStartPort))
accCmd = append(accCmd, "--auto-end-port", fmt.Sprintf("%v", req.AutoEndPort))
}
logrus.Info(accCmd)
acc := &access{
token: req.Token,
bindAddress: req.BindAddress,
autoMode: req.AutoMode,
autoAddress: req.AutoAddress,
autoStartPort: uint16(req.AutoStartPort),
autoEndPort: uint16(req.AutoEndPort),
responseHeaders: req.ResponseHeaders,
sub: subordinate.NewMessageHandler(),
agent: i.agent,
}
acc.sub.MessageHandler = func(msg subordinate.Message) {
logrus.Info(msg)
}
var bootErr error
acc.sub.BootHandler = func(msgType string, msg subordinate.Message) {
switch msgType {
case subordinate.BootMessage:
if v, found := msg["frontend_token"]; found {
if str, ok := v.(string); ok {
acc.frontendToken = str
}
}
if v, found := msg["bind_address"]; found {
if sr, ok := v.(string); ok {
acc.bindAddress = sr
}
}
case subordinate.ErrorMessage:
if v, found := msg[subordinate.ErrorMessage]; found {
if str, ok := v.(string); ok {
bootErr = errors.New(str)
}
}
}
}
acc.sub.MalformedHandler = func(msg subordinate.Message) {
logrus.Error(msg)
}
logrus.Infof("executing '%v'", accCmd)
acc.process, err = proctree.StartChild(acc.sub.Tail, accCmd...)
if err != nil {
return nil, err
}
<-acc.sub.BootComplete
if bootErr == nil {
go acc.monitor()
i.agent.addAccess <- acc
return &agentGrpc.AccessPrivateResponse{FrontendToken: acc.frontendToken}, nil
} else {
if err := proctree.WaitChild(acc.process); err != nil {
logrus.Errorf("error joining: %v", err)
}
return nil, fmt.Errorf("unable to start access: %v", bootErr)
}
}

197
agent/agent.go Normal file
View File

@ -0,0 +1,197 @@
package agent
import (
"context"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/openziti/zrok/agent/agentGrpc"
"github.com/openziti/zrok/agent/agentUi"
"github.com/openziti/zrok/agent/proctree"
"github.com/openziti/zrok/environment/env_core"
"github.com/openziti/zrok/sdk/golang/sdk"
"github.com/openziti/zrok/util"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"net"
"net/http"
"os"
)
type Agent struct {
cfg *AgentConfig
httpEndpoint string
root env_core.Root
agentSocket string
shares map[string]*share
addShare chan *share
rmShare chan *share
accesses map[string]*access
addAccess chan *access
rmAccess chan *access
}
func NewAgent(cfg *AgentConfig, root env_core.Root) (*Agent, error) {
if !root.IsEnabled() {
return nil, errors.Errorf("unable to load environment; did you 'zrok enable'?")
}
return &Agent{
cfg: cfg,
root: root,
shares: make(map[string]*share),
addShare: make(chan *share),
rmShare: make(chan *share),
accesses: make(map[string]*access),
addAccess: make(chan *access),
rmAccess: make(chan *access),
}, nil
}
func (a *Agent) Run() error {
logrus.Infof("started")
if err := proctree.Init("zrok Agent"); err != nil {
return err
}
agentSocket, err := a.root.AgentSocket()
if err != nil {
return err
}
l, err := net.Listen("unix", agentSocket)
if err != nil {
return err
}
a.agentSocket = agentSocket
go a.manager()
go a.gateway(a.cfg)
srv := grpc.NewServer()
agentGrpc.RegisterAgentServer(srv, &agentGrpcImpl{agent: a})
if err := srv.Serve(l); err != nil {
return err
}
return nil
}
func (a *Agent) Shutdown() {
logrus.Infof("stopping")
if err := os.Remove(a.agentSocket); err != nil {
logrus.Warnf("unable to remove agent socket: %v", err)
}
for _, shr := range a.shares {
logrus.Debugf("stopping share '%v'", shr.token)
a.rmShare <- shr
}
for _, acc := range a.accesses {
logrus.Debugf("stopping access '%v'", acc.token)
a.rmAccess <- acc
}
}
func (a *Agent) Config() *AgentConfig {
return a.cfg
}
func (a *Agent) gateway(cfg *AgentConfig) {
logrus.Info("started")
defer logrus.Warn("exited")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
mux := runtime.NewServeMux()
opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
endpoint := "unix:" + a.agentSocket
logrus.Debugf("endpoint: '%v'", endpoint)
if err := agentGrpc.RegisterAgentHandlerFromEndpoint(ctx, mux, "unix:"+a.agentSocket, opts); err != nil {
logrus.Fatalf("unable to register gateway: %v", err)
}
listener, err := util.AutoListener("tcp", cfg.ConsoleAddress, cfg.ConsoleStartPort, cfg.ConsoleEndPort)
if err != nil {
logrus.Fatalf("unable to create a listener: %v", err)
}
a.httpEndpoint = listener.Addr().String()
if err := http.Serve(listener, agentUi.Middleware(mux)); err != nil {
logrus.Error(err)
}
}
func (a *Agent) manager() {
logrus.Info("started")
defer logrus.Warn("exited")
for {
select {
case inShare := <-a.addShare:
logrus.Infof("adding new share '%v'", inShare.token)
a.shares[inShare.token] = inShare
case outShare := <-a.rmShare:
if shr, found := a.shares[outShare.token]; found {
logrus.Infof("removing share '%v'", shr.token)
if err := proctree.StopChild(shr.process); err != nil {
logrus.Errorf("error stopping share '%v': %v", shr.token, err)
}
if err := proctree.WaitChild(shr.process); err != nil {
logrus.Errorf("error joining share '%v': %v", shr.token, err)
}
if !shr.reserved {
if err := a.deleteShare(shr.token); err != nil {
logrus.Errorf("error deleting share '%v': %v", shr.token, err)
}
}
delete(a.shares, shr.token)
} else {
logrus.Debug("skipping unidentified (orphaned) share removal")
}
case inAccess := <-a.addAccess:
logrus.Infof("adding new access '%v'", inAccess.frontendToken)
a.accesses[inAccess.frontendToken] = inAccess
case outAccess := <-a.rmAccess:
if acc, found := a.accesses[outAccess.frontendToken]; found {
logrus.Infof("removing access '%v'", acc.frontendToken)
if err := proctree.StopChild(acc.process); err != nil {
logrus.Errorf("error stopping access '%v': %v", acc.frontendToken, err)
}
if err := proctree.WaitChild(acc.process); err != nil {
logrus.Errorf("error joining access '%v': %v", acc.frontendToken, err)
}
if err := a.deleteAccess(acc.token, acc.frontendToken); err != nil {
logrus.Errorf("error deleting access '%v': %v", acc.frontendToken, err)
}
delete(a.accesses, acc.frontendToken)
} else {
logrus.Debug("skipping unidentified (orphaned) access removal")
}
}
}
}
func (a *Agent) deleteShare(token string) error {
logrus.Debugf("deleting share '%v'", token)
if err := sdk.DeleteShare(a.root, &sdk.Share{Token: token}); err != nil {
return err
}
return nil
}
func (a *Agent) deleteAccess(token, frontendToken string) error {
logrus.Debugf("deleting access '%v'", frontendToken)
if err := sdk.DeleteAccess(a.root, &sdk.Access{Token: frontendToken, ShareToken: token}); err != nil {
return err
}
return nil
}
type agentGrpcImpl struct {
agentGrpc.UnimplementedAgentServer
agent *Agent
}

View File

@ -0,0 +1,51 @@
package agentClient
import (
"context"
"github.com/openziti/zrok/agent/agentGrpc"
"github.com/openziti/zrok/build"
"github.com/openziti/zrok/environment/env_core"
"github.com/pkg/errors"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/resolver"
"net"
"strings"
)
func NewClient(root env_core.Root) (client agentGrpc.AgentClient, conn *grpc.ClientConn, err error) {
agentSocket, err := root.AgentSocket()
if err != nil {
return nil, nil, err
}
opts := []grpc.DialOption{
grpc.WithContextDialer(func(_ context.Context, addr string) (net.Conn, error) {
return net.Dial("unix", addr)
}),
grpc.WithTransportCredentials(insecure.NewCredentials()),
}
resolver.SetDefaultScheme("passthrough")
conn, err = grpc.NewClient(agentSocket, opts...)
if err != nil {
return nil, nil, err
}
return agentGrpc.NewAgentClient(conn), conn, nil
}
func IsAgentRunning(root env_core.Root) (bool, error) {
client, conn, err := NewClient(root)
if err != nil {
return false, err
}
defer func() { _ = conn.Close() }()
resp, err := client.Version(context.Background(), &agentGrpc.VersionRequest{})
if err != nil {
return false, nil
}
if !strings.HasPrefix(resp.GetV(), build.Series) {
return false, errors.Errorf("agent reported version '%v'; we expected version '%v'", resp.GetV(), build.Series)
}
return true, nil
}

1625
agent/agentGrpc/agent.pb.go Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,659 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: agent/agentGrpc/agent.proto
/*
Package agentGrpc is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package agentGrpc
import (
"context"
"io"
"net/http"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
)
// Suppress "imported and not used" errors
var _ codes.Code
var _ io.Reader
var _ status.Status
var _ = runtime.String
var _ = utilities.NewDoubleArray
var _ = metadata.Join
var (
filter_Agent_AccessPrivate_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Agent_AccessPrivate_0(ctx context.Context, marshaler runtime.Marshaler, client AgentClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq AccessPrivateRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Agent_AccessPrivate_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.AccessPrivate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Agent_AccessPrivate_0(ctx context.Context, marshaler runtime.Marshaler, server AgentServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq AccessPrivateRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Agent_AccessPrivate_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.AccessPrivate(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_Agent_ReleaseAccess_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Agent_ReleaseAccess_0(ctx context.Context, marshaler runtime.Marshaler, client AgentClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ReleaseAccessRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Agent_ReleaseAccess_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.ReleaseAccess(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Agent_ReleaseAccess_0(ctx context.Context, marshaler runtime.Marshaler, server AgentServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ReleaseAccessRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Agent_ReleaseAccess_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.ReleaseAccess(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_Agent_ReleaseShare_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Agent_ReleaseShare_0(ctx context.Context, marshaler runtime.Marshaler, client AgentClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ReleaseShareRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Agent_ReleaseShare_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.ReleaseShare(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Agent_ReleaseShare_0(ctx context.Context, marshaler runtime.Marshaler, server AgentServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ReleaseShareRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Agent_ReleaseShare_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.ReleaseShare(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_Agent_SharePrivate_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Agent_SharePrivate_0(ctx context.Context, marshaler runtime.Marshaler, client AgentClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq SharePrivateRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Agent_SharePrivate_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.SharePrivate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Agent_SharePrivate_0(ctx context.Context, marshaler runtime.Marshaler, server AgentServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq SharePrivateRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Agent_SharePrivate_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.SharePrivate(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_Agent_SharePublic_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Agent_SharePublic_0(ctx context.Context, marshaler runtime.Marshaler, client AgentClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq SharePublicRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Agent_SharePublic_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.SharePublic(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Agent_SharePublic_0(ctx context.Context, marshaler runtime.Marshaler, server AgentServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq SharePublicRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Agent_SharePublic_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.SharePublic(ctx, &protoReq)
return msg, metadata, err
}
func request_Agent_Status_0(ctx context.Context, marshaler runtime.Marshaler, client AgentClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq StatusRequest
var metadata runtime.ServerMetadata
msg, err := client.Status(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Agent_Status_0(ctx context.Context, marshaler runtime.Marshaler, server AgentServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq StatusRequest
var metadata runtime.ServerMetadata
msg, err := server.Status(ctx, &protoReq)
return msg, metadata, err
}
func request_Agent_Version_0(ctx context.Context, marshaler runtime.Marshaler, client AgentClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq VersionRequest
var metadata runtime.ServerMetadata
msg, err := client.Version(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Agent_Version_0(ctx context.Context, marshaler runtime.Marshaler, server AgentServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq VersionRequest
var metadata runtime.ServerMetadata
msg, err := server.Version(ctx, &protoReq)
return msg, metadata, err
}
// RegisterAgentHandlerServer registers the http handlers for service Agent to "mux".
// UnaryRPC :call AgentServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterAgentHandlerFromEndpoint instead.
func RegisterAgentHandlerServer(ctx context.Context, mux *runtime.ServeMux, server AgentServer) error {
mux.Handle("POST", pattern_Agent_AccessPrivate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/.Agent/AccessPrivate", runtime.WithHTTPPathPattern("/v1/agent/accessPrivate"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Agent_AccessPrivate_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Agent_AccessPrivate_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Agent_ReleaseAccess_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/.Agent/ReleaseAccess", runtime.WithHTTPPathPattern("/v1/agent/releaseAccess"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Agent_ReleaseAccess_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Agent_ReleaseAccess_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Agent_ReleaseShare_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/.Agent/ReleaseShare", runtime.WithHTTPPathPattern("/v1/agent/releaseShare"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Agent_ReleaseShare_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Agent_ReleaseShare_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Agent_SharePrivate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/.Agent/SharePrivate", runtime.WithHTTPPathPattern("/v1/agent/sharePrivate"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Agent_SharePrivate_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Agent_SharePrivate_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Agent_SharePublic_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/.Agent/SharePublic", runtime.WithHTTPPathPattern("/v1/agent/sharePublic"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Agent_SharePublic_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Agent_SharePublic_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_Agent_Status_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/.Agent/Status", runtime.WithHTTPPathPattern("/v1/agent/status"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Agent_Status_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Agent_Status_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_Agent_Version_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/.Agent/Version", runtime.WithHTTPPathPattern("/v1/agent/version"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Agent_Version_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Agent_Version_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// RegisterAgentHandlerFromEndpoint is same as RegisterAgentHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterAgentHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.DialContext(ctx, endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterAgentHandler(ctx, mux, conn)
}
// RegisterAgentHandler registers the http handlers for service Agent to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterAgentHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterAgentHandlerClient(ctx, mux, NewAgentClient(conn))
}
// RegisterAgentHandlerClient registers the http handlers for service Agent
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "AgentClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "AgentClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "AgentClient" to call the correct interceptors.
func RegisterAgentHandlerClient(ctx context.Context, mux *runtime.ServeMux, client AgentClient) error {
mux.Handle("POST", pattern_Agent_AccessPrivate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/.Agent/AccessPrivate", runtime.WithHTTPPathPattern("/v1/agent/accessPrivate"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Agent_AccessPrivate_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Agent_AccessPrivate_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Agent_ReleaseAccess_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/.Agent/ReleaseAccess", runtime.WithHTTPPathPattern("/v1/agent/releaseAccess"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Agent_ReleaseAccess_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Agent_ReleaseAccess_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Agent_ReleaseShare_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/.Agent/ReleaseShare", runtime.WithHTTPPathPattern("/v1/agent/releaseShare"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Agent_ReleaseShare_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Agent_ReleaseShare_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Agent_SharePrivate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/.Agent/SharePrivate", runtime.WithHTTPPathPattern("/v1/agent/sharePrivate"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Agent_SharePrivate_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Agent_SharePrivate_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Agent_SharePublic_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/.Agent/SharePublic", runtime.WithHTTPPathPattern("/v1/agent/sharePublic"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Agent_SharePublic_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Agent_SharePublic_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_Agent_Status_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/.Agent/Status", runtime.WithHTTPPathPattern("/v1/agent/status"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Agent_Status_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Agent_Status_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_Agent_Version_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/.Agent/Version", runtime.WithHTTPPathPattern("/v1/agent/version"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Agent_Version_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Agent_Version_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_Agent_AccessPrivate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "agent", "accessPrivate"}, ""))
pattern_Agent_ReleaseAccess_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "agent", "releaseAccess"}, ""))
pattern_Agent_ReleaseShare_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "agent", "releaseShare"}, ""))
pattern_Agent_SharePrivate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "agent", "sharePrivate"}, ""))
pattern_Agent_SharePublic_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "agent", "sharePublic"}, ""))
pattern_Agent_Status_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "agent", "status"}, ""))
pattern_Agent_Version_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "agent", "version"}, ""))
)
var (
forward_Agent_AccessPrivate_0 = runtime.ForwardResponseMessage
forward_Agent_ReleaseAccess_0 = runtime.ForwardResponseMessage
forward_Agent_ReleaseShare_0 = runtime.ForwardResponseMessage
forward_Agent_SharePrivate_0 = runtime.ForwardResponseMessage
forward_Agent_SharePublic_0 = runtime.ForwardResponseMessage
forward_Agent_Status_0 = runtime.ForwardResponseMessage
forward_Agent_Version_0 = runtime.ForwardResponseMessage
)

150
agent/agentGrpc/agent.proto Normal file
View File

@ -0,0 +1,150 @@
syntax = "proto3";
option go_package = "github.com/openziti/zrok/agent/agentGrpc";
import "google/api/annotations.proto";
service Agent {
rpc AccessPrivate(AccessPrivateRequest) returns (AccessPrivateResponse) {
option(google.api.http) = {
post: "/v1/agent/accessPrivate"
};
}
rpc ReleaseAccess(ReleaseAccessRequest) returns (ReleaseAccessResponse) {
option(google.api.http) = {
post: "/v1/agent/releaseAccess"
};
}
rpc ReleaseShare(ReleaseShareRequest) returns (ReleaseShareResponse) {
option(google.api.http) = {
post: "/v1/agent/releaseShare"
};
}
rpc ShareReserved(ShareReservedRequest) returns (ShareReservedResponse) {}
rpc SharePrivate(SharePrivateRequest) returns (SharePrivateResponse) {
option(google.api.http) = {
post: "/v1/agent/sharePrivate"
};
}
rpc SharePublic(SharePublicRequest) returns (SharePublicResponse) {
option(google.api.http) = {
post: "/v1/agent/sharePublic"
};
}
rpc Status(StatusRequest) returns (StatusResponse) {
option(google.api.http) = {
get: "/v1/agent/status"
};
}
rpc Version(VersionRequest) returns (VersionResponse) {
option(google.api.http) = {
get: "/v1/agent/version"
};
}
}
message AccessDetail {
string frontendToken = 1;
string token = 2;
string bindAddress = 3;
repeated string responseHeaders = 4;
}
message AccessPrivateResponse{
string frontendToken = 1;
}
message AccessPrivateRequest{
string token = 1;
string bindAddress = 2;
bool autoMode = 3;
string autoAddress = 4;
uint32 autoStartPort = 5;
uint32 autoEndPort = 6;
repeated string responseHeaders = 7;
}
message ReleaseAccessRequest {
string frontendToken = 1;
}
message ReleaseAccessResponse {
}
message ReleaseShareRequest {
string token = 1;
}
message ReleaseShareResponse {
}
message ShareDetail {
string token = 1;
string shareMode = 2;
string backendMode = 3;
bool reserved = 4;
repeated string frontendEndpoint = 5;
string backendEndpoint = 6;
bool closed = 7;
string status = 8;
}
message SharePrivateRequest {
string target = 1;
string backendMode = 2;
bool insecure = 3;
bool closed = 4;
repeated string accessGrants = 5;
}
message SharePrivateResponse {
string token = 1;
}
message SharePublicRequest {
string target = 1;
repeated string basicAuth = 2;
repeated string frontendSelection = 3;
string backendMode = 4;
bool insecure = 5;
string oauthProvider = 6;
repeated string oauthEmailAddressPatterns = 7;
string oauthCheckInterval = 8;
bool closed = 9;
repeated string accessGrants = 10;
}
message SharePublicResponse {
string token = 1;
repeated string frontendEndpoints = 2;
}
message ShareReservedRequest {
string token = 1;
string overrideEndpoint = 2;
bool insecure = 3;
}
message ShareReservedResponse {
string token = 1;
string backendMode = 2;
string shareMode = 3;
repeated string frontendEndpoints = 4;
string target = 5;
}
message StatusRequest {
}
message StatusResponse {
repeated AccessDetail accesses = 1;
repeated ShareDetail shares = 2;
}
message VersionRequest {
}
message VersionResponse {
string v = 1;
string consoleEndpoint = 2;
}

View File

@ -0,0 +1,525 @@
{
"swagger": "2.0",
"info": {
"title": "agent/agentGrpc/agent.proto",
"version": "version not set"
},
"tags": [
{
"name": "Agent"
}
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {
"/v1/agent/accessPrivate": {
"post": {
"operationId": "Agent_AccessPrivate",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/AccessPrivateResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"parameters": [
{
"name": "token",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "bindAddress",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "autoMode",
"in": "query",
"required": false,
"type": "boolean"
},
{
"name": "autoAddress",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "autoStartPort",
"in": "query",
"required": false,
"type": "integer",
"format": "int64"
},
{
"name": "autoEndPort",
"in": "query",
"required": false,
"type": "integer",
"format": "int64"
},
{
"name": "responseHeaders",
"in": "query",
"required": false,
"type": "array",
"items": {
"type": "string"
},
"collectionFormat": "multi"
}
],
"tags": [
"Agent"
]
}
},
"/v1/agent/releaseAccess": {
"post": {
"operationId": "Agent_ReleaseAccess",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/ReleaseAccessResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"parameters": [
{
"name": "frontendToken",
"in": "query",
"required": false,
"type": "string"
}
],
"tags": [
"Agent"
]
}
},
"/v1/agent/releaseShare": {
"post": {
"operationId": "Agent_ReleaseShare",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/ReleaseShareResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"parameters": [
{
"name": "token",
"in": "query",
"required": false,
"type": "string"
}
],
"tags": [
"Agent"
]
}
},
"/v1/agent/sharePrivate": {
"post": {
"operationId": "Agent_SharePrivate",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/SharePrivateResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"parameters": [
{
"name": "target",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "backendMode",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "insecure",
"in": "query",
"required": false,
"type": "boolean"
},
{
"name": "closed",
"in": "query",
"required": false,
"type": "boolean"
},
{
"name": "accessGrants",
"in": "query",
"required": false,
"type": "array",
"items": {
"type": "string"
},
"collectionFormat": "multi"
}
],
"tags": [
"Agent"
]
}
},
"/v1/agent/sharePublic": {
"post": {
"operationId": "Agent_SharePublic",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/SharePublicResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"parameters": [
{
"name": "target",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "basicAuth",
"in": "query",
"required": false,
"type": "array",
"items": {
"type": "string"
},
"collectionFormat": "multi"
},
{
"name": "frontendSelection",
"in": "query",
"required": false,
"type": "array",
"items": {
"type": "string"
},
"collectionFormat": "multi"
},
{
"name": "backendMode",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "insecure",
"in": "query",
"required": false,
"type": "boolean"
},
{
"name": "oauthProvider",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "oauthEmailAddressPatterns",
"in": "query",
"required": false,
"type": "array",
"items": {
"type": "string"
},
"collectionFormat": "multi"
},
{
"name": "oauthCheckInterval",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "closed",
"in": "query",
"required": false,
"type": "boolean"
},
{
"name": "accessGrants",
"in": "query",
"required": false,
"type": "array",
"items": {
"type": "string"
},
"collectionFormat": "multi"
}
],
"tags": [
"Agent"
]
}
},
"/v1/agent/status": {
"get": {
"operationId": "Agent_Status",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/StatusResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"tags": [
"Agent"
]
}
},
"/v1/agent/version": {
"get": {
"operationId": "Agent_Version",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/VersionResponse"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "#/definitions/rpcStatus"
}
}
},
"tags": [
"Agent"
]
}
}
},
"definitions": {
"AccessDetail": {
"type": "object",
"properties": {
"frontendToken": {
"type": "string"
},
"token": {
"type": "string"
},
"bindAddress": {
"type": "string"
},
"responseHeaders": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"AccessPrivateResponse": {
"type": "object",
"properties": {
"frontendToken": {
"type": "string"
}
}
},
"ReleaseAccessResponse": {
"type": "object"
},
"ReleaseShareResponse": {
"type": "object"
},
"ShareDetail": {
"type": "object",
"properties": {
"token": {
"type": "string"
},
"shareMode": {
"type": "string"
},
"backendMode": {
"type": "string"
},
"reserved": {
"type": "boolean"
},
"frontendEndpoint": {
"type": "array",
"items": {
"type": "string"
}
},
"backendEndpoint": {
"type": "string"
},
"closed": {
"type": "boolean"
},
"status": {
"type": "string"
}
}
},
"SharePrivateResponse": {
"type": "object",
"properties": {
"token": {
"type": "string"
}
}
},
"SharePublicResponse": {
"type": "object",
"properties": {
"token": {
"type": "string"
},
"frontendEndpoints": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"ShareReservedResponse": {
"type": "object",
"properties": {
"token": {
"type": "string"
},
"backendMode": {
"type": "string"
},
"shareMode": {
"type": "string"
},
"frontendEndpoints": {
"type": "array",
"items": {
"type": "string"
}
},
"target": {
"type": "string"
}
}
},
"StatusResponse": {
"type": "object",
"properties": {
"accesses": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/AccessDetail"
}
},
"shares": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/ShareDetail"
}
}
}
},
"VersionResponse": {
"type": "object",
"properties": {
"v": {
"type": "string"
},
"consoleEndpoint": {
"type": "string"
}
}
},
"protobufAny": {
"type": "object",
"properties": {
"@type": {
"type": "string"
}
},
"additionalProperties": {}
},
"rpcStatus": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"message": {
"type": "string"
},
"details": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/protobufAny"
}
}
}
}
}
}

View File

@ -0,0 +1,387 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v5.27.3
// source: agent/agentGrpc/agent.proto
package agentGrpc
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
Agent_AccessPrivate_FullMethodName = "/Agent/AccessPrivate"
Agent_ReleaseAccess_FullMethodName = "/Agent/ReleaseAccess"
Agent_ReleaseShare_FullMethodName = "/Agent/ReleaseShare"
Agent_ShareReserved_FullMethodName = "/Agent/ShareReserved"
Agent_SharePrivate_FullMethodName = "/Agent/SharePrivate"
Agent_SharePublic_FullMethodName = "/Agent/SharePublic"
Agent_Status_FullMethodName = "/Agent/Status"
Agent_Version_FullMethodName = "/Agent/Version"
)
// AgentClient is the client API for Agent service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type AgentClient interface {
AccessPrivate(ctx context.Context, in *AccessPrivateRequest, opts ...grpc.CallOption) (*AccessPrivateResponse, error)
ReleaseAccess(ctx context.Context, in *ReleaseAccessRequest, opts ...grpc.CallOption) (*ReleaseAccessResponse, error)
ReleaseShare(ctx context.Context, in *ReleaseShareRequest, opts ...grpc.CallOption) (*ReleaseShareResponse, error)
ShareReserved(ctx context.Context, in *ShareReservedRequest, opts ...grpc.CallOption) (*ShareReservedResponse, error)
SharePrivate(ctx context.Context, in *SharePrivateRequest, opts ...grpc.CallOption) (*SharePrivateResponse, error)
SharePublic(ctx context.Context, in *SharePublicRequest, opts ...grpc.CallOption) (*SharePublicResponse, error)
Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error)
Version(ctx context.Context, in *VersionRequest, opts ...grpc.CallOption) (*VersionResponse, error)
}
type agentClient struct {
cc grpc.ClientConnInterface
}
func NewAgentClient(cc grpc.ClientConnInterface) AgentClient {
return &agentClient{cc}
}
func (c *agentClient) AccessPrivate(ctx context.Context, in *AccessPrivateRequest, opts ...grpc.CallOption) (*AccessPrivateResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AccessPrivateResponse)
err := c.cc.Invoke(ctx, Agent_AccessPrivate_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *agentClient) ReleaseAccess(ctx context.Context, in *ReleaseAccessRequest, opts ...grpc.CallOption) (*ReleaseAccessResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ReleaseAccessResponse)
err := c.cc.Invoke(ctx, Agent_ReleaseAccess_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *agentClient) ReleaseShare(ctx context.Context, in *ReleaseShareRequest, opts ...grpc.CallOption) (*ReleaseShareResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ReleaseShareResponse)
err := c.cc.Invoke(ctx, Agent_ReleaseShare_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *agentClient) ShareReserved(ctx context.Context, in *ShareReservedRequest, opts ...grpc.CallOption) (*ShareReservedResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ShareReservedResponse)
err := c.cc.Invoke(ctx, Agent_ShareReserved_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *agentClient) SharePrivate(ctx context.Context, in *SharePrivateRequest, opts ...grpc.CallOption) (*SharePrivateResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SharePrivateResponse)
err := c.cc.Invoke(ctx, Agent_SharePrivate_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *agentClient) SharePublic(ctx context.Context, in *SharePublicRequest, opts ...grpc.CallOption) (*SharePublicResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SharePublicResponse)
err := c.cc.Invoke(ctx, Agent_SharePublic_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *agentClient) Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(StatusResponse)
err := c.cc.Invoke(ctx, Agent_Status_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *agentClient) Version(ctx context.Context, in *VersionRequest, opts ...grpc.CallOption) (*VersionResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(VersionResponse)
err := c.cc.Invoke(ctx, Agent_Version_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// AgentServer is the server API for Agent service.
// All implementations must embed UnimplementedAgentServer
// for forward compatibility.
type AgentServer interface {
AccessPrivate(context.Context, *AccessPrivateRequest) (*AccessPrivateResponse, error)
ReleaseAccess(context.Context, *ReleaseAccessRequest) (*ReleaseAccessResponse, error)
ReleaseShare(context.Context, *ReleaseShareRequest) (*ReleaseShareResponse, error)
ShareReserved(context.Context, *ShareReservedRequest) (*ShareReservedResponse, error)
SharePrivate(context.Context, *SharePrivateRequest) (*SharePrivateResponse, error)
SharePublic(context.Context, *SharePublicRequest) (*SharePublicResponse, error)
Status(context.Context, *StatusRequest) (*StatusResponse, error)
Version(context.Context, *VersionRequest) (*VersionResponse, error)
mustEmbedUnimplementedAgentServer()
}
// UnimplementedAgentServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedAgentServer struct{}
func (UnimplementedAgentServer) AccessPrivate(context.Context, *AccessPrivateRequest) (*AccessPrivateResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AccessPrivate not implemented")
}
func (UnimplementedAgentServer) ReleaseAccess(context.Context, *ReleaseAccessRequest) (*ReleaseAccessResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ReleaseAccess not implemented")
}
func (UnimplementedAgentServer) ReleaseShare(context.Context, *ReleaseShareRequest) (*ReleaseShareResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ReleaseShare not implemented")
}
func (UnimplementedAgentServer) ShareReserved(context.Context, *ShareReservedRequest) (*ShareReservedResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ShareReserved not implemented")
}
func (UnimplementedAgentServer) SharePrivate(context.Context, *SharePrivateRequest) (*SharePrivateResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SharePrivate not implemented")
}
func (UnimplementedAgentServer) SharePublic(context.Context, *SharePublicRequest) (*SharePublicResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SharePublic not implemented")
}
func (UnimplementedAgentServer) Status(context.Context, *StatusRequest) (*StatusResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Status not implemented")
}
func (UnimplementedAgentServer) Version(context.Context, *VersionRequest) (*VersionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Version not implemented")
}
func (UnimplementedAgentServer) mustEmbedUnimplementedAgentServer() {}
func (UnimplementedAgentServer) testEmbeddedByValue() {}
// UnsafeAgentServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to AgentServer will
// result in compilation errors.
type UnsafeAgentServer interface {
mustEmbedUnimplementedAgentServer()
}
func RegisterAgentServer(s grpc.ServiceRegistrar, srv AgentServer) {
// If the following call pancis, it indicates UnimplementedAgentServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Agent_ServiceDesc, srv)
}
func _Agent_AccessPrivate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AccessPrivateRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AgentServer).AccessPrivate(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Agent_AccessPrivate_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AgentServer).AccessPrivate(ctx, req.(*AccessPrivateRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Agent_ReleaseAccess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ReleaseAccessRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AgentServer).ReleaseAccess(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Agent_ReleaseAccess_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AgentServer).ReleaseAccess(ctx, req.(*ReleaseAccessRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Agent_ReleaseShare_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ReleaseShareRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AgentServer).ReleaseShare(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Agent_ReleaseShare_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AgentServer).ReleaseShare(ctx, req.(*ReleaseShareRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Agent_ShareReserved_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ShareReservedRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AgentServer).ShareReserved(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Agent_ShareReserved_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AgentServer).ShareReserved(ctx, req.(*ShareReservedRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Agent_SharePrivate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SharePrivateRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AgentServer).SharePrivate(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Agent_SharePrivate_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AgentServer).SharePrivate(ctx, req.(*SharePrivateRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Agent_SharePublic_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SharePublicRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AgentServer).SharePublic(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Agent_SharePublic_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AgentServer).SharePublic(ctx, req.(*SharePublicRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Agent_Status_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(StatusRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AgentServer).Status(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Agent_Status_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AgentServer).Status(ctx, req.(*StatusRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Agent_Version_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(VersionRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AgentServer).Version(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Agent_Version_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AgentServer).Version(ctx, req.(*VersionRequest))
}
return interceptor(ctx, in, info, handler)
}
// Agent_ServiceDesc is the grpc.ServiceDesc for Agent service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Agent_ServiceDesc = grpc.ServiceDesc{
ServiceName: "Agent",
HandlerType: (*AgentServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "AccessPrivate",
Handler: _Agent_AccessPrivate_Handler,
},
{
MethodName: "ReleaseAccess",
Handler: _Agent_ReleaseAccess_Handler,
},
{
MethodName: "ReleaseShare",
Handler: _Agent_ReleaseShare_Handler,
},
{
MethodName: "ShareReserved",
Handler: _Agent_ShareReserved_Handler,
},
{
MethodName: "SharePrivate",
Handler: _Agent_SharePrivate_Handler,
},
{
MethodName: "SharePublic",
Handler: _Agent_SharePublic_Handler,
},
{
MethodName: "Status",
Handler: _Agent_Status_Handler,
},
{
MethodName: "Version",
Handler: _Agent_Version_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "agent/agentGrpc/agent.proto",
}

10
agent/agentGrpc/tools.go Normal file
View File

@ -0,0 +1,10 @@
//go:build tools
package agentGrpc
import (
_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway"
_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2"
_ "google.golang.org/grpc/cmd/protoc-gen-go-grpc"
_ "google.golang.org/protobuf/cmd/protoc-gen-go"
)

24
agent/agentUi/.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

50
agent/agentUi/README.md Normal file
View File

@ -0,0 +1,50 @@
# React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
- Configure the top-level `parserOptions` property like this:
```js
export default tseslint.config({
languageOptions: {
// other options...
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
},
})
```
- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
- Optionally add `...tseslint.configs.stylisticTypeChecked`
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:
```js
// eslint.config.js
import react from 'eslint-plugin-react'
export default tseslint.config({
// Set the react version
settings: { react: { version: '18.3' } },
plugins: {
// Add the react plugin
react,
},
rules: {
// other rules...
// Enable its recommended rules
...react.configs.recommended.rules,
...react.configs['jsx-runtime'].rules,
},
})
```

6
agent/agentUi/embed.go Normal file
View File

@ -0,0 +1,6 @@
package agentUi
import "embed"
//go:embed dist
var FS embed.FS

View File

@ -0,0 +1,28 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
)

17
agent/agentUi/index.html Normal file
View File

@ -0,0 +1,17 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/zrok.png" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono&display=swap" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>zrok Agent Console</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View File

@ -0,0 +1,53 @@
package agentUi
import (
"github.com/sirupsen/logrus"
"io/fs"
"net/http"
"os"
"path/filepath"
"strings"
)
const staticPath = "dist"
func Middleware(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.URL.Path, "/v1") {
handler.ServeHTTP(w, r)
return
}
path := filepath.ToSlash(filepath.Join(staticPath, r.URL.Path))
logrus.Debugf("path = %v", path)
f, err := FS.Open(path)
if os.IsNotExist(err) {
// file does not exist, serve index.gohtml
index, err := FS.ReadFile(filepath.ToSlash(filepath.Join(staticPath, "index.html")))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusAccepted)
_, _ = w.Write(index)
return
} else if err != nil {
// if we got an error (that wasn't that the file doesn't exist) stating the
// file, return a 500 internal server error and stop
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer func() { _ = f.Close() }()
// get the subdirectory of the static dir
if statics, err := fs.Sub(FS, staticPath); err == nil {
// otherwise, use http.FileServer to serve the static dir
http.FileServer(http.FS(statics)).ServeHTTP(w, r)
} else {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
})
}

4009
agent/agentUi/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,33 @@
{
"name": "agentui",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
"@mui/icons-material": "^6.1.7",
"@mui/material": "^6.1.7",
"formik": "^2.4.6",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@eslint/js": "^9.13.0",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@vitejs/plugin-react": "^4.3.3",
"eslint": "^9.13.0",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.14",
"globals": "^15.11.0",
"typescript-eslint": "^8.15.0",
"vite": "^6.2.0"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -0,0 +1,53 @@
import {AgentObject} from "./model/overview.ts";
import {AppBar, Box, Button, Card, Chip, Grid2, Toolbar, Typography} from "@mui/material";
import LanIcon from "@mui/icons-material/Lan";
import {AccessDetail} from "./api";
import DeleteIcon from "@mui/icons-material/Delete";
import {GetAgentApi} from "./model/api.ts";
interface AccessCardProps {
accessObject: AgentObject;
}
const AccessCard = ({ accessObject }: AccessCardProps) => {
let access = (accessObject.v as AccessDetail);
const releaseAccess = () => {
GetAgentApi().agentReleaseAccess({frontendToken: access.frontendToken})
.catch(e => {
console.log("error releasing access", e);
});
}
return (
<Card>
<AppBar position="sticky">
<Toolbar variant="dense">
<LanIcon />
<Grid2 container sx={{ flexGrow: 1 }}>
<Grid2 display="flex" justifyContent="center" size="grow">
<Typography variant="h6" component="div" style={{ color: "#9bf316" }}>{access.frontendToken}</Typography>
</Grid2>
</Grid2>
<Grid2 container>
<Grid2 display="flex" justifyContent="right">
<Chip label="private" size="small" style={{ backgroundColor: "#9bf316" }} />
</Grid2>
</Grid2>
</Toolbar>
</AppBar>
<Box sx={{ p: 2, textAlign: "center" }}>
<Typography variant="h6" component="div">
{access.token} &rarr; {access.bindAddress}
</Typography>
</Box>
<Grid2 container sx={{ flexGrow: 1 }}>
<Grid2 display="flex" justifyContent="right" size="grow">
<Button variant="contained" onClick={releaseAccess}><DeleteIcon /></Button>
</Grid2>
</Grid2>
</Card>
);
}
export default AccessCard;

View File

@ -0,0 +1,69 @@
import {useEffect, useState} from "react";
import {GetAgentApi} from "./model/api.ts";
import NavBar from "./NavBar.tsx";
import {AgentObject, buildOverview} from "./model/overview.ts";
import Overview from "./Overview.tsx";
import NewShareModal from "./NewShareModal.tsx";
import NewAccessModal from "./NewAccessModal.tsx";
const AgentUi = () => {
const [version, setVersion] = useState("unset");
const [overview, setOverview] = useState(new Array<AgentObject>());
const [newShareOpen, setNewShareOpen] = useState(false);
const [newAccessOpen, setNewAccessOpen] = useState(false);
const openNewShare = () => {
setNewShareOpen(true);
}
const closeNewShare = () => {
setNewShareOpen(false);
}
const openNewAccess = () => {
setNewAccessOpen(true);
}
const closeNewAccess = () => {
setNewAccessOpen(false);
}
useEffect(() => {
GetAgentApi().agentVersion()
.then(r => {
if(r.v) {
setVersion(r.v);
} else {
console.log("unexpected", r);
}
})
.catch(e => {
console.log(e);
});
}, []);
useEffect(() => {
let interval = setInterval(() => {
GetAgentApi().agentStatus()
.then(r => {
setOverview(buildOverview(r));
})
.catch(e => {
console.log(e);
})
}, 1000);
return () => {
clearInterval(interval);
setOverview(new Array<AgentObject>());
}
}, []);
return (
<>
<NavBar shareClick={openNewShare} accessClick={openNewAccess} />
<Overview overview={overview} shareClick={openNewShare} accessClick={openNewAccess} />
<NewShareModal isOpen={newShareOpen} close={closeNewShare} />
<NewAccessModal isOpen={newAccessOpen} close={closeNewAccess} />
</>
);
}
export default AgentUi;

View File

@ -0,0 +1,40 @@
import {AppBar, Box, Button, Grid2, Toolbar, Typography} from "@mui/material";
import LanIcon from "@mui/icons-material/Lan";
import ShareIcon from "@mui/icons-material/Share";
import zrokLogo from "./assets/zrok-1.0.0-rocket-green.svg";
interface NavBarProps {
shareClick: () => void;
accessClick: () => void;
}
const NavBar = ({ shareClick, accessClick }: NavBarProps) => {
return (
<Box ssx={{ flexGrow: 1 }}>
<AppBar position="static">
<Toolbar>
<Typography variant="h6" sx={{ flexGrow: 1 }}>
<Grid2 container sx={{ flexGrow: 1 }}>
<Grid2 display="flex" justifyContent="left">
<img src={zrokLogo} height="30" />
</Grid2>
<Grid2 display="flex" justifyContent="left" size="grow" sx={{ ml: 3 }} color="#9bf316">
<strong>z r o k &nbsp; Agent</strong>
</Grid2>
</Grid2>
</Typography>
<Grid2 container sx={{ flexGrow: 1 }}>
<Grid2 display="flex" justifyContent="right" size="grow">
<Button color="inherit" onClick={shareClick}><ShareIcon /></Button>
</Grid2>
<Grid2 display="flex" justifyContent="right">
<Button color="inherit" onClick={accessClick}><LanIcon /></Button>
</Grid2>
</Grid2>
</Toolbar>
</AppBar>
</Box>
);
}
export default NavBar

View File

@ -0,0 +1,69 @@
import {useState} from "react";
import {useFormik} from "formik";
import {GetAgentApi} from "./model/api.ts";
import {Box, Button, Modal, TextField, Typography} from "@mui/material";
import {modalStyle} from "./model/theme.ts";
import * as React from "react";
interface NewAccessModalProps {
close: () => void;
isOpen: boolean;
}
const NewAccessModal = ({ close, isOpen }: NewAccessModalProps) => {
const [errorMessage, setErrorMessage] = useState(null as React.JSX.Element);
const newAccessForm = useFormik({
initialValues: {
token: "",
bindAddress: "",
},
onSubmit: v => {
setErrorMessage(null as React.JSX.Element);
GetAgentApi().agentAccessPrivate(v)
.then(r => {
close();
})
.catch(e => {
e.response.json().then(ex => {
setErrorMessage(<span>{ex.message}</span>);
console.log(ex.message);
})
});
}
});
return (
<Modal open={isOpen} onClose={close}>
<Box sx={{...modalStyle}}>
<Typography><h2>Access...</h2></Typography>
<Typography color="red"><h3>{errorMessage}</h3></Typography>
<form onSubmit={newAccessForm.handleSubmit}>
<TextField
fullWidth
id="token"
name="token"
label="Share Token"
value={newAccessForm.values.token}
onChange={newAccessForm.handleChange}
onBlur={newAccessForm.handleBlur}
sx={{mt: 2}}
/>
<TextField
fullWidth
id="bindAddress"
name="bindAddress"
label="Bind Address"
value={newAccessForm.values.bindAddress}
onChange={newAccessForm.handleChange}
onBlur={newAccessForm.handleBlur}
sx={{mt: 2}}
/>
<Button color="primary" variant="contained" type="submit" sx={{mt: 2}}>Create Access</Button>
</form>
</Box>
</Modal>
);
}
export default NewAccessModal;

View File

@ -0,0 +1,146 @@
import {useFormik} from "formik";
import {GetAgentApi} from "./model/api.ts";
import {useState} from "react";
import {Box, Button, Checkbox, FormControlLabel, MenuItem, Modal, TextField, Typography} from "@mui/material";
import {modalStyle} from "./model/theme.ts";
import * as React from "react";
interface NewShareModalProps {
close: () => void;
isOpen: boolean;
}
const NewShareModal = ({ close, isOpen }: NewShareModalProps) => {
const [errorMessage, setErrorMessage] = useState(null as React.JSX.Element);
const form = useFormik({
initialValues: {
shareMode: "public",
backendMode: "proxy",
target: "",
insecure: false,
},
onSubmit: v => {
setErrorMessage(null as React.JSX.Element);
switch(v.shareMode) {
case "public":
GetAgentApi().agentSharePublic(v)
.then(r => {
close();
})
.catch(e => {
e.response.json().then(ex => {
setErrorMessage(<span>{ex.message}</span>);
console.log(ex.message);
})
});
break;
case "private":
GetAgentApi().agentSharePrivate(v)
.then(r => {
close();
})
.catch(e => {
e.response().json().then(ex => {
setErrorMessage(<span>{ex.message}</span>);
console.log(ex.message);
})
});
break;
}
},
});
return (
<Modal open={isOpen} onClose={close}>
<Box sx={{ ...modalStyle }}>
<Typography><h2>Share...</h2></Typography>
<Typography color="red"><h3>{errorMessage}</h3></Typography>
<form onSubmit={form.handleSubmit}>
<TextField
fullWidth
select
id="shareMode"
name="shareMode"
label="Share Mode"
value={form.values.shareMode}
onChange={form.handleChange}
onBlur={form.handleBlur}
sx={{ mt: 2 }}
>
<MenuItem value="public">public</MenuItem>
<MenuItem value="private">private</MenuItem>
</TextField>
{form.values.shareMode === "public" && (
<TextField
fullWidth select
id="backendMode"
name="backendMode"
label="Backend Mode"
value={form.values.backendMode}
onChange={form.handleChange}
onBlur={form.handleBlur}
sx={{ mt: 2 }}
>
<MenuItem value="proxy">proxy</MenuItem>
<MenuItem value="web">web</MenuItem>
<MenuItem value="caddy">caddy</MenuItem>
<MenuItem value="drive">drive</MenuItem>
</TextField>
)}
{form.values.shareMode === "private" && (
<TextField
fullWidth select
id="backendMode"
name="backendMode"
label="Backend Mode"
value={form.values.backendMode}
onChange={form.handleChange}
onBlur={form.handleBlur}
sx={{ mt: 2 }}
>
<MenuItem value="proxy">proxy</MenuItem>
<MenuItem value="web">web</MenuItem>
<MenuItem value="tcpTunnel">tcpTunnel</MenuItem>
<MenuItem value="udpTunnel">udpTunnel</MenuItem>
<MenuItem value="caddy">caddy</MenuItem>
<MenuItem value="drive">drive</MenuItem>
<MenuItem value="socks">socks</MenuItem>
<MenuItem value="vpn">vpn</MenuItem>
</TextField>
)}
<TextField
fullWidth
id="target"
name="target"
label="Target"
value={form.values.target}
onChange={form.handleChange}
onBlur={form.handleBlur}
sx={{ mt: 2 }}
/>
{form.values.backendMode === "proxy" && (
<Box>
<FormControlLabel
control={<Checkbox
id="insecure"
name="insecure"
label="Insecure"
checked={form.values.insecure}
onChange={form.handleChange}
onBlur={form.handleBlur}
/>}
label="Insecure"
sx={{ mt: 2 }}
/>
</Box>
)}
<Button color="primary" variant="contained" type="submit" sx={{ mt: 2 }}>Create Share</Button>
</form>
</Box>
</Modal>
);
}
export default NewShareModal;

View File

@ -0,0 +1,47 @@
import {AgentObject} from "./model/overview.ts";
import {Box, Card, Grid2, Typography} from "@mui/material";
import ShareIcon from "@mui/icons-material/Share";
import LanIcon from "@mui/icons-material/Lan";
import ShareCard from "./ShareCard.tsx";
import AccessCard from "./AccessCard.tsx";
interface OverviewProps {
overview: Array<AgentObject>;
shareClick: () => void;
accessClick: () => void;
}
const Overview = ({ overview, shareClick, accessClick }: OverviewProps) => {
let cards = [];
if(overview.length > 0) {
overview.forEach(row => {
switch(row.type) {
case "access":
cards.push(<Grid2 size={{ xs: 12, md: 6 }}><AccessCard accessObject={row} /></Grid2>);
break;
case "share":
cards.push(<Grid2 size={{ xs: 12, md: 6 }}><ShareCard shareObject={row} /></Grid2>);
break;
}
});
} else {
cards.push(<Grid2 size={{ xs: 12 }}>
<Card key="empty">
<Box sx={{ p: 2, textAlign: "center" }}>
<Typography variant="h6" component="div">
zrok Agent is empty! Add a <a href={"#"} onClick={shareClick}>share <ShareIcon/></a> or <a
href={"#"} onClick={accessClick}>access <LanIcon/></a> share to get started.
</Typography>
</Box>
</Card>
</Grid2>);
}
return (
<Grid2 container spacing={2}>
{cards}
</Grid2>
);
}
export default Overview;

View File

@ -0,0 +1,59 @@
import * as React from "react";
import {AgentObject} from "./model/overview.ts";
import {ShareDetail} from "./api";
import {AppBar, Box, Button, Card, Chip, Grid2, Toolbar, Typography} from "@mui/material";
import ShareIcon from "@mui/icons-material/Share";
import DeleteIcon from "@mui/icons-material/Delete";
import {GetAgentApi} from "./model/api.ts";
interface ShareCardProps {
shareObject: AgentObject;
}
const ShareCard = ({ shareObject }: ShareCardProps) => {
let frontends = new Array<React.JSX.Element>();
let share = (shareObject.v as ShareDetail);
share.frontendEndpoint!.map(fe => {
frontends.push(<a key={share.token} href={fe} target="_">{fe}</a>);
});
const releaseShare = () => {
GetAgentApi().agentReleaseShare({token: share.token})
.catch(e => {
console.log(e);
});
}
return (
<Card>
<AppBar position="sticky">
<Toolbar variant="dense">
<ShareIcon />
<Grid2 container sx={{ flexGrow: 1 }}>
<Grid2 display="flex" justifyContent="center" size="grow">
<Typography variant="h6" component="div" style={{ color: "#9bf316" }}>{share.token}</Typography>
</Grid2>
</Grid2>
<Grid2 container>
<Grid2 display="flex" justifyContent="right">
<Chip label={share.shareMode} size="small" style={{ backgroundColor: "#9bf316" }} sx={{ mr: 1}} />
<Chip label={share.backendMode} size="small" style={{ backgroundColor: "#9bf316" }} />
</Grid2>
</Grid2>
</Toolbar>
</AppBar>
<Box sx={{ p: 2, textAlign: "center" }}>
<Typography variant="h6" component="div">
{share.backendEndpoint} &rarr; {frontends} <br/>
</Typography>
</Box>
<Grid2 container sx={{ flexGrow: 1 }}>
<Grid2 display="flex" justifyContent="right" size="grow">
<Button variant="contained" onClick={releaseShare}><DeleteIcon /></Button>
</Grid2>
</Grid2>
</Card>
);
}
export default ShareCard;

View File

@ -21,4 +21,3 @@
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md
git_push.sh

View File

@ -0,0 +1,16 @@
.openapi-generator-ignore
apis/AgentApi.ts
apis/index.ts
index.ts
models/AccessDetail.ts
models/AccessPrivateResponse.ts
models/ProtobufAny.ts
models/RpcStatus.ts
models/ShareDetail.ts
models/SharePrivateResponse.ts
models/SharePublicResponse.ts
models/ShareReservedResponse.ts
models/StatusResponse.ts
models/VersionResponse.ts
models/index.ts
runtime.ts

View File

@ -0,0 +1 @@
7.12.0

View File

@ -0,0 +1,348 @@
/* tslint:disable */
/* eslint-disable */
/**
* agent/agentGrpc/agent.proto
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: version not set
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import * as runtime from '../runtime';
import type {
AccessPrivateResponse,
RpcStatus,
SharePrivateResponse,
SharePublicResponse,
StatusResponse,
VersionResponse,
} from '../models/index';
import {
AccessPrivateResponseFromJSON,
AccessPrivateResponseToJSON,
RpcStatusFromJSON,
RpcStatusToJSON,
SharePrivateResponseFromJSON,
SharePrivateResponseToJSON,
SharePublicResponseFromJSON,
SharePublicResponseToJSON,
StatusResponseFromJSON,
StatusResponseToJSON,
VersionResponseFromJSON,
VersionResponseToJSON,
} from '../models/index';
export interface AgentAccessPrivateRequest {
token?: string;
bindAddress?: string;
autoMode?: boolean;
autoAddress?: string;
autoStartPort?: number;
autoEndPort?: number;
responseHeaders?: Array<string>;
}
export interface AgentReleaseAccessRequest {
frontendToken?: string;
}
export interface AgentReleaseShareRequest {
token?: string;
}
export interface AgentSharePrivateRequest {
target?: string;
backendMode?: string;
insecure?: boolean;
closed?: boolean;
accessGrants?: Array<string>;
}
export interface AgentSharePublicRequest {
target?: string;
basicAuth?: Array<string>;
frontendSelection?: Array<string>;
backendMode?: string;
insecure?: boolean;
oauthProvider?: string;
oauthEmailAddressPatterns?: Array<string>;
oauthCheckInterval?: string;
closed?: boolean;
accessGrants?: Array<string>;
}
/**
*
*/
export class AgentApi extends runtime.BaseAPI {
/**
*/
async agentAccessPrivateRaw(requestParameters: AgentAccessPrivateRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<AccessPrivateResponse>> {
const queryParameters: any = {};
if (requestParameters['token'] != null) {
queryParameters['token'] = requestParameters['token'];
}
if (requestParameters['bindAddress'] != null) {
queryParameters['bindAddress'] = requestParameters['bindAddress'];
}
if (requestParameters['autoMode'] != null) {
queryParameters['autoMode'] = requestParameters['autoMode'];
}
if (requestParameters['autoAddress'] != null) {
queryParameters['autoAddress'] = requestParameters['autoAddress'];
}
if (requestParameters['autoStartPort'] != null) {
queryParameters['autoStartPort'] = requestParameters['autoStartPort'];
}
if (requestParameters['autoEndPort'] != null) {
queryParameters['autoEndPort'] = requestParameters['autoEndPort'];
}
if (requestParameters['responseHeaders'] != null) {
queryParameters['responseHeaders'] = requestParameters['responseHeaders'];
}
const headerParameters: runtime.HTTPHeaders = {};
const response = await this.request({
path: `/v1/agent/accessPrivate`,
method: 'POST',
headers: headerParameters,
query: queryParameters,
}, initOverrides);
return new runtime.JSONApiResponse(response, (jsonValue) => AccessPrivateResponseFromJSON(jsonValue));
}
/**
*/
async agentAccessPrivate(requestParameters: AgentAccessPrivateRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<AccessPrivateResponse> {
const response = await this.agentAccessPrivateRaw(requestParameters, initOverrides);
return await response.value();
}
/**
*/
async agentReleaseAccessRaw(requestParameters: AgentReleaseAccessRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<object>> {
const queryParameters: any = {};
if (requestParameters['frontendToken'] != null) {
queryParameters['frontendToken'] = requestParameters['frontendToken'];
}
const headerParameters: runtime.HTTPHeaders = {};
const response = await this.request({
path: `/v1/agent/releaseAccess`,
method: 'POST',
headers: headerParameters,
query: queryParameters,
}, initOverrides);
return new runtime.JSONApiResponse<any>(response);
}
/**
*/
async agentReleaseAccess(requestParameters: AgentReleaseAccessRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<object> {
const response = await this.agentReleaseAccessRaw(requestParameters, initOverrides);
return await response.value();
}
/**
*/
async agentReleaseShareRaw(requestParameters: AgentReleaseShareRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<object>> {
const queryParameters: any = {};
if (requestParameters['token'] != null) {
queryParameters['token'] = requestParameters['token'];
}
const headerParameters: runtime.HTTPHeaders = {};
const response = await this.request({
path: `/v1/agent/releaseShare`,
method: 'POST',
headers: headerParameters,
query: queryParameters,
}, initOverrides);
return new runtime.JSONApiResponse<any>(response);
}
/**
*/
async agentReleaseShare(requestParameters: AgentReleaseShareRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<object> {
const response = await this.agentReleaseShareRaw(requestParameters, initOverrides);
return await response.value();
}
/**
*/
async agentSharePrivateRaw(requestParameters: AgentSharePrivateRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<SharePrivateResponse>> {
const queryParameters: any = {};
if (requestParameters['target'] != null) {
queryParameters['target'] = requestParameters['target'];
}
if (requestParameters['backendMode'] != null) {
queryParameters['backendMode'] = requestParameters['backendMode'];
}
if (requestParameters['insecure'] != null) {
queryParameters['insecure'] = requestParameters['insecure'];
}
if (requestParameters['closed'] != null) {
queryParameters['closed'] = requestParameters['closed'];
}
if (requestParameters['accessGrants'] != null) {
queryParameters['accessGrants'] = requestParameters['accessGrants'];
}
const headerParameters: runtime.HTTPHeaders = {};
const response = await this.request({
path: `/v1/agent/sharePrivate`,
method: 'POST',
headers: headerParameters,
query: queryParameters,
}, initOverrides);
return new runtime.JSONApiResponse(response, (jsonValue) => SharePrivateResponseFromJSON(jsonValue));
}
/**
*/
async agentSharePrivate(requestParameters: AgentSharePrivateRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<SharePrivateResponse> {
const response = await this.agentSharePrivateRaw(requestParameters, initOverrides);
return await response.value();
}
/**
*/
async agentSharePublicRaw(requestParameters: AgentSharePublicRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<SharePublicResponse>> {
const queryParameters: any = {};
if (requestParameters['target'] != null) {
queryParameters['target'] = requestParameters['target'];
}
if (requestParameters['basicAuth'] != null) {
queryParameters['basicAuth'] = requestParameters['basicAuth'];
}
if (requestParameters['frontendSelection'] != null) {
queryParameters['frontendSelection'] = requestParameters['frontendSelection'];
}
if (requestParameters['backendMode'] != null) {
queryParameters['backendMode'] = requestParameters['backendMode'];
}
if (requestParameters['insecure'] != null) {
queryParameters['insecure'] = requestParameters['insecure'];
}
if (requestParameters['oauthProvider'] != null) {
queryParameters['oauthProvider'] = requestParameters['oauthProvider'];
}
if (requestParameters['oauthEmailAddressPatterns'] != null) {
queryParameters['oauthEmailAddressPatterns'] = requestParameters['oauthEmailAddressPatterns'];
}
if (requestParameters['oauthCheckInterval'] != null) {
queryParameters['oauthCheckInterval'] = requestParameters['oauthCheckInterval'];
}
if (requestParameters['closed'] != null) {
queryParameters['closed'] = requestParameters['closed'];
}
if (requestParameters['accessGrants'] != null) {
queryParameters['accessGrants'] = requestParameters['accessGrants'];
}
const headerParameters: runtime.HTTPHeaders = {};
const response = await this.request({
path: `/v1/agent/sharePublic`,
method: 'POST',
headers: headerParameters,
query: queryParameters,
}, initOverrides);
return new runtime.JSONApiResponse(response, (jsonValue) => SharePublicResponseFromJSON(jsonValue));
}
/**
*/
async agentSharePublic(requestParameters: AgentSharePublicRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<SharePublicResponse> {
const response = await this.agentSharePublicRaw(requestParameters, initOverrides);
return await response.value();
}
/**
*/
async agentStatusRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<StatusResponse>> {
const queryParameters: any = {};
const headerParameters: runtime.HTTPHeaders = {};
const response = await this.request({
path: `/v1/agent/status`,
method: 'GET',
headers: headerParameters,
query: queryParameters,
}, initOverrides);
return new runtime.JSONApiResponse(response, (jsonValue) => StatusResponseFromJSON(jsonValue));
}
/**
*/
async agentStatus(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<StatusResponse> {
const response = await this.agentStatusRaw(initOverrides);
return await response.value();
}
/**
*/
async agentVersionRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<VersionResponse>> {
const queryParameters: any = {};
const headerParameters: runtime.HTTPHeaders = {};
const response = await this.request({
path: `/v1/agent/version`,
method: 'GET',
headers: headerParameters,
query: queryParameters,
}, initOverrides);
return new runtime.JSONApiResponse(response, (jsonValue) => VersionResponseFromJSON(jsonValue));
}
/**
*/
async agentVersion(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<VersionResponse> {
const response = await this.agentVersionRaw(initOverrides);
return await response.value();
}
}

View File

@ -0,0 +1,3 @@
/* tslint:disable */
/* eslint-disable */
export * from './AgentApi';

View File

@ -0,0 +1,89 @@
/* tslint:disable */
/* eslint-disable */
/**
* agent/agentGrpc/agent.proto
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: version not set
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
/**
*
* @export
* @interface AccessDetail
*/
export interface AccessDetail {
/**
*
* @type {string}
* @memberof AccessDetail
*/
frontendToken?: string;
/**
*
* @type {string}
* @memberof AccessDetail
*/
token?: string;
/**
*
* @type {string}
* @memberof AccessDetail
*/
bindAddress?: string;
/**
*
* @type {Array<string>}
* @memberof AccessDetail
*/
responseHeaders?: Array<string>;
}
/**
* Check if a given object implements the AccessDetail interface.
*/
export function instanceOfAccessDetail(value: object): value is AccessDetail {
return true;
}
export function AccessDetailFromJSON(json: any): AccessDetail {
return AccessDetailFromJSONTyped(json, false);
}
export function AccessDetailFromJSONTyped(json: any, ignoreDiscriminator: boolean): AccessDetail {
if (json == null) {
return json;
}
return {
'frontendToken': json['frontendToken'] == null ? undefined : json['frontendToken'],
'token': json['token'] == null ? undefined : json['token'],
'bindAddress': json['bindAddress'] == null ? undefined : json['bindAddress'],
'responseHeaders': json['responseHeaders'] == null ? undefined : json['responseHeaders'],
};
}
export function AccessDetailToJSON(json: any): AccessDetail {
return AccessDetailToJSONTyped(json, false);
}
export function AccessDetailToJSONTyped(value?: AccessDetail | null, ignoreDiscriminator: boolean = false): any {
if (value == null) {
return value;
}
return {
'frontendToken': value['frontendToken'],
'token': value['token'],
'bindAddress': value['bindAddress'],
'responseHeaders': value['responseHeaders'],
};
}

View File

@ -0,0 +1,65 @@
/* tslint:disable */
/* eslint-disable */
/**
* agent/agentGrpc/agent.proto
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: version not set
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
/**
*
* @export
* @interface AccessPrivateResponse
*/
export interface AccessPrivateResponse {
/**
*
* @type {string}
* @memberof AccessPrivateResponse
*/
frontendToken?: string;
}
/**
* Check if a given object implements the AccessPrivateResponse interface.
*/
export function instanceOfAccessPrivateResponse(value: object): value is AccessPrivateResponse {
return true;
}
export function AccessPrivateResponseFromJSON(json: any): AccessPrivateResponse {
return AccessPrivateResponseFromJSONTyped(json, false);
}
export function AccessPrivateResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): AccessPrivateResponse {
if (json == null) {
return json;
}
return {
'frontendToken': json['frontendToken'] == null ? undefined : json['frontendToken'],
};
}
export function AccessPrivateResponseToJSON(json: any): AccessPrivateResponse {
return AccessPrivateResponseToJSONTyped(json, false);
}
export function AccessPrivateResponseToJSONTyped(value?: AccessPrivateResponse | null, ignoreDiscriminator: boolean = false): any {
if (value == null) {
return value;
}
return {
'frontendToken': value['frontendToken'],
};
}

View File

@ -0,0 +1,68 @@
/* tslint:disable */
/* eslint-disable */
/**
* agent/agentGrpc/agent.proto
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: version not set
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
/**
*
* @export
* @interface ProtobufAny
*/
export interface ProtobufAny {
[key: string]: object | any;
/**
*
* @type {string}
* @memberof ProtobufAny
*/
type?: string;
}
/**
* Check if a given object implements the ProtobufAny interface.
*/
export function instanceOfProtobufAny(value: object): value is ProtobufAny {
return true;
}
export function ProtobufAnyFromJSON(json: any): ProtobufAny {
return ProtobufAnyFromJSONTyped(json, false);
}
export function ProtobufAnyFromJSONTyped(json: any, ignoreDiscriminator: boolean): ProtobufAny {
if (json == null) {
return json;
}
return {
...json,
'type': json['@type'] == null ? undefined : json['@type'],
};
}
export function ProtobufAnyToJSON(json: any): ProtobufAny {
return ProtobufAnyToJSONTyped(json, false);
}
export function ProtobufAnyToJSONTyped(value?: ProtobufAny | null, ignoreDiscriminator: boolean = false): any {
if (value == null) {
return value;
}
return {
...value,
'@type': value['type'],
};
}

View File

@ -0,0 +1,89 @@
/* tslint:disable */
/* eslint-disable */
/**
* agent/agentGrpc/agent.proto
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: version not set
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
import type { ProtobufAny } from './ProtobufAny';
import {
ProtobufAnyFromJSON,
ProtobufAnyFromJSONTyped,
ProtobufAnyToJSON,
ProtobufAnyToJSONTyped,
} from './ProtobufAny';
/**
*
* @export
* @interface RpcStatus
*/
export interface RpcStatus {
/**
*
* @type {number}
* @memberof RpcStatus
*/
code?: number;
/**
*
* @type {string}
* @memberof RpcStatus
*/
message?: string;
/**
*
* @type {Array<ProtobufAny>}
* @memberof RpcStatus
*/
details?: Array<ProtobufAny>;
}
/**
* Check if a given object implements the RpcStatus interface.
*/
export function instanceOfRpcStatus(value: object): value is RpcStatus {
return true;
}
export function RpcStatusFromJSON(json: any): RpcStatus {
return RpcStatusFromJSONTyped(json, false);
}
export function RpcStatusFromJSONTyped(json: any, ignoreDiscriminator: boolean): RpcStatus {
if (json == null) {
return json;
}
return {
'code': json['code'] == null ? undefined : json['code'],
'message': json['message'] == null ? undefined : json['message'],
'details': json['details'] == null ? undefined : ((json['details'] as Array<any>).map(ProtobufAnyFromJSON)),
};
}
export function RpcStatusToJSON(json: any): RpcStatus {
return RpcStatusToJSONTyped(json, false);
}
export function RpcStatusToJSONTyped(value?: RpcStatus | null, ignoreDiscriminator: boolean = false): any {
if (value == null) {
return value;
}
return {
'code': value['code'],
'message': value['message'],
'details': value['details'] == null ? undefined : ((value['details'] as Array<any>).map(ProtobufAnyToJSON)),
};
}

View File

@ -0,0 +1,121 @@
/* tslint:disable */
/* eslint-disable */
/**
* agent/agentGrpc/agent.proto
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: version not set
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
/**
*
* @export
* @interface ShareDetail
*/
export interface ShareDetail {
/**
*
* @type {string}
* @memberof ShareDetail
*/
token?: string;
/**
*
* @type {string}
* @memberof ShareDetail
*/
shareMode?: string;
/**
*
* @type {string}
* @memberof ShareDetail
*/
backendMode?: string;
/**
*
* @type {boolean}
* @memberof ShareDetail
*/
reserved?: boolean;
/**
*
* @type {Array<string>}
* @memberof ShareDetail
*/
frontendEndpoint?: Array<string>;
/**
*
* @type {string}
* @memberof ShareDetail
*/
backendEndpoint?: string;
/**
*
* @type {boolean}
* @memberof ShareDetail
*/
closed?: boolean;
/**
*
* @type {string}
* @memberof ShareDetail
*/
status?: string;
}
/**
* Check if a given object implements the ShareDetail interface.
*/
export function instanceOfShareDetail(value: object): value is ShareDetail {
return true;
}
export function ShareDetailFromJSON(json: any): ShareDetail {
return ShareDetailFromJSONTyped(json, false);
}
export function ShareDetailFromJSONTyped(json: any, ignoreDiscriminator: boolean): ShareDetail {
if (json == null) {
return json;
}
return {
'token': json['token'] == null ? undefined : json['token'],
'shareMode': json['shareMode'] == null ? undefined : json['shareMode'],
'backendMode': json['backendMode'] == null ? undefined : json['backendMode'],
'reserved': json['reserved'] == null ? undefined : json['reserved'],
'frontendEndpoint': json['frontendEndpoint'] == null ? undefined : json['frontendEndpoint'],
'backendEndpoint': json['backendEndpoint'] == null ? undefined : json['backendEndpoint'],
'closed': json['closed'] == null ? undefined : json['closed'],
'status': json['status'] == null ? undefined : json['status'],
};
}
export function ShareDetailToJSON(json: any): ShareDetail {
return ShareDetailToJSONTyped(json, false);
}
export function ShareDetailToJSONTyped(value?: ShareDetail | null, ignoreDiscriminator: boolean = false): any {
if (value == null) {
return value;
}
return {
'token': value['token'],
'shareMode': value['shareMode'],
'backendMode': value['backendMode'],
'reserved': value['reserved'],
'frontendEndpoint': value['frontendEndpoint'],
'backendEndpoint': value['backendEndpoint'],
'closed': value['closed'],
'status': value['status'],
};
}

View File

@ -0,0 +1,65 @@
/* tslint:disable */
/* eslint-disable */
/**
* agent/agentGrpc/agent.proto
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: version not set
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
/**
*
* @export
* @interface SharePrivateResponse
*/
export interface SharePrivateResponse {
/**
*
* @type {string}
* @memberof SharePrivateResponse
*/
token?: string;
}
/**
* Check if a given object implements the SharePrivateResponse interface.
*/
export function instanceOfSharePrivateResponse(value: object): value is SharePrivateResponse {
return true;
}
export function SharePrivateResponseFromJSON(json: any): SharePrivateResponse {
return SharePrivateResponseFromJSONTyped(json, false);
}
export function SharePrivateResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): SharePrivateResponse {
if (json == null) {
return json;
}
return {
'token': json['token'] == null ? undefined : json['token'],
};
}
export function SharePrivateResponseToJSON(json: any): SharePrivateResponse {
return SharePrivateResponseToJSONTyped(json, false);
}
export function SharePrivateResponseToJSONTyped(value?: SharePrivateResponse | null, ignoreDiscriminator: boolean = false): any {
if (value == null) {
return value;
}
return {
'token': value['token'],
};
}

View File

@ -0,0 +1,73 @@
/* tslint:disable */
/* eslint-disable */
/**
* agent/agentGrpc/agent.proto
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: version not set
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
/**
*
* @export
* @interface SharePublicResponse
*/
export interface SharePublicResponse {
/**
*
* @type {string}
* @memberof SharePublicResponse
*/
token?: string;
/**
*
* @type {Array<string>}
* @memberof SharePublicResponse
*/
frontendEndpoints?: Array<string>;
}
/**
* Check if a given object implements the SharePublicResponse interface.
*/
export function instanceOfSharePublicResponse(value: object): value is SharePublicResponse {
return true;
}
export function SharePublicResponseFromJSON(json: any): SharePublicResponse {
return SharePublicResponseFromJSONTyped(json, false);
}
export function SharePublicResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): SharePublicResponse {
if (json == null) {
return json;
}
return {
'token': json['token'] == null ? undefined : json['token'],
'frontendEndpoints': json['frontendEndpoints'] == null ? undefined : json['frontendEndpoints'],
};
}
export function SharePublicResponseToJSON(json: any): SharePublicResponse {
return SharePublicResponseToJSONTyped(json, false);
}
export function SharePublicResponseToJSONTyped(value?: SharePublicResponse | null, ignoreDiscriminator: boolean = false): any {
if (value == null) {
return value;
}
return {
'token': value['token'],
'frontendEndpoints': value['frontendEndpoints'],
};
}

View File

@ -0,0 +1,97 @@
/* tslint:disable */
/* eslint-disable */
/**
* agent/agentGrpc/agent.proto
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: version not set
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
/**
*
* @export
* @interface ShareReservedResponse
*/
export interface ShareReservedResponse {
/**
*
* @type {string}
* @memberof ShareReservedResponse
*/
token?: string;
/**
*
* @type {string}
* @memberof ShareReservedResponse
*/
backendMode?: string;
/**
*
* @type {string}
* @memberof ShareReservedResponse
*/
shareMode?: string;
/**
*
* @type {Array<string>}
* @memberof ShareReservedResponse
*/
frontendEndpoints?: Array<string>;
/**
*
* @type {string}
* @memberof ShareReservedResponse
*/
target?: string;
}
/**
* Check if a given object implements the ShareReservedResponse interface.
*/
export function instanceOfShareReservedResponse(value: object): value is ShareReservedResponse {
return true;
}
export function ShareReservedResponseFromJSON(json: any): ShareReservedResponse {
return ShareReservedResponseFromJSONTyped(json, false);
}
export function ShareReservedResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): ShareReservedResponse {
if (json == null) {
return json;
}
return {
'token': json['token'] == null ? undefined : json['token'],
'backendMode': json['backendMode'] == null ? undefined : json['backendMode'],
'shareMode': json['shareMode'] == null ? undefined : json['shareMode'],
'frontendEndpoints': json['frontendEndpoints'] == null ? undefined : json['frontendEndpoints'],
'target': json['target'] == null ? undefined : json['target'],
};
}
export function ShareReservedResponseToJSON(json: any): ShareReservedResponse {
return ShareReservedResponseToJSONTyped(json, false);
}
export function ShareReservedResponseToJSONTyped(value?: ShareReservedResponse | null, ignoreDiscriminator: boolean = false): any {
if (value == null) {
return value;
}
return {
'token': value['token'],
'backendMode': value['backendMode'],
'shareMode': value['shareMode'],
'frontendEndpoints': value['frontendEndpoints'],
'target': value['target'],
};
}

View File

@ -0,0 +1,88 @@
/* tslint:disable */
/* eslint-disable */
/**
* agent/agentGrpc/agent.proto
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: version not set
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
import type { AccessDetail } from './AccessDetail';
import {
AccessDetailFromJSON,
AccessDetailFromJSONTyped,
AccessDetailToJSON,
AccessDetailToJSONTyped,
} from './AccessDetail';
import type { ShareDetail } from './ShareDetail';
import {
ShareDetailFromJSON,
ShareDetailFromJSONTyped,
ShareDetailToJSON,
ShareDetailToJSONTyped,
} from './ShareDetail';
/**
*
* @export
* @interface StatusResponse
*/
export interface StatusResponse {
/**
*
* @type {Array<AccessDetail>}
* @memberof StatusResponse
*/
accesses?: Array<AccessDetail>;
/**
*
* @type {Array<ShareDetail>}
* @memberof StatusResponse
*/
shares?: Array<ShareDetail>;
}
/**
* Check if a given object implements the StatusResponse interface.
*/
export function instanceOfStatusResponse(value: object): value is StatusResponse {
return true;
}
export function StatusResponseFromJSON(json: any): StatusResponse {
return StatusResponseFromJSONTyped(json, false);
}
export function StatusResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): StatusResponse {
if (json == null) {
return json;
}
return {
'accesses': json['accesses'] == null ? undefined : ((json['accesses'] as Array<any>).map(AccessDetailFromJSON)),
'shares': json['shares'] == null ? undefined : ((json['shares'] as Array<any>).map(ShareDetailFromJSON)),
};
}
export function StatusResponseToJSON(json: any): StatusResponse {
return StatusResponseToJSONTyped(json, false);
}
export function StatusResponseToJSONTyped(value?: StatusResponse | null, ignoreDiscriminator: boolean = false): any {
if (value == null) {
return value;
}
return {
'accesses': value['accesses'] == null ? undefined : ((value['accesses'] as Array<any>).map(AccessDetailToJSON)),
'shares': value['shares'] == null ? undefined : ((value['shares'] as Array<any>).map(ShareDetailToJSON)),
};
}

View File

@ -0,0 +1,73 @@
/* tslint:disable */
/* eslint-disable */
/**
* agent/agentGrpc/agent.proto
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: version not set
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
/**
*
* @export
* @interface VersionResponse
*/
export interface VersionResponse {
/**
*
* @type {string}
* @memberof VersionResponse
*/
v?: string;
/**
*
* @type {string}
* @memberof VersionResponse
*/
consoleEndpoint?: string;
}
/**
* Check if a given object implements the VersionResponse interface.
*/
export function instanceOfVersionResponse(value: object): value is VersionResponse {
return true;
}
export function VersionResponseFromJSON(json: any): VersionResponse {
return VersionResponseFromJSONTyped(json, false);
}
export function VersionResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): VersionResponse {
if (json == null) {
return json;
}
return {
'v': json['v'] == null ? undefined : json['v'],
'consoleEndpoint': json['consoleEndpoint'] == null ? undefined : json['consoleEndpoint'],
};
}
export function VersionResponseToJSON(json: any): VersionResponse {
return VersionResponseToJSONTyped(json, false);
}
export function VersionResponseToJSONTyped(value?: VersionResponse | null, ignoreDiscriminator: boolean = false): any {
if (value == null) {
return value;
}
return {
'v': value['v'],
'consoleEndpoint': value['consoleEndpoint'],
};
}

View File

@ -0,0 +1,12 @@
/* tslint:disable */
/* eslint-disable */
export * from './AccessDetail';
export * from './AccessPrivateResponse';
export * from './ProtobufAny';
export * from './RpcStatus';
export * from './ShareDetail';
export * from './SharePrivateResponse';
export * from './SharePublicResponse';
export * from './ShareReservedResponse';
export * from './StatusResponse';
export * from './VersionResponse';

View File

@ -0,0 +1,431 @@
/* tslint:disable */
/* eslint-disable */
/**
* agent/agentGrpc/agent.proto
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: version not set
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
export const BASE_PATH = "http://localhost".replace(/\/+$/, "");
export interface ConfigurationParameters {
basePath?: string; // override base path
fetchApi?: FetchAPI; // override for fetch implementation
middleware?: Middleware[]; // middleware to apply before/after fetch requests
queryParamsStringify?: (params: HTTPQuery) => string; // stringify function for query strings
username?: string; // parameter for basic security
password?: string; // parameter for basic security
apiKey?: string | Promise<string> | ((name: string) => string | Promise<string>); // parameter for apiKey security
accessToken?: string | Promise<string> | ((name?: string, scopes?: string[]) => string | Promise<string>); // parameter for oauth2 security
headers?: HTTPHeaders; //header params we want to use on every request
credentials?: RequestCredentials; //value for the credentials param we want to use on each request
}
export class Configuration {
constructor(private configuration: ConfigurationParameters = {}) {}
set config(configuration: Configuration) {
this.configuration = configuration;
}
get basePath(): string {
return this.configuration.basePath != null ? this.configuration.basePath : BASE_PATH;
}
get fetchApi(): FetchAPI | undefined {
return this.configuration.fetchApi;
}
get middleware(): Middleware[] {
return this.configuration.middleware || [];
}
get queryParamsStringify(): (params: HTTPQuery) => string {
return this.configuration.queryParamsStringify || querystring;
}
get username(): string | undefined {
return this.configuration.username;
}
get password(): string | undefined {
return this.configuration.password;
}
get apiKey(): ((name: string) => string | Promise<string>) | undefined {
const apiKey = this.configuration.apiKey;
if (apiKey) {
return typeof apiKey === 'function' ? apiKey : () => apiKey;
}
return undefined;
}
get accessToken(): ((name?: string, scopes?: string[]) => string | Promise<string>) | undefined {
const accessToken = this.configuration.accessToken;
if (accessToken) {
return typeof accessToken === 'function' ? accessToken : async () => accessToken;
}
return undefined;
}
get headers(): HTTPHeaders | undefined {
return this.configuration.headers;
}
get credentials(): RequestCredentials | undefined {
return this.configuration.credentials;
}
}
export const DefaultConfig = new Configuration();
/**
* This is the base class for all generated API classes.
*/
export class BaseAPI {
private static readonly jsonRegex = new RegExp('^(:?application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(:?;.*)?$', 'i');
private middleware: Middleware[];
constructor(protected configuration = DefaultConfig) {
this.middleware = configuration.middleware;
}
withMiddleware<T extends BaseAPI>(this: T, ...middlewares: Middleware[]) {
const next = this.clone<T>();
next.middleware = next.middleware.concat(...middlewares);
return next;
}
withPreMiddleware<T extends BaseAPI>(this: T, ...preMiddlewares: Array<Middleware['pre']>) {
const middlewares = preMiddlewares.map((pre) => ({ pre }));
return this.withMiddleware<T>(...middlewares);
}
withPostMiddleware<T extends BaseAPI>(this: T, ...postMiddlewares: Array<Middleware['post']>) {
const middlewares = postMiddlewares.map((post) => ({ post }));
return this.withMiddleware<T>(...middlewares);
}
/**
* Check if the given MIME is a JSON MIME.
* JSON MIME examples:
* application/json
* application/json; charset=UTF8
* APPLICATION/JSON
* application/vnd.company+json
* @param mime - MIME (Multipurpose Internet Mail Extensions)
* @return True if the given MIME is JSON, false otherwise.
*/
protected isJsonMime(mime: string | null | undefined): boolean {
if (!mime) {
return false;
}
return BaseAPI.jsonRegex.test(mime);
}
protected async request(context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction): Promise<Response> {
const { url, init } = await this.createFetchParams(context, initOverrides);
const response = await this.fetchApi(url, init);
if (response && (response.status >= 200 && response.status < 300)) {
return response;
}
throw new ResponseError(response, 'Response returned an error code');
}
private async createFetchParams(context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction) {
let url = this.configuration.basePath + context.path;
if (context.query !== undefined && Object.keys(context.query).length !== 0) {
// only add the querystring to the URL if there are query parameters.
// this is done to avoid urls ending with a "?" character which buggy webservers
// do not handle correctly sometimes.
url += '?' + this.configuration.queryParamsStringify(context.query);
}
const headers = Object.assign({}, this.configuration.headers, context.headers);
Object.keys(headers).forEach(key => headers[key] === undefined ? delete headers[key] : {});
const initOverrideFn =
typeof initOverrides === "function"
? initOverrides
: async () => initOverrides;
const initParams = {
method: context.method,
headers,
body: context.body,
credentials: this.configuration.credentials,
};
const overriddenInit: RequestInit = {
...initParams,
...(await initOverrideFn({
init: initParams,
context,
}))
};
let body: any;
if (isFormData(overriddenInit.body)
|| (overriddenInit.body instanceof URLSearchParams)
|| isBlob(overriddenInit.body)) {
body = overriddenInit.body;
} else if (this.isJsonMime(headers['Content-Type'])) {
body = JSON.stringify(overriddenInit.body);
} else {
body = overriddenInit.body;
}
const init: RequestInit = {
...overriddenInit,
body
};
return { url, init };
}
private fetchApi = async (url: string, init: RequestInit) => {
let fetchParams = { url, init };
for (const middleware of this.middleware) {
if (middleware.pre) {
fetchParams = await middleware.pre({
fetch: this.fetchApi,
...fetchParams,
}) || fetchParams;
}
}
let response: Response | undefined = undefined;
try {
response = await (this.configuration.fetchApi || fetch)(fetchParams.url, fetchParams.init);
} catch (e) {
for (const middleware of this.middleware) {
if (middleware.onError) {
response = await middleware.onError({
fetch: this.fetchApi,
url: fetchParams.url,
init: fetchParams.init,
error: e,
response: response ? response.clone() : undefined,
}) || response;
}
}
if (response === undefined) {
if (e instanceof Error) {
throw new FetchError(e, 'The request failed and the interceptors did not return an alternative response');
} else {
throw e;
}
}
}
for (const middleware of this.middleware) {
if (middleware.post) {
response = await middleware.post({
fetch: this.fetchApi,
url: fetchParams.url,
init: fetchParams.init,
response: response.clone(),
}) || response;
}
}
return response;
}
/**
* Create a shallow clone of `this` by constructing a new instance
* and then shallow cloning data members.
*/
private clone<T extends BaseAPI>(this: T): T {
const constructor = this.constructor as any;
const next = new constructor(this.configuration);
next.middleware = this.middleware.slice();
return next;
}
};
function isBlob(value: any): value is Blob {
return typeof Blob !== 'undefined' && value instanceof Blob;
}
function isFormData(value: any): value is FormData {
return typeof FormData !== "undefined" && value instanceof FormData;
}
export class ResponseError extends Error {
override name: "ResponseError" = "ResponseError";
constructor(public response: Response, msg?: string) {
super(msg);
}
}
export class FetchError extends Error {
override name: "FetchError" = "FetchError";
constructor(public cause: Error, msg?: string) {
super(msg);
}
}
export class RequiredError extends Error {
override name: "RequiredError" = "RequiredError";
constructor(public field: string, msg?: string) {
super(msg);
}
}
export const COLLECTION_FORMATS = {
csv: ",",
ssv: " ",
tsv: "\t",
pipes: "|",
};
export type FetchAPI = WindowOrWorkerGlobalScope['fetch'];
export type Json = any;
export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD';
export type HTTPHeaders = { [key: string]: string };
export type HTTPQuery = { [key: string]: string | number | null | boolean | Array<string | number | null | boolean> | Set<string | number | null | boolean> | HTTPQuery };
export type HTTPBody = Json | FormData | URLSearchParams;
export type HTTPRequestInit = { headers?: HTTPHeaders; method: HTTPMethod; credentials?: RequestCredentials; body?: HTTPBody };
export type ModelPropertyNaming = 'camelCase' | 'snake_case' | 'PascalCase' | 'original';
export type InitOverrideFunction = (requestContext: { init: HTTPRequestInit, context: RequestOpts }) => Promise<RequestInit>
export interface FetchParams {
url: string;
init: RequestInit;
}
export interface RequestOpts {
path: string;
method: HTTPMethod;
headers: HTTPHeaders;
query?: HTTPQuery;
body?: HTTPBody;
}
export function querystring(params: HTTPQuery, prefix: string = ''): string {
return Object.keys(params)
.map(key => querystringSingleKey(key, params[key], prefix))
.filter(part => part.length > 0)
.join('&');
}
function querystringSingleKey(key: string, value: string | number | null | undefined | boolean | Array<string | number | null | boolean> | Set<string | number | null | boolean> | HTTPQuery, keyPrefix: string = ''): string {
const fullKey = keyPrefix + (keyPrefix.length ? `[${key}]` : key);
if (value instanceof Array) {
const multiValue = value.map(singleValue => encodeURIComponent(String(singleValue)))
.join(`&${encodeURIComponent(fullKey)}=`);
return `${encodeURIComponent(fullKey)}=${multiValue}`;
}
if (value instanceof Set) {
const valueAsArray = Array.from(value);
return querystringSingleKey(key, valueAsArray, keyPrefix);
}
if (value instanceof Date) {
return `${encodeURIComponent(fullKey)}=${encodeURIComponent(value.toISOString())}`;
}
if (value instanceof Object) {
return querystring(value as HTTPQuery, fullKey);
}
return `${encodeURIComponent(fullKey)}=${encodeURIComponent(String(value))}`;
}
export function exists(json: any, key: string) {
const value = json[key];
return value !== null && value !== undefined;
}
export function mapValues(data: any, fn: (item: any) => any) {
return Object.keys(data).reduce(
(acc, key) => ({ ...acc, [key]: fn(data[key]) }),
{}
);
}
export function canConsumeForm(consumes: Consume[]): boolean {
for (const consume of consumes) {
if ('multipart/form-data' === consume.contentType) {
return true;
}
}
return false;
}
export interface Consume {
contentType: string;
}
export interface RequestContext {
fetch: FetchAPI;
url: string;
init: RequestInit;
}
export interface ResponseContext {
fetch: FetchAPI;
url: string;
init: RequestInit;
response: Response;
}
export interface ErrorContext {
fetch: FetchAPI;
url: string;
init: RequestInit;
error: unknown;
response?: Response;
}
export interface Middleware {
pre?(context: RequestContext): Promise<FetchParams | void>;
post?(context: ResponseContext): Promise<Response | void>;
onError?(context: ErrorContext): Promise<Response | void>;
}
export interface ApiResponse<T> {
raw: Response;
value(): Promise<T>;
}
export interface ResponseTransformer<T> {
(json: any): T;
}
export class JSONApiResponse<T> {
constructor(public raw: Response, private transformer: ResponseTransformer<T> = (jsonValue: any) => jsonValue) {}
async value(): Promise<T> {
return this.transformer(await this.raw.json());
}
}
export class VoidApiResponse {
constructor(public raw: Response) {}
async value(): Promise<void> {
return undefined;
}
}
export class BlobApiResponse {
constructor(public raw: Response) {}
async value(): Promise<Blob> {
return await this.raw.blob();
};
}
export class TextApiResponse {
constructor(public raw: Response) {}
async value(): Promise<string> {
return await this.raw.text();
};
}

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="125.797mm"
height="166.26598mm"
viewBox="0 0 125.797 166.26598"
version="1.1"
id="svg1"
xml:space="preserve"
inkscape:version="1.4 (e7c3feb, 2024-10-09)"
sodipodi:docname="zrok-1.0.0-rocket-white.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showguides="false"
inkscape:zoom="0.85634716"
inkscape:cx="560.52034"
inkscape:cy="509.72318"
inkscape:window-width="1952"
inkscape:window-height="1304"
inkscape:window-x="1311"
inkscape:window-y="48"
inkscape:window-maximized="0"
inkscape:current-layer="layer1"><inkscape:page
x="0"
y="-4.4822158e-22"
width="125.797"
height="166.26598"
id="page2"
margin="0"
bleed="0" /></sodipodi:namedview><defs
id="defs1" /><g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-41.620475,-64.027978)"><path
id="path2"
style="fill:#9bf316;fill-opacity:1;stroke-width:0.865487"
d="m 104.52059,64.027974 c 0,0 -12.300998,16.684095 -17.163248,24.964306 -4.8623,8.28022 -7.86765,12.84754 -10.59501,26.3339 -2.72736,13.48636 -1.27406,42.21319 -1.27406,42.21319 l -18.58752,20.04004 -15.280273,49.9615 40.422833,-6.79427 a 22.714797,11.567473 0 0 0 22.320548,9.54731 22.714797,11.567473 0 0 0 22.32712,-9.55945 l 40.72649,6.84277 -15.28027,-49.95979 -18.58916,-20.04001 c 0,0 1.45496,-28.72857 -1.2724,-42.21493 -2.72736,-13.48636 -5.73437,-18.05368 -10.59667,-26.3339 -4.8608,-8.277646 -17.14905,-24.947062 -17.15668,-24.957378 z m -0.0151,14.741718 c 0.52421,0.860497 14.76063,18.300498 18.34492,32.586238 3.65093,14.55125 3.25036,30.16797 2.29067,46.75483 -0.68395,11.82081 -5.48912,37.13724 -8.2384,50.97445 a 22.714797,11.567473 0 0 0 -12.54073,-1.9254 22.714797,11.567473 0 0 0 -12.237098,1.84921 c -2.75047,-13.84452 -7.55003,-39.12307 -8.23343,-50.93462 -0.95969,-16.58687 -1.36026,-32.20531 2.29067,-46.75656 3.57561,-14.25109 17.733538,-31.579359 18.323398,-32.548148 z m -27.999198,95.069708 6.36686,35.53319 -30.30472,9.08328 10.94985,-35.7998 z m 56.021568,0.0351 12.988,8.81667 10.9482,35.80155 -30.30311,-9.08504 z"
inkscape:export-filename="zrok-1.0.0-rocket-white.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" /></g></svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,55 @@
body {
margin: 0;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
padding-bottom: 15px;
min-width: 320px;
min-height: 100vh;
}
code {
font-family: 'JetBrains Mono', sans-serif;
}
a {
font-weight: 500;
color: #241775;
text-decoration: inherit;
}
a:hover {
color: #9bf316;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
}
#footer {
text-align: center;
}

View File

@ -0,0 +1,14 @@
import "./index.css";
import {StrictMode} from "react";
import {createRoot} from "react-dom/client";
import {ThemeProvider} from "@mui/material";
import {theme} from "./model/theme.ts";
import AgentUi from "./AgentUi.tsx";
createRoot(document.getElementById('root')!).render(
<StrictMode>
<ThemeProvider theme={theme}>
<AgentUi />
</ThemeProvider>
</StrictMode>
);

View File

@ -0,0 +1,5 @@
import {AgentApi, Configuration} from "../api";
export const GetAgentApi = () => {
return new AgentApi(new Configuration({basePath: window.location.origin}));
}

View File

@ -0,0 +1,36 @@
import {AccessDetail, ShareDetail, StatusResponse} from "../api";
export class AgentObject {
type: string;
id: string;
v: (ShareDetail|AccessDetail);
}
export function buildOverview(status: StatusResponse): Array<AgentObject> {
let out = new Array<AgentObject>();
if(status) {
if(status.accesses) {
status.accesses.forEach(acc => {
let accObj = new AgentObject();
accObj.type = "access";
accObj.id = acc.frontendToken!;
accObj.v = acc;
out.push(accObj);
});
}
if(status.shares) {
status.shares.forEach(shr => {
let shrObj = new AgentObject();
shrObj.type = "share";
shrObj.id = shr.token!;
shrObj.v = shr;
out.push(shrObj);
});
}
out.sort((a, b) => {
if(a.id < b.id) return -1;
if(a.id > b.id) return 1;
});
}
return out;
}

View File

@ -0,0 +1,47 @@
import {createTheme} from "@mui/material";
const componentOptions = {
MuiCard: {
styleOverrides: {
root: ({theme}) => theme.unstable_sx({
mt: 5,
p: 1,
borderRadius: 3,
}),
}
},
MuiAppBar: {
styleOverrides: {
root : ({theme}) => theme.unstable_sx({
borderRadius: 3,
}),
}
}
}
export const theme = createTheme({
components: componentOptions,
palette: {
mode: 'light',
primary: {
main: '#241775',
},
secondary: {
main: '#9bf316',
},
},
typography: {
fontFamily: 'Poppins',
},
})
export const modalStyle = {
position: 'absolute',
top: '25%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 600,
bgcolor: 'background.paper',
boxShadow: 24,
p: 4,
};

1
agent/agentUi/src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="vite/client" />

View File

@ -0,0 +1,26 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}

View File

@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

View File

@ -0,0 +1,24 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}

View File

@ -0,0 +1,15 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
server: {
proxy: {
'/v1': {
target: 'http://localhost:8888',
changeOrigin: true,
}
}
}
})

15
agent/config.go Normal file
View File

@ -0,0 +1,15 @@
package agent
type AgentConfig struct {
ConsoleAddress string
ConsoleStartPort uint16
ConsoleEndPort uint16
}
func DefaultConfig() *AgentConfig {
return &AgentConfig{
ConsoleAddress: "127.0.0.1",
ConsoleStartPort: 8080,
ConsoleEndPort: 8181,
}
}

View File

@ -0,0 +1,59 @@
//go:build !windows
package proctree
import (
"os/exec"
"sync"
)
func Init(_ string) error {
return nil
}
func StartChild(tail TailFunction, args ...string) (*Child, error) {
cmd := exec.Command(args[0], args[1:]...)
cld := &Child{
TailFunction: tail,
cmd: cmd,
outStream: make(chan []byte),
errStream: make(chan []byte),
wg: new(sync.WaitGroup),
}
stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}
stderr, err := cmd.StderrPipe()
if err != nil {
return nil, err
}
if err := cmd.Start(); err != nil {
return nil, err
}
cld.wg.Add(3)
go reader(stdout, cld.outStream, cld.wg)
go reader(stderr, cld.errStream, cld.wg)
go cld.combiner(cld.wg)
return cld, nil
}
func WaitChild(c *Child) error {
c.wg.Wait()
if err := c.cmd.Wait(); err != nil {
return err
}
return nil
}
func StopChild(c *Child) error {
if err := c.cmd.Process.Kill(); err != nil {
return err
}
return nil
}

79
agent/proctree/impl_windows.go Executable file
View File

@ -0,0 +1,79 @@
//go:build windows
package proctree
import (
"github.com/kolesnikovae/go-winjob"
"golang.org/x/sys/windows"
"os/exec"
"sync"
)
var job *winjob.JobObject
func Init(name string) error {
var err error
if job == nil {
job, err = winjob.Create(name, winjob.LimitKillOnJobClose, winjob.LimitBreakawayOK)
if err != nil {
return err
}
}
return nil
}
func StartChild(tail TailFunction, args ...string) (*Child, error) {
cmd := exec.Command(args[0], args[1:]...)
cmd.SysProcAttr = &windows.SysProcAttr{CreationFlags: windows.CREATE_SUSPENDED}
cld := &Child{
TailFunction: tail,
cmd: cmd,
outStream: make(chan []byte),
errStream: make(chan []byte),
wg: new(sync.WaitGroup),
}
stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}
stderr, err := cmd.StderrPipe()
if err != nil {
return nil, err
}
if err := cmd.Start(); err != nil {
return nil, err
}
if err := job.Assign(cmd.Process); err != nil {
return nil, err
}
if err := winjob.ResumeProcess(cmd.Process.Pid); err != nil {
return nil, err
}
cld.wg.Add(3)
go reader(stdout, cld.outStream, cld.wg)
go reader(stderr, cld.errStream, cld.wg)
go cld.combiner(cld.wg)
return cld, nil
}
func WaitChild(c *Child) error {
c.wg.Wait()
if err := c.cmd.Wait(); err != nil {
return err
}
return nil
}
func StopChild(c *Child) error {
if err := c.cmd.Process.Kill(); err != nil {
return err
}
return nil
}

67
agent/proctree/proctree.go Executable file
View File

@ -0,0 +1,67 @@
package proctree
import (
"fmt"
_ "github.com/kolesnikovae/go-winjob"
"io"
"os/exec"
"sync"
)
type Child struct {
TailFunction TailFunction
cmd *exec.Cmd
outStream chan []byte
errStream chan []byte
wg *sync.WaitGroup
}
type TailFunction func(data []byte)
func (c *Child) combiner(wg *sync.WaitGroup) {
defer wg.Done()
outDone := false
errDone := false
for {
select {
case data := <-c.outStream:
if data != nil {
if c.TailFunction != nil {
c.TailFunction(data)
}
} else {
outDone = true
}
case data := <-c.errStream:
if data != nil {
if c.TailFunction != nil {
c.TailFunction(data)
}
} else {
errDone = true
}
}
if outDone && errDone {
return
}
}
}
func reader(r io.ReadCloser, o chan []byte, wg *sync.WaitGroup) {
defer close(o)
defer wg.Done()
buf := make([]byte, 64*1024)
for {
n, err := r.Read(buf)
if err != nil {
if err == io.EOF {
return
}
fmt.Printf("error reading: %v", err)
return
}
o <- buf[:n]
}
}

19
agent/releaseAccess.go Normal file
View File

@ -0,0 +1,19 @@
package agent
import (
"context"
"github.com/openziti/zrok/agent/agentGrpc"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
func (i *agentGrpcImpl) ReleaseAccess(_ context.Context, req *agentGrpc.ReleaseAccessRequest) (*agentGrpc.ReleaseAccessResponse, error) {
if acc, found := i.agent.accesses[req.FrontendToken]; found {
i.agent.rmAccess <- acc
logrus.Infof("released access '%v'", acc.frontendToken)
} else {
return nil, errors.Errorf("agent has no access with frontend token '%v'", req.FrontendToken)
}
return nil, nil
}

19
agent/releaseShare.go Executable file
View File

@ -0,0 +1,19 @@
package agent
import (
"context"
"github.com/openziti/zrok/agent/agentGrpc"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
func (i *agentGrpcImpl) ReleaseShare(_ context.Context, req *agentGrpc.ReleaseShareRequest) (*agentGrpc.ReleaseShareResponse, error) {
if shr, found := i.agent.shares[req.Token]; found {
i.agent.rmShare <- shr
logrus.Infof("released share '%v'", shr.token)
} else {
return nil, errors.Errorf("agent has no share with token '%v'", req.Token)
}
return nil, nil
}

83
agent/share.go Normal file
View File

@ -0,0 +1,83 @@
package agent
import (
"errors"
"github.com/michaelquigley/pfxlog"
"github.com/openziti/zrok/agent/proctree"
"github.com/openziti/zrok/cmd/zrok/subordinate"
"github.com/openziti/zrok/sdk/golang/sdk"
"time"
)
type share struct {
token string
frontendEndpoints []string
target string
basicAuth []string
frontendSelection []string
shareMode sdk.ShareMode
backendMode sdk.BackendMode
reserved bool
insecure bool
oauthProvider string
oauthEmailAddressPatterns []string
oauthCheckInterval time.Duration
closed bool
accessGrants []string
process *proctree.Child
sub *subordinate.MessageHandler
agent *Agent
}
func (s *share) monitor() {
if err := proctree.WaitChild(s.process); err != nil {
pfxlog.ChannelLogger(s.token).Error(err)
}
s.agent.rmShare <- s
}
func (s *share) bootHandler(msgType string, msg subordinate.Message) error {
switch msgType {
case subordinate.BootMessage:
if v, found := msg["token"]; found {
if str, ok := v.(string); ok {
s.token = str
}
}
if v, found := msg["backend_mode"]; found {
if str, ok := v.(string); ok {
s.backendMode = sdk.BackendMode(str)
}
}
if v, found := msg["share_mode"]; found {
if str, ok := v.(string); ok {
s.shareMode = sdk.ShareMode(str)
}
}
if v, found := msg["frontend_endpoints"]; found {
if vArr, ok := v.([]interface{}); ok {
for _, v := range vArr {
if str, ok := v.(string); ok {
s.frontendEndpoints = append(s.frontendEndpoints, str)
}
}
}
}
if v, found := msg["target"]; found {
if str, ok := v.(string); ok {
s.target = str
}
}
case subordinate.ErrorMessage:
if v, found := msg[subordinate.ErrorMessage]; found {
if str, ok := v.(string); ok {
return errors.New(str)
}
}
}
return nil
}

82
agent/sharePrivate.go Normal file
View File

@ -0,0 +1,82 @@
package agent
import (
"context"
"errors"
"fmt"
"github.com/openziti/zrok/agent/agentGrpc"
"github.com/openziti/zrok/agent/proctree"
"github.com/openziti/zrok/cmd/zrok/subordinate"
"github.com/openziti/zrok/environment"
"github.com/openziti/zrok/sdk/golang/sdk"
"github.com/sirupsen/logrus"
"os"
)
func (i *agentGrpcImpl) SharePrivate(_ context.Context, req *agentGrpc.SharePrivateRequest) (*agentGrpc.SharePrivateResponse, error) {
root, err := environment.LoadRoot()
if err != nil {
return nil, err
}
if !root.IsEnabled() {
return nil, errors.New("unable to load environment; did you 'zrok enable'?")
}
shrCmd := []string{os.Args[0], "share", "private", "--subordinate", "-b", req.BackendMode}
shr := &share{
shareMode: sdk.PrivateShareMode,
backendMode: sdk.BackendMode(req.BackendMode),
sub: subordinate.NewMessageHandler(),
agent: i.agent,
}
shr.sub.MessageHandler = func(msg subordinate.Message) {
logrus.Info(msg)
}
var bootErr error
shr.sub.BootHandler = func(msgType string, msg subordinate.Message) {
bootErr = shr.bootHandler(msgType, msg)
}
shr.sub.MalformedHandler = func(msg subordinate.Message) {
logrus.Error(msg)
}
if req.Insecure {
shrCmd = append(shrCmd, "--insecure")
}
shr.insecure = req.Insecure
if req.Closed {
shrCmd = append(shrCmd, "--closed")
}
shr.closed = req.Closed
for _, grant := range req.AccessGrants {
shrCmd = append(shrCmd, "--access-grant", grant)
}
shr.accessGrants = req.AccessGrants
shrCmd = append(shrCmd, req.Target)
shr.target = req.Target
logrus.Infof("executing '%v'", shrCmd)
shr.process, err = proctree.StartChild(shr.sub.Tail, shrCmd...)
if err != nil {
return nil, err
}
<-shr.sub.BootComplete
if bootErr == nil {
go shr.monitor()
i.agent.addShare <- shr
return &agentGrpc.SharePrivateResponse{Token: shr.token}, nil
} else {
if err := proctree.WaitChild(shr.process); err != nil {
logrus.Errorf("error joining: %v", err)
}
return nil, fmt.Errorf("unable to start share: %v", bootErr)
}
}

109
agent/sharePublic.go Normal file
View File

@ -0,0 +1,109 @@
package agent
import (
"context"
"errors"
"fmt"
"github.com/openziti/zrok/agent/agentGrpc"
"github.com/openziti/zrok/agent/proctree"
"github.com/openziti/zrok/cmd/zrok/subordinate"
"github.com/openziti/zrok/environment"
"github.com/openziti/zrok/sdk/golang/sdk"
"github.com/sirupsen/logrus"
"os"
)
func (i *agentGrpcImpl) SharePublic(_ context.Context, req *agentGrpc.SharePublicRequest) (*agentGrpc.SharePublicResponse, error) {
root, err := environment.LoadRoot()
if err != nil {
return nil, err
}
if !root.IsEnabled() {
return nil, errors.New("unable to load environment; did you 'zrok enable'?")
}
shrCmd := []string{os.Args[0], "share", "public", "--subordinate", "-b", req.BackendMode}
shr := &share{
shareMode: sdk.PublicShareMode,
backendMode: sdk.BackendMode(req.BackendMode),
sub: subordinate.NewMessageHandler(),
agent: i.agent,
}
shr.sub.MessageHandler = func(msg subordinate.Message) {
logrus.Info(msg)
}
var bootErr error
shr.sub.BootHandler = func(msgType string, msg subordinate.Message) {
bootErr = shr.bootHandler(msgType, msg)
}
shr.sub.MalformedHandler = func(msg subordinate.Message) {
logrus.Error(msg)
}
for _, basicAuth := range req.BasicAuth {
shrCmd = append(shrCmd, "--basic-auth", basicAuth)
}
shr.basicAuth = req.BasicAuth
for _, frontendSelection := range req.FrontendSelection {
shrCmd = append(shrCmd, "--frontend", frontendSelection)
}
shr.frontendSelection = req.FrontendSelection
if req.Insecure {
shrCmd = append(shrCmd, "--insecure")
}
shr.insecure = req.Insecure
if req.OauthProvider != "" {
shrCmd = append(shrCmd, "--oauth-provider", req.OauthProvider)
}
shr.oauthProvider = req.OauthProvider
for _, pattern := range req.OauthEmailAddressPatterns {
shrCmd = append(shrCmd, "--oauth-email-address-patterns", pattern)
}
shr.oauthEmailAddressPatterns = req.OauthEmailAddressPatterns
if req.OauthCheckInterval != "" {
shrCmd = append(shrCmd, "--oauth-check-interval", req.OauthCheckInterval)
}
if req.Closed {
shrCmd = append(shrCmd, "--closed")
}
shr.closed = req.Closed
for _, grant := range req.AccessGrants {
shrCmd = append(shrCmd, "--access-grant", grant)
}
shr.accessGrants = req.AccessGrants
shrCmd = append(shrCmd, req.Target)
shr.target = req.Target
logrus.Infof("executing '%v'", shrCmd)
shr.process, err = proctree.StartChild(shr.sub.Tail, shrCmd...)
if err != nil {
return nil, err
}
<-shr.sub.BootComplete
if bootErr == nil {
go shr.monitor()
i.agent.addShare <- shr
return &agentGrpc.SharePublicResponse{
Token: shr.token,
FrontendEndpoints: shr.frontendEndpoints,
}, nil
} else {
if err := proctree.WaitChild(shr.process); err != nil {
logrus.Errorf("error joining: %v", err)
}
return nil, fmt.Errorf("unable to start share: %v", bootErr)
}
}

78
agent/shareReserved.go Normal file
View File

@ -0,0 +1,78 @@
package agent
import (
"context"
"errors"
"fmt"
"github.com/openziti/zrok/agent/agentGrpc"
"github.com/openziti/zrok/agent/proctree"
"github.com/openziti/zrok/cmd/zrok/subordinate"
"github.com/openziti/zrok/environment"
"github.com/sirupsen/logrus"
"os"
)
func (i *agentGrpcImpl) ShareReserved(_ context.Context, req *agentGrpc.ShareReservedRequest) (*agentGrpc.ShareReservedResponse, error) {
root, err := environment.LoadRoot()
if err != nil {
return nil, err
}
if !root.IsEnabled() {
return nil, errors.New("unable to load environment; did you 'zrok enable'?")
}
shrCmd := []string{os.Args[0], "share", "reserved", "--subordinate"}
shr := &share{
reserved: true,
sub: subordinate.NewMessageHandler(),
agent: i.agent,
}
shr.sub.MessageHandler = func(msg subordinate.Message) {
logrus.Info(msg)
}
var bootErr error
shr.sub.BootHandler = func(msgType string, msg subordinate.Message) {
bootErr = shr.bootHandler(msgType, msg)
}
shr.sub.MalformedHandler = func(msg subordinate.Message) {
logrus.Error(msg)
}
if req.OverrideEndpoint != "" {
shrCmd = append(shrCmd, "--override-endpoint", req.OverrideEndpoint)
}
if req.Insecure {
shrCmd = append(shrCmd, "--insecure")
}
shr.insecure = req.Insecure
shrCmd = append(shrCmd, req.Token)
shr.token = req.Token
shr.process, err = proctree.StartChild(shr.sub.Tail, shrCmd...)
if err != nil {
return nil, err
}
<-shr.sub.BootComplete
if bootErr == nil {
go shr.monitor()
i.agent.addShare <- shr
return &agentGrpc.ShareReservedResponse{
Token: shr.token,
BackendMode: string(shr.backendMode),
ShareMode: string(shr.shareMode),
FrontendEndpoints: shr.frontendEndpoints,
Target: shr.target,
}, nil
} else {
if err := proctree.WaitChild(shr.process); err != nil {
logrus.Errorf("error joining: %v", err)
}
return nil, fmt.Errorf("unable to start share: %v", bootErr)
}
}

40
agent/status.go Normal file
View File

@ -0,0 +1,40 @@
package agent
import (
"context"
"github.com/openziti/zrok/agent/agentGrpc"
"sort"
)
func (i *agentGrpcImpl) Status(_ context.Context, _ *agentGrpc.StatusRequest) (*agentGrpc.StatusResponse, error) {
var accesses []*agentGrpc.AccessDetail
for feToken, acc := range i.agent.accesses {
accesses = append(accesses, &agentGrpc.AccessDetail{
FrontendToken: feToken,
Token: acc.token,
BindAddress: acc.bindAddress,
ResponseHeaders: acc.responseHeaders,
})
}
sort.Slice(accesses, func(i, j int) bool {
return accesses[i].FrontendToken < accesses[j].FrontendToken
})
var shares []*agentGrpc.ShareDetail
for token, shr := range i.agent.shares {
shares = append(shares, &agentGrpc.ShareDetail{
Token: token,
ShareMode: string(shr.shareMode),
BackendMode: string(shr.backendMode),
Reserved: shr.reserved,
FrontendEndpoint: shr.frontendEndpoints,
BackendEndpoint: shr.target,
Closed: shr.closed,
})
}
sort.Slice(shares, func(i, j int) bool {
return shares[i].Token < shares[j].Token
})
return &agentGrpc.StatusResponse{Accesses: accesses, Shares: shares}, nil
}

17
agent/version.go Normal file
View File

@ -0,0 +1,17 @@
package agent
import (
"context"
"github.com/openziti/zrok/agent/agentGrpc"
"github.com/openziti/zrok/build"
"github.com/sirupsen/logrus"
)
func (i *agentGrpcImpl) Version(_ context.Context, _ *agentGrpc.VersionRequest) (*agentGrpc.VersionResponse, error) {
v := build.String()
logrus.Debugf("responding to version inquiry with '%v'", v)
return &agentGrpc.VersionResponse{
V: v,
ConsoleEndpoint: i.agent.httpEndpoint,
}, nil
}

14
bin/generate_pb.sh Executable file
View File

@ -0,0 +1,14 @@
#!/bin/sh
go install \
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway \
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 \
google.golang.org/protobuf/cmd/protoc-gen-go \
google.golang.org/grpc/cmd/protoc-gen-go-grpc
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
--grpc-gateway_out=. --grpc-gateway_opt=paths=source_relative \
--openapiv2_out=. \
agent/agentGrpc/agent.proto

View File

@ -2,39 +2,41 @@
set -euo pipefail
command -v swagger >/dev/null 2>&1 || {
command -v swagger &>/dev/null || {
echo >&2 "command 'swagger' not installed. see: https://github.com/go-swagger/go-swagger for installation"
exit 1
}
command -v openapi >/dev/null 2>&1 || {
echo >&2 "command 'openapi' not installed. see: https://www.npmjs.com/package/openapi-client for installation"
exit 1
}
command -v swagger-codegen 2>&1 || {
command -v swagger-codegen &>/dev/null || {
echo >&2 "command 'swagger-codegen' not installed. see: https://github.com/swagger-api/swagger-codegen for installation"
exit 1
}
command -v openapi-generator-cli 2>&1 || {
command -v openapi-generator-cli &>/dev/null || {
echo >&2 "command 'openapi-generator-cli' not installed. see: https://www.npmjs.com/package/@openapitools/openapi-generator-cli for installation"
exit 1
}
command -v realpath 2>&1 || {
command -v realpath &>/dev/null || {
echo >&2 "command 'realpath' not installed. see: https://www.npmjs.com/package/realpath for installation"
exit 1
}
scriptPath=$(realpath $0)
scriptPath=$(realpath "$0")
scriptDir=$(dirname "$scriptPath")
zrokDir=$(realpath "$scriptDir/..")
zrokSpec=$(realpath "$zrokDir/specs/zrok.yml")
pythonConfig=$(realpath "$zrokDir/bin/python_config.json")
# anti-oops in case user runs this script from somewhere else
if [[ "$(realpath "$zrokDir")" != "$(realpath "$(pwd)")" ]]
then
echo "ERROR: must be run from zrok root" >&2
exit 1
fi
echo "...clean generate zrok server/client"
rm -rf ./rest_client_zrok ./rest_server_zrok ./rest_model_zrok
echo "...generating zrok server"
swagger generate server -P rest_model_zrok.Principal -f "$zrokSpec" -s rest_server_zrok -t "$zrokDir" -m "rest_model_zrok" --exclude-main
@ -42,13 +44,22 @@ swagger generate server -P rest_model_zrok.Principal -f "$zrokSpec" -s rest_serv
echo "...generating zrok client"
swagger generate client -P rest_model_zrok.Principal -f "$zrokSpec" -c rest_client_zrok -t "$zrokDir" -m "rest_model_zrok"
echo "...generating js client"
openapi -s specs/zrok.yml -o ui/src/api -l js
echo "...generating api console ts client"
rm -rf ui/src/api
openapi-generator-cli generate -i "$zrokSpec" -o ui/src/api -g typescript-fetch
echo "...generating ts client"
openapi-generator-cli generate -i specs/zrok.yml -o sdk/nodejs/sdk/src/zrok/api -g typescript-node
echo "...generating agent console ts client"
rm -rf agent/agentUi/src/api
openapi-generator-cli generate -i agent/agentGrpc/agent.swagger.json -o agent/agentUi/src/api -g typescript-fetch
echo "...generating python client"
swagger-codegen generate -i specs/zrok.yml -o sdk/python/sdk/zrok -c $pythonConfig -l python
echo "...generating nodejs sdk ts client"
rm -rf sdk/nodejs/sdk/src/api
openapi-generator-cli generate -i "$zrokSpec" -o sdk/nodejs/sdk/src/api -g typescript-fetch
echo "...generating python sdk client"
pyMod="./sdk/python/src"
rm -rf "$pyMod"/{zrok_api,docs,test,.gitignore,README.md,requirements.txt}
openapi-generator-cli generate -i "$zrokSpec" -o "$pyMod" -g python \
--package-name zrok_api --additional-properties projectName=zrok
git checkout rest_server_zrok/configure_zrok.go

View File

@ -1,4 +0,0 @@
{
"packageName":"zrok_api",
"projectName":"zrok_sdk"
}

View File

@ -5,7 +5,7 @@ import "fmt"
var Version string
var Hash string
const Series = "v0.4"
const Series = "v1.0"
func String() string {
if Version != "" {

47
canary/looper.go Normal file
View File

@ -0,0 +1,47 @@
package canary
import (
"github.com/openziti/zrok/util"
"github.com/sirupsen/logrus"
"time"
)
type LooperOptions struct {
Iterations uint
StatusInterval uint
Timeout time.Duration
MinPayload uint64
MaxPayload uint64
MinDwell time.Duration
MaxDwell time.Duration
MinPacing time.Duration
MaxPacing time.Duration
}
type LooperResults struct {
StartTime time.Time
StopTime time.Time
Loops uint
Errors uint
Mismatches uint
Bytes uint64
}
func ReportLooperResults(results []*LooperResults) {
totalBytes := uint64(0)
totalXferRate := uint64(0)
totalErrors := uint(0)
totalMismatches := uint(0)
totalLoops := uint(0)
for i, result := range results {
totalBytes += result.Bytes
deltaSeconds := result.StopTime.Sub(result.StartTime).Seconds()
xferRate := uint64(float64(result.Bytes) / deltaSeconds)
totalXferRate += xferRate
totalErrors += result.Errors
totalMismatches += result.Mismatches
totalLoops += result.Loops
logrus.Infof("looper #%d: %d loops, %v, %d errors, %d mismatches, %s/sec", i, result.Loops, util.BytesToSize(int64(result.Bytes)), result.Errors, result.Mismatches, util.BytesToSize(int64(xferRate)))
}
logrus.Infof("total: %d loops, %v, %d errors, %d mismatches, %s/sec", totalLoops, util.BytesToSize(int64(totalBytes)), totalErrors, totalMismatches, util.BytesToSize(int64(totalXferRate)))
}

236
canary/privateHttpLooper.go Normal file
View File

@ -0,0 +1,236 @@
package canary
import (
"bytes"
"context"
cryptorand "crypto/rand"
"encoding/base64"
"github.com/openziti/sdk-golang/ziti"
"github.com/openziti/sdk-golang/ziti/edge"
"github.com/openziti/zrok/environment/env_core"
"github.com/openziti/zrok/sdk/golang/sdk"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"io"
"math/rand"
"net"
"net/http"
"time"
)
type PrivateHttpLooper struct {
id uint
acc *sdk.Access
opt *LooperOptions
root env_core.Root
shr *sdk.Share
listener edge.Listener
abort bool
done chan struct{}
results *LooperResults
}
func NewPrivateHttpLooper(id uint, opt *LooperOptions, root env_core.Root) *PrivateHttpLooper {
return &PrivateHttpLooper{
id: id,
opt: opt,
root: root,
done: make(chan struct{}),
results: &LooperResults{},
}
}
func (l *PrivateHttpLooper) Run() {
defer close(l.done)
defer logrus.Infof("#%d stopping", l.id)
defer l.shutdown()
logrus.Infof("#%d starting", l.id)
if err := l.startup(); err != nil {
logrus.Fatalf("#%d error starting: %v", l.id, err)
}
if err := l.bind(); err != nil {
logrus.Fatalf("#%d error binding: %v", l.id, err)
}
l.dwell()
l.iterate()
logrus.Infof("#%d completed", l.id)
}
func (l *PrivateHttpLooper) Abort() {
l.abort = true
}
func (l *PrivateHttpLooper) Done() <-chan struct{} {
return l.done
}
func (l *PrivateHttpLooper) Results() *LooperResults {
return l.results
}
func (l *PrivateHttpLooper) startup() error {
shr, err := sdk.CreateShare(l.root, &sdk.ShareRequest{
ShareMode: sdk.PrivateShareMode,
BackendMode: sdk.ProxyBackendMode,
Target: "canary.PrivateHttpLooper",
PermissionMode: sdk.ClosedPermissionMode,
})
if err != nil {
return err
}
l.shr = shr
acc, err := sdk.CreateAccess(l.root, &sdk.AccessRequest{
ShareToken: shr.Token,
})
if err != nil {
return err
}
l.acc = acc
logrus.Infof("#%d allocated share '%v', allocated frontend '%v'", l.id, shr.Token, acc.Token)
return nil
}
func (l *PrivateHttpLooper) bind() error {
zif, err := l.root.ZitiIdentityNamed(l.root.EnvironmentIdentityName())
if err != nil {
return errors.Wrapf(err, "#%d error getting identity", l.id)
}
zcfg, err := ziti.NewConfigFromFile(zif)
if err != nil {
return errors.Wrapf(err, "#%d error loading ziti config", l.id)
}
options := ziti.ListenOptions{
ConnectTimeout: 5 * time.Minute,
WaitForNEstablishedListeners: 1,
}
zctx, err := ziti.NewContext(zcfg)
if err != nil {
return errors.Wrapf(err, "#%d error creating ziti context", l.id)
}
if l.listener, err = zctx.ListenWithOptions(l.shr.Token, &options); err != nil {
return errors.Wrapf(err, "#%d error binding listener", l.id)
}
go func() {
if err := http.Serve(l.listener, l); err != nil {
logrus.Errorf("#%d error in http listener: %v", l.id, err)
}
}()
return nil
}
func (l *PrivateHttpLooper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
buf := new(bytes.Buffer)
io.Copy(buf, r.Body)
w.Write(buf.Bytes())
}
func (l *PrivateHttpLooper) dwell() {
dwell := l.opt.MinDwell.Milliseconds()
dwelta := l.opt.MaxDwell.Milliseconds() - l.opt.MinDwell.Milliseconds()
if dwelta > 0 {
dwell = int64(rand.Intn(int(dwelta)) + int(l.opt.MinDwell.Milliseconds()))
}
time.Sleep(time.Duration(dwell) * time.Millisecond)
}
type connDialer struct {
c net.Conn
}
func (cd connDialer) Dial(_ context.Context, network, addr string) (net.Conn, error) {
return cd.c, nil
}
func (l *PrivateHttpLooper) iterate() {
l.results.StartTime = time.Now()
defer func() { l.results.StopTime = time.Now() }()
for i := uint(0); i < l.opt.Iterations; i++ {
if i > 0 && i%l.opt.StatusInterval == 0 {
logrus.Infof("#%d: iteration %d", l.id, i)
}
conn, err := sdk.NewDialer(l.shr.Token, l.root)
if err != nil {
logrus.Errorf("#%d: error dialing: %v", l.id, err)
l.results.Errors++
time.Sleep(1 * time.Second)
continue
}
payloadSize := l.opt.MaxPayload
payloadRange := l.opt.MaxPayload - l.opt.MinPayload
if payloadRange > 0 {
payloadSize = (rand.Uint64() % payloadRange) + l.opt.MinPayload
}
outPayload := make([]byte, payloadSize)
cryptorand.Read(outPayload)
outBase64 := base64.StdEncoding.EncodeToString(outPayload)
if req, err := http.NewRequest("POST", "http://"+l.shr.Token, bytes.NewBufferString(outBase64)); err == nil {
client := &http.Client{Timeout: l.opt.Timeout, Transport: &http.Transport{DialContext: connDialer{conn}.Dial}}
if resp, err := client.Do(req); err == nil {
if resp.StatusCode != 200 {
logrus.Errorf("#%d: unexpected status code: %v", l.id, resp.StatusCode)
l.results.Errors++
}
inPayload := new(bytes.Buffer)
io.Copy(inPayload, resp.Body)
inBase64 := inPayload.String()
if inBase64 != outBase64 {
logrus.Errorf("#%d: payload mismatch", l.id)
l.results.Mismatches++
} else {
l.results.Bytes += uint64(len(outBase64))
logrus.Debugf("#%d: payload match", l.id)
}
} else {
logrus.Errorf("#%d: error: %v", l.id, err)
l.results.Errors++
}
} else {
logrus.Errorf("#%d: error creating request: %v", l.id, err)
l.results.Errors++
}
if err := conn.Close(); err != nil {
logrus.Errorf("#%d: error closing connection: %v", l.id, err)
}
pacingMs := l.opt.MaxPacing.Milliseconds()
pacingDelta := l.opt.MaxPacing.Milliseconds() - l.opt.MinPacing.Milliseconds()
if pacingDelta > 0 {
pacingMs = (rand.Int63() % pacingDelta) + l.opt.MinPacing.Milliseconds()
time.Sleep(time.Duration(pacingMs) * time.Millisecond)
}
l.results.Loops++
}
}
func (l *PrivateHttpLooper) shutdown() {
if l.listener != nil {
if err := l.listener.Close(); err != nil {
logrus.Errorf("#%d error closing listener: %v", l.id, err)
}
}
if err := sdk.DeleteAccess(l.root, l.acc); err != nil {
logrus.Errorf("#%d error deleting access '%v': %v", l.id, l.acc.Token, err)
}
if err := sdk.DeleteShare(l.root, l.shr); err != nil {
logrus.Errorf("#%d error deleting share '%v': %v", l.id, l.shr.Token, err)
}
}

204
canary/publicHttpLooper.go Normal file
View File

@ -0,0 +1,204 @@
package canary
import (
"bytes"
cryptorand "crypto/rand"
"encoding/base64"
"github.com/openziti/sdk-golang/ziti"
"github.com/openziti/sdk-golang/ziti/edge"
"github.com/openziti/zrok/environment/env_core"
"github.com/openziti/zrok/sdk/golang/sdk"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"io"
"math/rand"
"net/http"
"time"
)
type PublicHttpLooper struct {
id uint
frontend string
opt *LooperOptions
root env_core.Root
shr *sdk.Share
listener edge.Listener
abort bool
done chan struct{}
results *LooperResults
}
func NewPublicHttpLooper(id uint, frontend string, opt *LooperOptions, root env_core.Root) *PublicHttpLooper {
return &PublicHttpLooper{
id: id,
frontend: frontend,
opt: opt,
root: root,
done: make(chan struct{}),
results: &LooperResults{},
}
}
func (l *PublicHttpLooper) Run() {
defer close(l.done)
defer logrus.Infof("#%d stopping", l.id)
defer l.shutdown()
logrus.Infof("#%d starting", l.id)
if err := l.startup(); err != nil {
logrus.Fatalf("#%d error starting: %v", l.id, err)
}
if err := l.bind(); err != nil {
logrus.Fatalf("#%d error binding: %v", l.id, err)
}
l.dwell()
l.iterate()
logrus.Infof("#%d completed", l.id)
}
func (l *PublicHttpLooper) Abort() {
l.abort = true
}
func (l *PublicHttpLooper) Done() <-chan struct{} {
return l.done
}
func (l *PublicHttpLooper) Results() *LooperResults {
return l.results
}
func (l *PublicHttpLooper) startup() error {
shr, err := sdk.CreateShare(l.root, &sdk.ShareRequest{
ShareMode: sdk.PublicShareMode,
BackendMode: sdk.ProxyBackendMode,
Target: "canary.PublicHttpLooper",
Frontends: []string{l.frontend},
PermissionMode: sdk.ClosedPermissionMode,
})
if err != nil {
return err
}
l.shr = shr
logrus.Infof("#%d allocated share '%v'", l.id, l.shr.Token)
return nil
}
func (l *PublicHttpLooper) bind() error {
zif, err := l.root.ZitiIdentityNamed(l.root.EnvironmentIdentityName())
if err != nil {
return errors.Wrapf(err, "#%d error getting identity", l.id)
}
zcfg, err := ziti.NewConfigFromFile(zif)
if err != nil {
return errors.Wrapf(err, "#%d error loading ziti config", l.id)
}
options := ziti.ListenOptions{
ConnectTimeout: 5 * time.Minute,
WaitForNEstablishedListeners: 1,
}
zctx, err := ziti.NewContext(zcfg)
if err != nil {
return errors.Wrapf(err, "#%d error creating ziti context", l.id)
}
if l.listener, err = zctx.ListenWithOptions(l.shr.Token, &options); err != nil {
return errors.Wrapf(err, "#%d error binding listener", l.id)
}
go func() {
if err := http.Serve(l.listener, l); err != nil {
logrus.Errorf("#%d error in http listener: %v", l.id, err)
}
}()
return nil
}
func (l *PublicHttpLooper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
buf := new(bytes.Buffer)
io.Copy(buf, r.Body)
w.Write(buf.Bytes())
}
func (l *PublicHttpLooper) dwell() {
dwell := l.opt.MinDwell.Milliseconds()
dwelta := l.opt.MaxDwell.Milliseconds() - l.opt.MinDwell.Milliseconds()
if dwelta > 0 {
dwell = int64(rand.Intn(int(dwelta)) + int(l.opt.MinDwell.Milliseconds()))
}
time.Sleep(time.Duration(dwell) * time.Millisecond)
}
func (l *PublicHttpLooper) iterate() {
l.results.StartTime = time.Now()
defer func() { l.results.StopTime = time.Now() }()
for i := uint(0); i < l.opt.Iterations && !l.abort; i++ {
if i > 0 && i%l.opt.StatusInterval == 0 {
logrus.Infof("#%d: iteration %d", l.id, i)
}
payloadSize := l.opt.MaxPayload
payloadRange := l.opt.MaxPayload - l.opt.MinPayload
if payloadRange > 0 {
payloadSize = (rand.Uint64() % payloadRange) + l.opt.MinPayload
}
outPayload := make([]byte, payloadSize)
cryptorand.Read(outPayload)
outBase64 := base64.StdEncoding.EncodeToString(outPayload)
if req, err := http.NewRequest("POST", l.shr.FrontendEndpoints[0], bytes.NewBufferString(outBase64)); err == nil {
client := &http.Client{Timeout: l.opt.Timeout}
if resp, err := client.Do(req); err == nil {
if resp.StatusCode != 200 {
logrus.Errorf("#%d: unexpected status code: %v", l.id, resp.StatusCode)
l.results.Errors++
}
inPayload := new(bytes.Buffer)
io.Copy(inPayload, resp.Body)
inBase64 := inPayload.String()
if inBase64 != outBase64 {
logrus.Errorf("#%d: payload mismatch", l.id)
l.results.Mismatches++
} else {
l.results.Bytes += uint64(len(outBase64))
logrus.Debugf("#%d: payload match", l.id)
}
} else {
logrus.Errorf("#%d: error: %v", l.id, err)
l.results.Errors++
}
} else {
logrus.Errorf("#%d: error creating request: %v", l.id, err)
l.results.Errors++
}
pacingMs := l.opt.MaxPacing.Milliseconds()
pacingDelta := l.opt.MaxPacing.Milliseconds() - l.opt.MinPacing.Milliseconds()
if pacingDelta > 0 {
pacingMs = (rand.Int63() % pacingDelta) + l.opt.MinPacing.Milliseconds()
time.Sleep(time.Duration(pacingMs) * time.Millisecond)
}
l.results.Loops++
}
}
func (l *PublicHttpLooper) shutdown() {
if l.listener != nil {
if err := l.listener.Close(); err != nil {
logrus.Errorf("#%d error closing listener: %v", l.id, err)
}
}
if err := sdk.DeleteShare(l.root, l.shr); err != nil {
logrus.Errorf("#%d error deleting share '%v': %v", l.id, l.shr.Token, err)
}
}

View File

@ -1,19 +1,27 @@
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
tea "github.com/charmbracelet/bubbletea"
"github.com/go-openapi/runtime"
httptransport "github.com/go-openapi/runtime/client"
"github.com/openziti/zrok/agent/agentClient"
"github.com/openziti/zrok/agent/agentGrpc"
"github.com/openziti/zrok/cmd/zrok/subordinate"
"github.com/openziti/zrok/endpoints"
"github.com/openziti/zrok/endpoints/proxy"
"github.com/openziti/zrok/endpoints/tcpTunnel"
"github.com/openziti/zrok/endpoints/udpTunnel"
"github.com/openziti/zrok/endpoints/vpn"
"github.com/openziti/zrok/environment"
"github.com/openziti/zrok/environment/env_core"
"github.com/openziti/zrok/rest_client_zrok"
"github.com/openziti/zrok/rest_client_zrok/share"
"github.com/openziti/zrok/rest_model_zrok"
"github.com/openziti/zrok/tui"
"github.com/openziti/zrok/util"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"net/url"
@ -29,7 +37,14 @@ func init() {
type accessPrivateCommand struct {
bindAddress string
autoMode bool
autoAddress string
autoStartPort uint16
autoEndPort uint16
headless bool
subordinate bool
forceLocal bool
forceAgent bool
responseHeaders []string
cmd *cobra.Command
}
@ -41,47 +56,98 @@ func newAccessPrivateCommand() *accessPrivateCommand {
Args: cobra.ExactArgs(1),
}
command := &accessPrivateCommand{cmd: cmd}
cmd.Flags().BoolVar(&command.headless, "headless", false, "Disable TUI and run headless")
cmd.Flags().StringVarP(&command.bindAddress, "bind", "b", "127.0.0.1:9191", "The address to bind the private frontend")
headless := false
if root, err := environment.LoadRoot(); err == nil {
headless, _ = root.Headless()
}
cmd.Flags().BoolVar(&command.headless, "headless", headless, "Disable TUI and run headless")
cmd.Flags().BoolVar(&command.subordinate, "subordinate", false, "Enable subordinate mode")
cmd.MarkFlagsMutuallyExclusive("headless", "subordinate")
cmd.Flags().BoolVar(&command.forceLocal, "force-local", false, "Skip agent detection and force local mode")
cmd.Flags().BoolVar(&command.forceAgent, "force-agent", false, "Skip agent detection and force agent mode")
cmd.MarkFlagsMutuallyExclusive("force-local", "force-agent")
cmd.Flags().StringVarP(&command.bindAddress, "bind", "b", "127.0.0.1:9191", "The address to bind the private frontend (ignored when using '--auto')")
cmd.Flags().BoolVar(&command.autoMode, "auto", false, "Enable automatic port detection")
cmd.Flags().StringVar(&command.autoAddress, "auto-address", "127.0.0.1", "The address to use for automatic port detection")
cmd.Flags().Uint16Var(&command.autoStartPort, "auto-start-port", 8080, "The starting port to use for automatic port detection")
cmd.Flags().Uint16Var(&command.autoEndPort, "auto-end-port", 8888, "The ending port to use for automatic port detection")
cmd.Flags().StringArrayVar(&command.responseHeaders, "response-header", []string{}, "Add a response header ('key:value')")
cmd.Run = command.run
return command
}
func (cmd *accessPrivateCommand) run(_ *cobra.Command, args []string) {
shrToken := args[0]
env, err := environment.LoadRoot()
if err != nil {
tui.Error("error loading environment", err)
if cmd.subordinate {
logrus.SetFormatter(&logrus.JSONFormatter{TimestampFormat: time.RFC3339Nano})
}
if !env.IsEnabled() {
root, err := environment.LoadRoot()
if err != nil {
cmd.error(err)
}
if !root.IsEnabled() {
tui.Error("unable to load environment; did you 'zrok enable'?", nil)
}
zrok, err := env.Client()
if cmd.subordinate || cmd.forceLocal {
cmd.accessLocal(args, root)
} else {
agent := cmd.forceAgent
if !cmd.forceAgent {
agent, err = agentClient.IsAgentRunning(root)
if err != nil {
if !panicInstead {
tui.Error("unable to create zrok client", err)
tui.Error("error checking if agent is running", err)
}
}
if agent {
cmd.accessAgent(args, root)
} else {
cmd.accessLocal(args, root)
}
}
panic(err)
}
auth := httptransport.APIKeyAuth("X-TOKEN", "header", env.Environment().Token)
req := share.NewAccessParams()
req.Body = &rest_model_zrok.AccessRequest{
ShrToken: shrToken,
EnvZID: env.Environment().ZitiIdentity,
func (cmd *accessPrivateCommand) accessLocal(args []string, root env_core.Root) {
shrToken := args[0]
zrok, err := root.Client()
if err != nil {
cmd.error(err)
}
auth := httptransport.APIKeyAuth("X-TOKEN", "header", root.Environment().AccountToken)
req := share.NewAccessParams()
req.Body.ShareToken = shrToken
req.Body.EnvZID = root.Environment().ZitiIdentity
accessResp, err := zrok.Share.Access(req, auth)
if err != nil {
if !panicInstead {
tui.Error("unable to access", err)
cmd.error(err)
}
panic(err)
bindAddress := cmd.bindAddress
if cmd.autoMode {
if accessResp.Payload.BackendMode == "udpTunnel" {
cmd.shutdown(accessResp.Payload.FrontendToken, root.Environment().ZitiIdentity, shrToken, zrok, auth)
cmd.error(errors.New("auto-addressing is not compatible with the 'udpTunnel' backend mode"))
}
autoAddress, err := util.AutoListenerAddress("tcp", cmd.autoAddress, cmd.autoStartPort, cmd.autoEndPort)
if err != nil {
cmd.shutdown(accessResp.Payload.FrontendToken, root.Environment().ZitiIdentity, shrToken, zrok, auth)
cmd.error(err)
}
bindAddress = autoAddress
}
upReq := share.NewUpdateAccessParams()
upReq.Body.FrontendToken = accessResp.Payload.FrontendToken
upReq.Body.BindAddress = bindAddress
_, err = zrok.Share.UpdateAccess(upReq, auth)
if err != nil {
cmd.shutdown(accessResp.Payload.FrontendToken, root.Environment().ZitiIdentity, shrToken, zrok, auth)
cmd.error(err)
}
logrus.Infof("allocated frontend '%v'", accessResp.Payload.FrontendToken)
protocol := "http://"
switch accessResp.Payload.BackendMode {
@ -91,80 +157,66 @@ func (cmd *accessPrivateCommand) run(_ *cobra.Command, args []string) {
protocol = "udp://"
}
endpointUrl, err := url.Parse(protocol + cmd.bindAddress)
endpointUrl, err := url.Parse(protocol + bindAddress)
if err != nil {
if !panicInstead {
tui.Error("invalid endpoint address", err)
}
panic(err)
cmd.shutdown(accessResp.Payload.FrontendToken, root.Environment().ZitiIdentity, shrToken, zrok, auth)
cmd.error(err)
}
requests := make(chan *endpoints.Request, 1024)
switch accessResp.Payload.BackendMode {
case "tcpTunnel":
fe, err := tcpTunnel.NewFrontend(&tcpTunnel.FrontendConfig{
BindAddress: cmd.bindAddress,
IdentityName: env.EnvironmentIdentityName(),
BindAddress: bindAddress,
IdentityName: root.EnvironmentIdentityName(),
ShrToken: args[0],
RequestsChan: requests,
})
if err != nil {
if !panicInstead {
tui.Error("unable to create private access", err)
}
panic(err)
cmd.shutdown(accessResp.Payload.FrontendToken, root.Environment().ZitiIdentity, shrToken, zrok, auth)
cmd.error(err)
}
go func() {
if err := fe.Run(); err != nil {
if !panicInstead {
tui.Error("error starting access", err)
}
panic(err)
cmd.shutdown(accessResp.Payload.FrontendToken, root.Environment().ZitiIdentity, shrToken, zrok, auth)
cmd.error(err)
}
}()
case "udpTunnel":
fe, err := udpTunnel.NewFrontend(&udpTunnel.FrontendConfig{
BindAddress: cmd.bindAddress,
IdentityName: env.EnvironmentIdentityName(),
IdentityName: root.EnvironmentIdentityName(),
ShrToken: args[0],
RequestsChan: requests,
IdleTime: time.Minute,
})
if err != nil {
if !panicInstead {
tui.Error("unable to create private frontend", err)
}
panic(err)
cmd.shutdown(accessResp.Payload.FrontendToken, root.Environment().ZitiIdentity, shrToken, zrok, auth)
cmd.error(err)
}
go func() {
if err := fe.Run(); err != nil {
if !panicInstead {
tui.Error("error starting frontend", err)
}
panic(err)
cmd.shutdown(accessResp.Payload.FrontendToken, root.Environment().ZitiIdentity, shrToken, zrok, auth)
cmd.error(err)
}
}()
case "socks":
fe, err := tcpTunnel.NewFrontend(&tcpTunnel.FrontendConfig{
BindAddress: cmd.bindAddress,
IdentityName: env.EnvironmentIdentityName(),
BindAddress: bindAddress,
IdentityName: root.EnvironmentIdentityName(),
ShrToken: args[0],
RequestsChan: requests,
})
if err != nil {
if !panicInstead {
tui.Error("unable to create private access", err)
}
panic(err)
cmd.shutdown(accessResp.Payload.FrontendToken, root.Environment().ZitiIdentity, shrToken, zrok, auth)
cmd.error(err)
}
go func() {
if err := fe.Run(); err != nil {
if !panicInstead {
tui.Error("error starting access", err)
}
panic(err)
cmd.shutdown(accessResp.Payload.FrontendToken, root.Environment().ZitiIdentity, shrToken, zrok, auth)
cmd.error(err)
}
}()
@ -173,55 +225,60 @@ func (cmd *accessPrivateCommand) run(_ *cobra.Command, args []string) {
Scheme: "VPN",
}
fe, err := vpn.NewFrontend(&vpn.FrontendConfig{
IdentityName: env.EnvironmentIdentityName(),
IdentityName: root.EnvironmentIdentityName(),
ShrToken: args[0],
RequestsChan: requests,
})
if err != nil {
if !panicInstead {
tui.Error("unable to create private access", err)
}
panic(err)
cmd.shutdown(accessResp.Payload.FrontendToken, root.Environment().ZitiIdentity, shrToken, zrok, auth)
cmd.error(err)
}
go func() {
if err := fe.Run(); err != nil {
if !panicInstead {
tui.Error("error starting access", err)
}
panic(err)
cmd.shutdown(accessResp.Payload.FrontendToken, root.Environment().ZitiIdentity, shrToken, zrok, auth)
cmd.error(err)
}
}()
default:
cfg := proxy.DefaultFrontendConfig(env.EnvironmentIdentityName())
cfg := proxy.DefaultFrontendConfig(root.EnvironmentIdentityName())
cfg.ShrToken = shrToken
cfg.Address = cmd.bindAddress
cfg.Address = bindAddress
cfg.ResponseHeaders = cmd.responseHeaders
cfg.RequestsChan = requests
fe, err := proxy.NewFrontend(cfg)
if err != nil {
if !panicInstead {
tui.Error("unable to create private frontend", err)
}
panic(err)
cmd.shutdown(accessResp.Payload.FrontendToken, root.Environment().ZitiIdentity, shrToken, zrok, auth)
cmd.error(err)
}
go func() {
if err := fe.Run(); err != nil {
if !panicInstead {
tui.Error("unable to run frontend", err)
}
cmd.shutdown(accessResp.Payload.FrontendToken, root.Environment().ZitiIdentity, shrToken, zrok, auth)
cmd.error(err)
}
}()
}
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGKILL, syscall.SIGQUIT)
go func() {
<-c
cmd.destroy(accessResp.Payload.FrontendToken, env.Environment().ZitiIdentity, shrToken, zrok, auth)
cmd.shutdown(accessResp.Payload.FrontendToken, root.Environment().ZitiIdentity, shrToken, zrok, auth)
os.Exit(0)
}()
if cmd.subordinate {
data := make(map[string]interface{})
data[subordinate.MessageKey] = subordinate.BootMessage
data["frontend_token"] = accessResp.Payload.FrontendToken
data["bind_address"] = bindAddress
jsonData, err := json.Marshal(data)
if err != nil {
subordinateError(err)
}
fmt.Println(string(jsonData))
}
if cmd.headless {
logrus.Infof("access the zrok share at the following endpoint: %v", endpointUrl.String())
for {
@ -230,7 +287,22 @@ func (cmd *accessPrivateCommand) run(_ *cobra.Command, args []string) {
logrus.Infof("%v -> %v %v", req.RemoteAddr, req.Method, req.Path)
}
}
} else if cmd.subordinate {
for {
select {
case req := <-requests:
data := make(map[string]interface{})
data[subordinate.MessageKey] = "access"
data["remote-address"] = req.RemoteAddr
data["method"] = req.Method
data["path"] = req.Path
jsonData, err := json.Marshal(data)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(jsonData))
}
}
} else {
mdl := newAccessModel(shrToken, endpointUrl.String())
logrus.SetOutput(mdl)
@ -253,21 +325,56 @@ func (cmd *accessPrivateCommand) run(_ *cobra.Command, args []string) {
}
close(requests)
cmd.destroy(accessResp.Payload.FrontendToken, env.Environment().ZitiIdentity, shrToken, zrok, auth)
cmd.shutdown(accessResp.Payload.FrontendToken, root.Environment().ZitiIdentity, shrToken, zrok, auth)
}
}
func (cmd *accessPrivateCommand) destroy(frontendName, envZId, shrToken string, zrok *rest_client_zrok.Zrok, auth runtime.ClientAuthInfoWriter) {
logrus.Debugf("shutting down '%v'", shrToken)
req := share.NewUnaccessParams()
req.Body = &rest_model_zrok.UnaccessRequest{
FrontendToken: frontendName,
ShrToken: shrToken,
EnvZID: envZId,
func (cmd *accessPrivateCommand) error(err error) {
if cmd.subordinate {
subordinateError(err)
}
if !panicInstead {
tui.Error("unable to create private access", err)
}
panic(err)
}
func (cmd *accessPrivateCommand) shutdown(frontendToken, envZId, shrToken string, zrok *rest_client_zrok.Zrok, auth runtime.ClientAuthInfoWriter) {
logrus.Infof("shutting down '%v'", shrToken)
req := share.NewUnaccessParams()
req.Body.FrontendToken = frontendToken
req.Body.ShareToken = shrToken
req.Body.EnvZID = envZId
if _, err := zrok.Share.Unaccess(req, auth); err == nil {
logrus.Debugf("shutdown complete")
} else {
logrus.Errorf("error shutting down: %v", err)
}
}
func (cmd *accessPrivateCommand) accessAgent(args []string, root env_core.Root) {
client, conn, err := agentClient.NewClient(root)
if err != nil {
tui.Error("error connecting to agent", err)
}
defer func() { _ = conn.Close() }()
req := &agentGrpc.AccessPrivateRequest{
Token: args[0],
BindAddress: cmd.bindAddress,
ResponseHeaders: cmd.responseHeaders,
}
if cmd.autoMode {
req.AutoMode = true
req.AutoAddress = cmd.autoAddress
req.AutoStartPort = uint32(cmd.autoStartPort)
req.AutoEndPort = uint32(cmd.autoEndPort)
}
acc, err := client.AccessPrivate(context.Background(), req)
if err != nil {
tui.Error("error creating access", err)
}
fmt.Println(acc)
}

View File

@ -46,5 +46,5 @@ func (cmd *adminCreateAccount) run(_ *cobra.Command, args []string) {
panic(err)
}
fmt.Println(resp.GetPayload().Token)
fmt.Println(resp.GetPayload().AccountToken)
}

View File

@ -3,7 +3,6 @@ package main
import (
"github.com/openziti/zrok/environment"
"github.com/openziti/zrok/rest_client_zrok/admin"
"github.com/openziti/zrok/rest_model_zrok"
"github.com/openziti/zrok/sdk/golang/sdk"
"github.com/openziti/zrok/tui"
"github.com/sirupsen/logrus"
@ -52,12 +51,10 @@ func (cmd *adminCreateFrontendCommand) run(_ *cobra.Command, args []string) {
permissionMode = sdk.ClosedPermissionMode
}
req := admin.NewCreateFrontendParams()
req.Body = &rest_model_zrok.CreateFrontendRequest{
ZID: zId,
PublicName: publicName,
URLTemplate: urlTemplate,
PermissionMode: string(permissionMode),
}
req.Body.ZID = zId
req.Body.PublicName = publicName
req.Body.URLTemplate = urlTemplate
req.Body.PermissionMode = string(permissionMode)
resp, err := zrok.Admin.CreateFrontend(req, mustGetAdminAuth())
if err != nil {
@ -71,5 +68,5 @@ func (cmd *adminCreateFrontendCommand) run(_ *cobra.Command, args []string) {
}
}
logrus.Infof("created global public frontend '%v'", resp.Payload.Token)
logrus.Infof("created global public frontend '%v'", resp.Payload.FrontendToken)
}

View File

@ -41,7 +41,7 @@ func (cmd *adminCreateOrgMemberCommand) run(_ *cobra.Command, args []string) {
}
req := admin.NewAddOrganizationMemberParams()
req.Body.Token = args[0]
req.Body.OrganizationToken = args[0]
req.Body.Email = args[1]
req.Body.Admin = cmd.admin

View File

@ -48,5 +48,5 @@ func (cmd *adminCreateOrganizationCommand) run(_ *cobra.Command, _ []string) {
panic(err)
}
logrus.Infof("created new organization with token '%v'", resp.Payload.Token)
logrus.Infof("created new organization with organization token '%v'", resp.Payload.OrganizationToken)
}

View File

@ -3,7 +3,6 @@ package main
import (
"github.com/openziti/zrok/environment"
"github.com/openziti/zrok/rest_client_zrok/admin"
"github.com/openziti/zrok/rest_model_zrok"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@ -41,7 +40,7 @@ func (cmd *adminDeleteFrontendCommand) run(_ *cobra.Command, args []string) {
}
req := admin.NewDeleteFrontendParams()
req.Body = &rest_model_zrok.DeleteFrontendRequest{FrontendToken: feToken}
req.Body.FrontendToken = feToken
_, err = zrok.Admin.DeleteFrontend(req, mustGetAdminAuth())
if err != nil {

View File

@ -39,7 +39,7 @@ func (cmd *adminDeleteOrgMemberCommand) run(_ *cobra.Command, args []string) {
}
req := admin.NewRemoveOrganizationMemberParams()
req.Body.Token = args[0]
req.Body.OrganizationToken = args[0]
req.Body.Email = args[1]
_, err = zrok.Admin.RemoveOrganizationMember(req, mustGetAdminAuth())

View File

@ -39,7 +39,7 @@ func (cmd *adminDeleteOrganizationCommand) run(_ *cobra.Command, args []string)
}
req := admin.NewDeleteOrganizationParams()
req.Body.Token = args[0]
req.Body.OrganizationToken = args[0]
_, err = zrok.Admin.DeleteOrganization(req, mustGetAdminAuth())
if err != nil {

View File

@ -5,7 +5,6 @@ import (
"github.com/jaevor/go-nanoid"
"github.com/openziti/zrok/environment"
"github.com/openziti/zrok/rest_client_zrok/admin"
"github.com/openziti/zrok/rest_model_zrok"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@ -54,9 +53,8 @@ func (cmd *adminGenerateCommand) run(_ *cobra.Command, args []string) {
panic(err)
}
req := admin.NewInviteTokenGenerateParams()
req.Body = &rest_model_zrok.InviteTokenGenerateRequest{
Tokens: tokens,
}
req.Body.InviteTokens = tokens
_, err = zrok.Admin.InviteTokenGenerate(req, mustGetAdminAuth())
if err != nil {
if !panicInstead {

View File

@ -54,7 +54,7 @@ func (cmd *adminListFrontendsCommand) run(_ *cobra.Command, _ []string) {
t.AppendHeader(table.Row{"Token", "zId", "Public Name", "Url Template", "Created At", "Updated At"})
for _, pfe := range resp.Payload {
t.AppendRow(table.Row{
pfe.Token,
pfe.FrontendToken,
pfe.ZID,
pfe.PublicName,
pfe.URLTemplate,

View File

@ -41,7 +41,7 @@ func (cmd *adminListOrgMembersCommand) run(_ *cobra.Command, args []string) {
}
req := admin.NewListOrganizationMembersParams()
req.Body.Token = args[0]
req.Body.OrganizationToken = args[0]
resp, err := zrok.Admin.ListOrganizationMembers(req, mustGetAdminAuth())
if err != nil {

View File

@ -52,7 +52,7 @@ func (c *adminListOrganizationsCommand) run(_ *cobra.Command, _ []string) {
t.SetStyle(table.StyleColoredDark)
t.AppendHeader(table.Row{"Organization Token", "Description"})
for _, org := range resp.Payload.Organizations {
t.AppendRow(table.Row{org.Token, org.Description})
t.AppendRow(table.Row{org.OrganizationToken, org.Description})
}
t.Render()
fmt.Println()

View File

@ -3,7 +3,6 @@ package main
import (
"github.com/openziti/zrok/environment"
"github.com/openziti/zrok/rest_client_zrok/admin"
"github.com/openziti/zrok/rest_model_zrok"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@ -49,11 +48,9 @@ func (cmd *adminUpdateFrontendCommand) run(_ *cobra.Command, args []string) {
}
req := admin.NewUpdateFrontendParams()
req.Body = &rest_model_zrok.UpdateFrontendRequest{
FrontendToken: feToken,
PublicName: cmd.newPublicName,
URLTemplate: cmd.newUrlTemplate,
}
req.Body.FrontendToken = feToken
req.Body.PublicName = cmd.newPublicName
req.Body.URLTemplate = cmd.newUrlTemplate
_, err = zrok.Admin.UpdateFrontend(req, mustGetAdminAuth())
if err != nil {

52
cmd/zrok/agentConsole.go Normal file
View File

@ -0,0 +1,52 @@
package main
import (
"context"
"fmt"
"github.com/openziti/zrok/agent/agentClient"
"github.com/openziti/zrok/agent/agentGrpc"
"github.com/openziti/zrok/environment"
"github.com/openziti/zrok/tui"
"github.com/spf13/cobra"
)
func init() {
agentCmd.AddCommand(newAgentConsoleCommand().cmd)
}
type agentConsoleCommand struct {
cmd *cobra.Command
}
func newAgentConsoleCommand() *agentConsoleCommand {
cmd := &cobra.Command{
Use: "console",
Short: "Open the Agent console",
Args: cobra.NoArgs,
}
command := &agentConsoleCommand{cmd}
cmd.Run = command.run
return command
}
func (cmd *agentConsoleCommand) run(_ *cobra.Command, _ []string) {
root, err := environment.LoadRoot()
if err != nil {
tui.Error("error loading zrokdir", err)
}
client, conn, err := agentClient.NewClient(root)
if err != nil {
tui.Error("error connecting to agent", err)
}
defer func() { _ = conn.Close() }()
v, err := client.Version(context.Background(), &agentGrpc.VersionRequest{})
if err != nil {
tui.Error("error getting agent version", err)
}
if err := openBrowser("http://" + v.ConsoleEndpoint); err != nil {
tui.Error(fmt.Sprintf("unable to open agent console at 'http://%v'", v.ConsoleEndpoint), err)
}
}

View File

@ -0,0 +1,55 @@
package main
import (
"context"
"fmt"
"github.com/openziti/zrok/agent/agentClient"
"github.com/openziti/zrok/agent/agentGrpc"
"github.com/openziti/zrok/environment"
"github.com/openziti/zrok/tui"
"github.com/spf13/cobra"
)
func init() {
agentReleaseCmd.AddCommand(newAgentReleaseAccessCommand().cmd)
}
type agentReleaseAccessCommand struct {
cmd *cobra.Command
}
func newAgentReleaseAccessCommand() *agentReleaseAccessCommand {
cmd := &cobra.Command{
Use: "access <frontendToken>",
Short: "Unbind an access from the zrok Agent",
Args: cobra.ExactArgs(1),
}
command := &agentReleaseAccessCommand{cmd: cmd}
cmd.Run = command.run
return command
}
func (cmd *agentReleaseAccessCommand) run(_ *cobra.Command, args []string) {
root, err := environment.LoadRoot()
if err != nil {
if !panicInstead {
tui.Error("unable to load environment", err)
}
panic(err)
}
client, conn, err := agentClient.NewClient(root)
if err != nil {
tui.Error("error connecting to agent", err)
}
defer conn.Close()
_, err = client.ReleaseAccess(context.Background(), &agentGrpc.ReleaseAccessRequest{
FrontendToken: args[0],
})
if err != nil {
tui.Error("error releasing access", err)
}
fmt.Println("success.")
}

55
cmd/zrok/agentReleaseShare.go Executable file
View File

@ -0,0 +1,55 @@
package main
import (
"context"
"fmt"
"github.com/openziti/zrok/agent/agentClient"
"github.com/openziti/zrok/agent/agentGrpc"
"github.com/openziti/zrok/environment"
"github.com/openziti/zrok/tui"
"github.com/spf13/cobra"
)
func init() {
agentReleaseCmd.AddCommand(newAgentReleaseShareCommand().cmd)
}
type agentReleaseShareCommand struct {
cmd *cobra.Command
}
func newAgentReleaseShareCommand() *agentReleaseShareCommand {
cmd := &cobra.Command{
Use: "share <token>",
Short: "Release a share from the zrok Agent",
Args: cobra.ExactArgs(1),
}
command := &agentReleaseShareCommand{cmd: cmd}
cmd.Run = command.run
return command
}
func (cmd *agentReleaseShareCommand) run(_ *cobra.Command, args []string) {
root, err := environment.LoadRoot()
if err != nil {
if !panicInstead {
tui.Error("unable to load environment", err)
}
panic(err)
}
client, conn, err := agentClient.NewClient(root)
if err != nil {
tui.Error("error connecting to agent", err)
}
defer conn.Close()
_, err = client.ReleaseShare(context.Background(), &agentGrpc.ReleaseShareRequest{
Token: args[0],
})
if err != nil {
tui.Error("error releasing share", err)
}
fmt.Println("success.")
}

72
cmd/zrok/agentStart.go Normal file
View File

@ -0,0 +1,72 @@
package main
import (
"github.com/openziti/zrok/agent"
"github.com/openziti/zrok/environment"
"github.com/openziti/zrok/tui"
"github.com/spf13/cobra"
"os"
"os/signal"
"syscall"
)
func init() {
agentCmd.AddCommand(newAgentStartCommand().cmd)
}
type agentStartCommand struct {
cmd *cobra.Command
consoleAddress string
consoleStartPort uint16
consoleEndPort uint16
}
func newAgentStartCommand() *agentStartCommand {
cmd := &cobra.Command{
Use: "start",
Short: "Start a zrok agent",
Args: cobra.NoArgs,
}
command := &agentStartCommand{cmd: cmd}
cmd.Run = command.run
cmd.Flags().StringVar(&command.consoleAddress, "console-address", "127.0.0.1", "gRPC gateway address")
cmd.Flags().Uint16Var(&command.consoleStartPort, "console-start-port", 8888, "gRPC gateway starting port")
cmd.Flags().Uint16Var(&command.consoleEndPort, "console-end-port", 8988, "gRPC gateway ending port")
return command
}
func (cmd *agentStartCommand) run(_ *cobra.Command, _ []string) {
root, err := environment.LoadRoot()
if err != nil {
tui.Error("error loading zrokdir", err)
}
if !root.IsEnabled() {
tui.Error("unable to load environment; did you 'zrok enable'?", nil)
}
cfg := agent.DefaultConfig()
cfg.ConsoleAddress = cmd.consoleAddress
cfg.ConsoleStartPort = cmd.consoleStartPort
cfg.ConsoleEndPort = cmd.consoleEndPort
a, err := agent.NewAgent(cfg, root)
if err != nil {
tui.Error("error creating agent", err)
}
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
cmd.shutdown(a)
os.Exit(0)
}()
if err := a.Run(); err != nil {
tui.Error("agent aborted", err)
}
}
func (cmd *agentStartCommand) shutdown(a *agent.Agent) {
a.Shutdown()
}

74
cmd/zrok/agentStatus.go Normal file
View File

@ -0,0 +1,74 @@
package main
import (
"context"
"fmt"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/openziti/zrok/agent/agentClient"
"github.com/openziti/zrok/agent/agentGrpc"
"github.com/openziti/zrok/environment"
"github.com/openziti/zrok/tui"
"github.com/spf13/cobra"
"os"
)
func init() {
agentCmd.AddCommand(newAgentStatusCommand().cmd)
}
type agentStatusCommand struct {
cmd *cobra.Command
}
func newAgentStatusCommand() *agentStatusCommand {
cmd := &cobra.Command{
Use: "status",
Short: "Show the status of the running zrok Agent",
Args: cobra.NoArgs,
}
command := &agentStatusCommand{cmd: cmd}
cmd.Run = command.run
return command
}
func (cmd *agentStatusCommand) run(_ *cobra.Command, _ []string) {
root, err := environment.LoadRoot()
if err != nil {
tui.Error("error loading zrokdir", err)
}
client, conn, err := agentClient.NewClient(root)
if err != nil {
tui.Error("error connecting to agent", err)
}
defer conn.Close()
status, err := client.Status(context.Background(), &agentGrpc.StatusRequest{})
if err != nil {
tui.Error("error getting status", err)
}
fmt.Println()
t := table.NewWriter()
t.SetOutputMirror(os.Stdout)
t.SetStyle(table.StyleColoredDark)
t.AppendHeader(table.Row{"Frontend Token", "Token", "Bind Address"})
for _, access := range status.GetAccesses() {
t.AppendRow(table.Row{access.FrontendToken, access.Token, access.BindAddress})
}
t.Render()
fmt.Printf("%d accesses in agent\n", len(status.GetAccesses()))
fmt.Println()
t = table.NewWriter()
t.SetOutputMirror(os.Stdout)
t.SetStyle(table.StyleColoredDark)
t.AppendHeader(table.Row{"Token", "Reserved", "Share Mode", "Backend Mode", "Target"})
for _, share := range status.GetShares() {
t.AppendRow(table.Row{share.Token, share.Reserved, share.ShareMode, share.BackendMode, share.BackendEndpoint})
}
t.Render()
fmt.Printf("%d shares in agent\n", len(status.GetShares()))
fmt.Println()
}

Some files were not shown because too many files have changed in this diff Show More