├── .dockerignore
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ └── release.yaml
├── .gitignore
├── .goreleaser.yml
├── .vscode
└── launch.json
├── CODE_OF_CONDUCT.md
├── LICENSE
├── Makefile
├── README.md
├── agent
├── adapter
│ └── http
│ │ ├── acl.go
│ │ ├── acl_policy.go
│ │ ├── acl_token.go
│ │ ├── agent.go
│ │ ├── connection.go
│ │ ├── errors.go
│ │ ├── fallthrough.go
│ │ ├── interface.go
│ │ ├── middleware
│ │ ├── cors.go
│ │ └── logging.go
│ │ ├── network.go
│ │ ├── node.go
│ │ ├── spa.go
│ │ ├── status.go
│ │ └── util.go
├── agent.go
├── banner.go
├── config.go
├── conn
│ ├── conn.go
│ └── conn_test.go
└── ui.go
├── air.conf
├── air.sh
├── api
├── README.md
├── acl.go
├── acl_policy.go
├── acl_token.go
├── agent.go
├── api.go
├── config.go
├── connection.go
├── error.go
├── interface.go
├── network.go
└── node.go
├── build
├── Dockerfile.builder
└── Dockerfile.linux_amd64
├── client
├── client.go
├── config.go
├── nic
│ ├── controller.go
│ ├── controller_test.go
│ ├── nic.go
│ └── util.go
├── state
│ ├── boltdb
│ │ └── boltdb.go
│ ├── inmem
│ │ └── inmem.go
│ └── state.go
└── util.go
├── command
├── acl.go
├── acl_bootstrap.go
├── acl_policy.go
├── acl_policy_apply.go
├── acl_policy_delete.go
├── acl_policy_info.go
├── acl_policy_list.go
├── acl_token.go
├── acl_token_create.go
├── acl_token_delete.go
├── acl_token_info.go
├── acl_token_list.go
├── acl_token_self.go
├── acl_token_update.go
├── agent.go
├── agent_info.go
├── command.go
├── connection.go
├── connection_create.go
├── connection_delete.go
├── connection_list.go
├── connection_update.go
├── connection_update_rules.go
├── interface.go
├── interface_list.go
├── interface_update.go
├── network.go
├── network_create.go
├── network_delete.go
├── network_info.go
├── network_list.go
├── node.go
├── node_info.go
├── node_join.go
├── node_leave.go
├── node_list.go
├── node_status.go
├── ui.go
├── util.go
└── version.go
├── docs
├── .nojekyll
├── README.md
├── _404.md
├── _coverpage.md
├── _navbar.md
├── _sidebar.md
├── api
│ ├── README.md
│ ├── acl-policies.md
│ ├── acl-tokens.md
│ ├── connections.md
│ ├── interfaces.md
│ ├── networks.md
│ ├── nodes.md
│ ├── status.md
│ └── ui.md
├── assets
│ └── logos
│ │ └── dragopher.svg
├── contributing.md
├── css
│ └── custom.css
├── docs
│ ├── README.md
│ ├── commands
│ │ ├── README.md
│ │ ├── acl
│ │ │ ├── README.md
│ │ │ ├── bootstrap.md
│ │ │ ├── policy-apply.md
│ │ │ ├── policy-delete.md
│ │ │ ├── policy-info.md
│ │ │ ├── policy-list.md
│ │ │ ├── token-create.md
│ │ │ ├── token-delete.md
│ │ │ ├── token-info.md
│ │ │ ├── token-list.md
│ │ │ ├── token-self.md
│ │ │ └── token-update.md
│ │ ├── agent-info.md
│ │ ├── agent.md
│ │ ├── connection
│ │ │ ├── create.md
│ │ │ ├── list.md
│ │ │ └── update.md
│ │ ├── interface
│ │ │ ├── list.md
│ │ │ └── update.md
│ │ ├── network
│ │ │ ├── create.md
│ │ │ ├── delete.md
│ │ │ ├── info.md
│ │ │ └── list.md
│ │ ├── node
│ │ │ ├── info.md
│ │ │ ├── join.md
│ │ │ ├── leave.md
│ │ │ ├── list.md
│ │ │ └── status.md
│ │ └── ui.md
│ ├── configuration
│ │ ├── README.md
│ │ ├── acl.md
│ │ ├── client.md
│ │ └── server.md
│ ├── installing
│ │ ├── README.md
│ │ └── quickstart.md
│ ├── internals
│ │ ├── README.md
│ │ └── architecture.md
│ └── overview.md
├── faq.md
├── favicon.ico
├── index.html
├── js
│ └── darklight-plugin.js
└── license.md
├── drago
├── acl.go
├── acl_policy.go
├── acl_token.go
├── auth
│ ├── auth.go
│ ├── policy.go
│ ├── rule.go
│ └── token.go
├── config.go
├── connection.go
├── interface.go
├── mock
│ └── mock.go
├── network.go
├── node.go
├── server.go
├── state
│ ├── etcd
│ │ ├── acl_policy.go
│ │ ├── acl_state.go
│ │ ├── acl_token.go
│ │ ├── connection.go
│ │ ├── etcd.go
│ │ ├── interface.go
│ │ ├── network.go
│ │ └── node.go
│ ├── inmem
│ │ ├── acl_policy.go
│ │ ├── acl_state.go
│ │ ├── acl_token.go
│ │ ├── connection.go
│ │ ├── inmem.go
│ │ ├── interface.go
│ │ ├── network.go
│ │ └── node.go
│ └── state.go
├── status.go
├── structs
│ ├── acl.go
│ ├── acl_policy.go
│ ├── acl_token.go
│ ├── agent.go
│ ├── config
│ │ ├── acl.go
│ │ └── etcd.go
│ ├── connection.go
│ ├── errors.go
│ ├── interface.go
│ ├── network.go
│ ├── node.go
│ ├── status.go
│ └── structs.go
└── test
│ ├── acl_test.go
│ ├── interface_test.go
│ └── node_test.go
├── dragopher.svg
├── example
├── client1.hcl
├── client2.hcl
└── server.hcl
├── go.mod
├── go.sum
├── init
├── README.md
└── systemd
│ └── drago.service
├── main.go
├── pkg
├── acl
│ ├── README.md
│ ├── acl.go
│ ├── acl_test.go
│ ├── config.go
│ ├── errors.go
│ ├── logger.go
│ ├── model.go
│ ├── policy.go
│ ├── resolver.go
│ └── token.go
├── cli
│ ├── cli.go
│ ├── command.go
│ ├── command_mock.go
│ ├── config.go
│ ├── help.go
│ ├── router.go
│ └── ui.go
├── concurrent
│ └── map.go
├── http
│ ├── config.go
│ ├── handler.go
│ ├── http.go
│ └── middleware.go
├── log
│ ├── log.go
│ ├── logrus
│ │ └── logrus.go
│ ├── simple
│ │ └── simple.go
│ └── zap
│ │ └── zap.go
├── radix
│ ├── README.md
│ ├── node.go
│ ├── radix.go
│ └── radix_test.go
├── rpc
│ ├── codec.go
│ ├── config.go
│ └── rpc.go
├── string
│ └── string.go
├── util
│ └── util.go
├── uuid
│ └── uuid.go
└── validator
│ └── validator.go
├── plugin
├── admission
│ └── admission.go
├── lease
│ └── lease.go
├── mesh
│ └── mesh.go
├── notification
│ └── notification.go
└── plugin.go
├── ui
├── .babelrc
├── .eslintrc
├── .prettierrc
├── .vscode
│ └── settings.json
├── README.md
├── build
│ └── .keep
├── config
│ ├── rewired
│ │ └── index.js
│ └── webpack
│ │ ├── eslint.js
│ │ └── resolve.js
├── jsconfig.json
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── manifest.json
│ └── stylesheet.css
├── src
│ ├── assets
│ │ ├── fonts
│ │ │ ├── Lato-Black.woff
│ │ │ ├── Lato-Black.woff2
│ │ │ ├── Lato-BlackItalic.woff
│ │ │ ├── Lato-BlackItalic.woff2
│ │ │ ├── Lato-Bold.woff
│ │ │ ├── Lato-Bold.woff2
│ │ │ ├── Lato-BoldItalic.woff
│ │ │ ├── Lato-BoldItalic.woff2
│ │ │ ├── Lato-Hairline.woff
│ │ │ ├── Lato-Hairline.woff2
│ │ │ ├── Lato-HairlineItalic.woff
│ │ │ ├── Lato-HairlineItalic.woff2
│ │ │ ├── Lato-Italic.woff
│ │ │ ├── Lato-Italic.woff2
│ │ │ ├── Lato-Light.woff
│ │ │ ├── Lato-Light.woff2
│ │ │ ├── Lato-LightItalic.woff
│ │ │ ├── Lato-LightItalic.woff2
│ │ │ ├── Lato-Regular.woff
│ │ │ ├── Lato-Regular.woff2
│ │ │ ├── Montserrat-Black.woff
│ │ │ ├── Montserrat-Black.woff2
│ │ │ ├── Montserrat-BlackItalic.woff
│ │ │ ├── Montserrat-BlackItalic.woff2
│ │ │ ├── Montserrat-Bold.woff
│ │ │ ├── Montserrat-Bold.woff2
│ │ │ ├── Montserrat-BoldItalic.woff
│ │ │ ├── Montserrat-BoldItalic.woff2
│ │ │ ├── Montserrat-ExtraBold.woff
│ │ │ ├── Montserrat-ExtraBold.woff2
│ │ │ ├── Montserrat-ExtraBoldItalic.woff
│ │ │ ├── Montserrat-ExtraBoldItalic.woff2
│ │ │ ├── Montserrat-ExtraLight.woff
│ │ │ ├── Montserrat-ExtraLight.woff2
│ │ │ ├── Montserrat-ExtraLightItalic.woff
│ │ │ ├── Montserrat-ExtraLightItalic.woff2
│ │ │ ├── Montserrat-Italic.woff
│ │ │ ├── Montserrat-Italic.woff2
│ │ │ ├── Montserrat-Light.woff
│ │ │ ├── Montserrat-Light.woff2
│ │ │ ├── Montserrat-LightItalic.woff
│ │ │ ├── Montserrat-LightItalic.woff2
│ │ │ ├── Montserrat-Medium.woff
│ │ │ ├── Montserrat-Medium.woff2
│ │ │ ├── Montserrat-MediumItalic.woff
│ │ │ ├── Montserrat-MediumItalic.woff2
│ │ │ ├── Montserrat-Regular.woff
│ │ │ ├── Montserrat-Regular.woff2
│ │ │ ├── Montserrat-SemiBold.woff
│ │ │ ├── Montserrat-SemiBold.woff2
│ │ │ ├── Montserrat-SemiBoldItalic.woff
│ │ │ ├── Montserrat-SemiBoldItalic.woff2
│ │ │ ├── Montserrat-Thin.woff
│ │ │ ├── Montserrat-Thin.woff2
│ │ │ ├── Montserrat-ThinItalic.woff
│ │ │ ├── Montserrat-ThinItalic.woff2
│ │ │ ├── Raleway-Black.woff
│ │ │ ├── Raleway-Black.woff2
│ │ │ ├── Raleway-BlackItalic.woff
│ │ │ ├── Raleway-BlackItalic.woff2
│ │ │ ├── Raleway-Bold.woff
│ │ │ ├── Raleway-Bold.woff2
│ │ │ ├── Raleway-BoldItalic.woff
│ │ │ ├── Raleway-BoldItalic.woff2
│ │ │ ├── Raleway-ExtraBold.woff
│ │ │ ├── Raleway-ExtraBold.woff2
│ │ │ ├── Raleway-ExtraBoldItalic.woff
│ │ │ ├── Raleway-ExtraBoldItalic.woff2
│ │ │ ├── Raleway-ExtraLight.woff
│ │ │ ├── Raleway-ExtraLight.woff2
│ │ │ ├── Raleway-ExtraLightItalic.woff
│ │ │ ├── Raleway-ExtraLightItalic.woff2
│ │ │ ├── Raleway-Italic.woff
│ │ │ ├── Raleway-Italic.woff2
│ │ │ ├── Raleway-Light.woff
│ │ │ ├── Raleway-Light.woff2
│ │ │ ├── Raleway-LightItalic.woff
│ │ │ ├── Raleway-LightItalic.woff2
│ │ │ ├── Raleway-Medium.woff
│ │ │ ├── Raleway-Medium.woff2
│ │ │ ├── Raleway-MediumItalic.woff
│ │ │ ├── Raleway-MediumItalic.woff2
│ │ │ ├── Raleway-Regular.woff
│ │ │ ├── Raleway-Regular.woff2
│ │ │ ├── Raleway-SemiBold.woff
│ │ │ ├── Raleway-SemiBold.woff2
│ │ │ ├── Raleway-SemiBoldItalic.woff
│ │ │ ├── Raleway-SemiBoldItalic.woff2
│ │ │ ├── Raleway-Thin.woff
│ │ │ ├── Raleway-Thin.woff2
│ │ │ ├── Raleway-ThinItalic.woff
│ │ │ ├── Raleway-ThinItalic.woff2
│ │ │ ├── Roboto-Black.woff
│ │ │ ├── Roboto-Black.woff2
│ │ │ ├── Roboto-BlackItalic.woff
│ │ │ ├── Roboto-BlackItalic.woff2
│ │ │ ├── Roboto-Bold.woff
│ │ │ ├── Roboto-Bold.woff2
│ │ │ ├── Roboto-BoldItalic.woff
│ │ │ ├── Roboto-BoldItalic.woff2
│ │ │ ├── Roboto-Italic.woff
│ │ │ ├── Roboto-Italic.woff2
│ │ │ ├── Roboto-Light.woff
│ │ │ ├── Roboto-Light.woff2
│ │ │ ├── Roboto-LightItalic.woff
│ │ │ ├── Roboto-LightItalic.woff2
│ │ │ ├── Roboto-Medium.woff
│ │ │ ├── Roboto-Medium.woff2
│ │ │ ├── Roboto-MediumItalic.woff
│ │ │ ├── Roboto-MediumItalic.woff2
│ │ │ ├── Roboto-Regular.woff
│ │ │ ├── Roboto-Regular.woff2
│ │ │ ├── Roboto-Thin.woff
│ │ │ ├── Roboto-Thin.woff2
│ │ │ ├── Roboto-ThinItalic.woff
│ │ │ ├── Roboto-ThinItalic.woff2
│ │ │ └── index.css
│ │ ├── icons
│ │ │ ├── back.svg
│ │ │ ├── bell.svg
│ │ │ ├── buoy.svg
│ │ │ ├── connection-small.svg
│ │ │ ├── connection.svg
│ │ │ ├── dots.svg
│ │ │ ├── down.svg
│ │ │ ├── error.svg
│ │ │ ├── external-link.svg
│ │ │ ├── host.svg
│ │ │ ├── index.js
│ │ │ ├── interface.svg
│ │ │ ├── key.svg
│ │ │ ├── label.svg
│ │ │ ├── leave.svg
│ │ │ ├── left.svg
│ │ │ ├── link.svg
│ │ │ ├── logo.svg
│ │ │ ├── network.svg
│ │ │ ├── plus.svg
│ │ │ ├── refresh.svg
│ │ │ ├── right.svg
│ │ │ ├── search.svg
│ │ │ ├── success.svg
│ │ │ ├── times.svg
│ │ │ ├── trash.svg
│ │ │ ├── unknown.svg
│ │ │ ├── up.svg
│ │ │ └── warning.svg
│ │ ├── illustrations
│ │ │ ├── empty.svg
│ │ │ ├── error.svg
│ │ │ ├── index.js
│ │ │ ├── not-found.svg
│ │ │ └── unauthorized.svg
│ │ └── index.js
│ ├── components
│ │ ├── avatar
│ │ │ └── index.js
│ │ ├── back-link
│ │ │ └── index.js
│ │ ├── box
│ │ │ └── index.js
│ │ ├── button
│ │ │ └── index.js
│ │ ├── collapse
│ │ │ └── index.js
│ │ ├── confirmation-dialog
│ │ │ ├── dialog.js
│ │ │ └── index.js
│ │ ├── empty-state
│ │ │ └── index.js
│ │ ├── error-state
│ │ │ └── index.js
│ │ ├── flex
│ │ │ └── index.js
│ │ ├── headers
│ │ │ └── index.js
│ │ ├── icon-button
│ │ │ └── index.js
│ │ ├── icon
│ │ │ └── index.js
│ │ ├── inputs
│ │ │ ├── number-input
│ │ │ │ └── index.js
│ │ │ ├── search-input
│ │ │ │ └── index.js
│ │ │ ├── select-input
│ │ │ │ └── index.js
│ │ │ ├── tags-input
│ │ │ │ └── index.js
│ │ │ └── text-input
│ │ │ │ └── index.js
│ │ ├── key-value
│ │ │ └── index.js
│ │ ├── link
│ │ │ └── index.js
│ │ ├── list
│ │ │ └── index.js
│ │ ├── nav
│ │ │ └── index.js
│ │ ├── network-select-input
│ │ │ └── index.js
│ │ ├── node-select-input
│ │ │ └── index.js
│ │ ├── popover
│ │ │ └── index.js
│ │ ├── separator
│ │ │ └── index.js
│ │ ├── spinner
│ │ │ ├── dragon.js
│ │ │ ├── index.js
│ │ │ └── jellyfish.js
│ │ ├── steps
│ │ │ └── index.js
│ │ ├── text
│ │ │ └── index.js
│ │ ├── toast
│ │ │ └── index.js
│ │ ├── tooltip
│ │ │ └── index.js
│ │ └── unauthorized-state
│ │ │ └── index.js
│ ├── containers
│ │ ├── footer
│ │ │ └── index.js
│ │ ├── header
│ │ │ └── index.js
│ │ └── side-nav
│ │ │ ├── brand.js
│ │ │ ├── index.js
│ │ │ └── styled.js
│ ├── environment.js
│ ├── graphql
│ │ ├── apollo-provider.js
│ │ ├── local-state.js
│ │ ├── mutations
│ │ │ └── index.js
│ │ └── queries
│ │ │ └── index.js
│ ├── index.js
│ ├── mock
│ │ ├── commands.js
│ │ ├── factories.js
│ │ ├── identity.js
│ │ ├── index.js
│ │ ├── models.js
│ │ ├── queries.js
│ │ ├── routes.js
│ │ ├── seeds.js
│ │ └── util.js
│ ├── modals
│ │ ├── admit-node
│ │ │ ├── index.js
│ │ │ └── node-card.js
│ │ ├── connect-peer
│ │ │ ├── index.js
│ │ │ └── peer-card.js
│ │ └── join-network
│ │ │ ├── index.js
│ │ │ └── network-card.js
│ ├── serviceWorker.js
│ ├── styles
│ │ ├── index.js
│ │ └── themes
│ │ │ └── light.js
│ ├── utils
│ │ ├── formik-utils.js
│ │ ├── hocs
│ │ │ ├── index.js
│ │ │ └── with-validity-indicator.js
│ │ ├── toast-provider.js
│ │ └── use-localstorage.js
│ └── views
│ │ ├── app
│ │ └── index.js
│ │ ├── clients
│ │ ├── details
│ │ │ ├── connection-card.js
│ │ │ ├── index.js
│ │ │ ├── interface-card.js
│ │ │ └── network-card.js
│ │ ├── index.js
│ │ └── list
│ │ │ ├── client-card.js
│ │ │ └── index.js
│ │ ├── home
│ │ └── index.js
│ │ ├── networks
│ │ ├── details
│ │ │ ├── graph
│ │ │ │ ├── graph.js
│ │ │ │ ├── index.js
│ │ │ │ ├── interface-card.js
│ │ │ │ └── link-card.js
│ │ │ ├── index.js
│ │ │ ├── peer-card.js
│ │ │ └── topology.js
│ │ ├── index.js
│ │ ├── list
│ │ │ ├── index.js
│ │ │ └── network-card.js
│ │ └── new
│ │ │ └── index.js
│ │ ├── not-found
│ │ └── index.js
│ │ └── settings
│ │ ├── index.js
│ │ └── tokens
│ │ └── index.js
├── yarn-error.log
└── yarn.lock
└── version
└── version.go
/.dockerignore:
--------------------------------------------------------------------------------
1 | ui/node_modules
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/workflows/release.yaml:
--------------------------------------------------------------------------------
1 | name: Release Drago
2 |
3 | on:
4 | push:
5 | tags:
6 | - "v*"
7 |
8 | jobs:
9 | release:
10 | name: Release
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - name: Checkout branch
15 | uses: actions/checkout@v2
16 | with:
17 | fetch-depth: 0
18 |
19 | - name: Setup Go
20 | uses: actions/setup-go@v2
21 | with:
22 | go-version: 1.16
23 |
24 | - name: Setup Node
25 | uses: actions/setup-node@v2
26 | with:
27 | node-version: '14'
28 |
29 | - name: Run GoReleaser
30 | uses: goreleaser/goreleaser-action@v2
31 | with:
32 | version: latest
33 | args: release --rm-dist
34 | env:
35 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
36 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.gitignore.io/api/go
2 | # Edit at https://www.gitignore.io/?templates=go
3 |
4 | ### Go ###
5 | # Binaries for programs and plugins
6 | *.exe
7 | *.exe~
8 | *.dll
9 | *.so
10 | *.dylib
11 |
12 | # Test binary, built with `go test -c`
13 | *.test
14 |
15 | # Output of the go coverage tool, specifically when used with LiteIDE
16 | *.out
17 |
18 | # Dependency directories (remove the comment below to include it)
19 | # vendor/
20 |
21 | bin/
22 |
23 | ### Go Patch ###
24 | vendor/
25 | Godeps/
26 |
27 | # End of https://www.gitignore.io/api/go
28 | ui/node_modules/*
29 | ui/.eslintcache
30 | ui/build/*
31 | vendor/*
32 |
33 | !*.keep
34 |
35 | tmp
36 | dist/
37 |
--------------------------------------------------------------------------------
/.goreleaser.yml:
--------------------------------------------------------------------------------
1 | before:
2 | hooks:
3 | - go mod tidy
4 | - go generate ./...
5 | builds:
6 | - env:
7 | - CGO_ENABLED=0
8 | - GO111MODULE=on
9 | goos:
10 | - linux
11 | goarch:
12 | - amd64
13 | - arm
14 | - arm64
15 | ldflags:
16 | - -s -w -extldflags "-static"
17 | - -s -d -X version.Version={{ .Version }}
18 | archives:
19 | - replacements:
20 | darwin: Darwin
21 | linux: Linux
22 | windows: Windows
23 | 386: i386
24 | amd64: x86_64
25 | name_template: "{{ .ProjectName }}_v{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
26 | checksum:
27 | name_template: "{{ .ProjectName }}_v{{ .Version }}_checksums.txt"
28 | snapshot:
29 | name_template: "{{ .Tag }}-next"
30 | release:
31 | name_template: "v{{ .Version }}"
32 | changelog:
33 | sort: asc
34 | filters:
35 | exclude:
36 | - '^docs:'
37 | - '^test:'
38 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Debug Drago Development Mode",
9 | "type": "go",
10 | "request": "launch",
11 | "mode": "debug",
12 | "program": "${workspaceFolder}",
13 | "args": ["agent", "--dev"]
14 | },
15 | {
16 | "name": "Debug Drago Server",
17 | "type": "go",
18 | "request": "launch",
19 | "mode": "debug",
20 | "program": "${workspaceFolder}",
21 | "args": ["agent", "--config=./dist/server.hcl"]
22 | },
23 | {
24 | "name": "Debug Drago Client",
25 | "type": "go",
26 | "request": "launch",
27 | "mode": "debug",
28 | "program": "${workspaceFolder}",
29 | "args": ["agent", "--config=./dist/client1.hcl"]
30 | },
31 | ]
32 | }
--------------------------------------------------------------------------------
/agent/adapter/http/acl.go:
--------------------------------------------------------------------------------
1 | package http
2 |
3 | import (
4 | "net/http"
5 |
6 | "github.com/seashell/drago/agent/conn"
7 | structs "github.com/seashell/drago/drago/structs"
8 | )
9 |
10 | // ACLHandler :
11 | type ACLHandler struct {
12 | rpcConn conn.RPCConnection
13 | }
14 |
15 | // NewACLHandler :
16 | func NewACLHandler(conn conn.RPCConnection) *ACLHandler {
17 | return &ACLHandler{
18 | rpcConn: conn,
19 | }
20 | }
21 |
22 | // Handle :
23 | func (h *ACLHandler) Handle(rw http.ResponseWriter, req *http.Request) (interface{}, error) {
24 |
25 | params := parsePathParams(req)
26 | if len(params) > 1 {
27 | return nil, NewCodedError(404, ErrNotFound)
28 | }
29 |
30 | switch params[0] {
31 | case "bootstrap":
32 | return h.handleBootstrap(rw, req)
33 | default:
34 | return nil, NewCodedError(404, "Not found")
35 | }
36 |
37 | }
38 |
39 | func (h *ACLHandler) handleBootstrap(rw http.ResponseWriter, req *http.Request) (interface{}, error) {
40 |
41 | if req.Method != "POST" {
42 | return nil, NewCodedError(405, ErrMethodNotAllowed)
43 | }
44 |
45 | args := structs.ACLBootstrapRequest{
46 | WriteRequest: parseWriteRequestOptions(req),
47 | }
48 |
49 | var out structs.ACLTokenUpsertResponse
50 | if err := h.rpcConn.Call("ACL.BootstrapACL", &args, &out); err != nil {
51 | return nil, parseError(err)
52 | }
53 |
54 | return out.ACLToken, nil
55 | }
56 |
--------------------------------------------------------------------------------
/agent/adapter/http/agent.go:
--------------------------------------------------------------------------------
1 | package http
2 |
3 | import (
4 | "net/http"
5 |
6 | conn "github.com/seashell/drago/agent/conn"
7 | structs "github.com/seashell/drago/drago/structs"
8 | )
9 |
10 | type AgentAdapter interface {
11 | Config() map[string]interface{}
12 | Stats() map[string]map[string]string
13 | }
14 |
15 | // AgentHandler provides an API for interacting with an Agent
16 | // in runtime, getting stats and setting configs.
17 | type AgentHandler struct {
18 | agent AgentAdapter
19 | }
20 |
21 | // NewAgentHandler :
22 | func NewAgentHandler(conn conn.RPCConnection, ag AgentAdapter) *AgentHandler {
23 | return &AgentHandler{
24 | agent: ag,
25 | }
26 | }
27 |
28 | // Handle :
29 | func (h *AgentHandler) Handle(rw http.ResponseWriter, req *http.Request) (interface{}, error) {
30 |
31 | params := parsePathParams(req)
32 | if len(params) > 1 {
33 | return nil, NewCodedError(404, ErrNotFound)
34 | }
35 |
36 | id := params[0]
37 |
38 | switch req.Method {
39 | case "GET":
40 | return h.handleGet(rw, req, id)
41 | default:
42 | return nil, NewCodedError(405, ErrMethodNotAllowed)
43 | }
44 | }
45 |
46 | func (h *AgentHandler) handleGet(rw http.ResponseWriter, req *http.Request, id string) (interface{}, error) {
47 |
48 | if id != "self" {
49 | return nil, NewCodedError(404, ErrNotFound)
50 | }
51 |
52 | self := &structs.Agent{
53 | Config: map[string]interface{}{},
54 | Stats: h.agent.Stats(),
55 | }
56 |
57 | return self, nil
58 | }
59 |
--------------------------------------------------------------------------------
/agent/adapter/http/errors.go:
--------------------------------------------------------------------------------
1 | package http
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | const (
8 | ErrNotFound = "Not found"
9 | ErrInternal = "Internal error"
10 | ErrBadRequest = "Bad request"
11 | ErrMethodNotAllowed = "Method not allowed"
12 | )
13 |
14 | // CodedError represents an error generated by an HTTP handler.
15 | // Besides implementing the standard error interface, it also
16 | // implements the http.Error interface, which includes a method
17 | // for obtaining the HTTP status code associated with the error.
18 | type CodedError struct {
19 | // Code is an HTTP status code associated with the error.
20 | code int
21 |
22 | // Message is a human-readable message that describes the error.
23 | Message string
24 | }
25 |
26 | // NewCodedError : creates a new HTTPError object.
27 | func NewCodedError(code int, s string, extra ...interface{}) CodedError {
28 | msg := s
29 | // TODO: only append extra to msg if DEBUG enabled
30 | for _, v := range extra {
31 | msg = fmt.Sprintf("%s : %v", msg, v)
32 | }
33 | return CodedError{code, msg}
34 | }
35 |
36 | // Error returns a string representation for the Error type.
37 | func (e CodedError) Error() string {
38 | return fmt.Sprintf("%s", e.Message)
39 | }
40 |
41 | // Code returns the HTTP status code associated with the error.
42 | func (e CodedError) Code() int {
43 | return e.code
44 | }
45 |
--------------------------------------------------------------------------------
/agent/adapter/http/fallthrough.go:
--------------------------------------------------------------------------------
1 | package http
2 |
3 | import (
4 | "net/http"
5 | )
6 |
7 | // FallthroughHandler :
8 | type FallthroughHandler struct {
9 | redirectTo string
10 | }
11 |
12 | // NewFallthroughHandler :
13 | func NewFallthroughHandler(to string) *FallthroughHandler {
14 | return &FallthroughHandler{redirectTo: to}
15 | }
16 |
17 | // Handle :
18 | func (a *FallthroughHandler) Handle(rw http.ResponseWriter, req *http.Request) (interface{}, error) {
19 | switch req.Method {
20 | case "GET":
21 | return a.handleGet(rw, req)
22 | default:
23 | return nil, NewCodedError(405, ErrMethodNotAllowed)
24 | }
25 | }
26 |
27 | func (a *FallthroughHandler) handleGet(rw http.ResponseWriter, req *http.Request) (interface{}, error) {
28 | if req.URL.Path == "/" {
29 | http.Redirect(rw, req, a.redirectTo, 307)
30 | } else {
31 | return nil, NewCodedError(404, ErrNotFound)
32 | }
33 | return nil, nil
34 | }
35 |
--------------------------------------------------------------------------------
/agent/adapter/http/middleware/cors.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "net/http"
5 | )
6 |
7 | // CORS :
8 | func CORS() func(http.HandlerFunc) http.HandlerFunc {
9 | m := func(next http.HandlerFunc) http.HandlerFunc {
10 | return func(rw http.ResponseWriter, req *http.Request) {
11 | rw.Header().Set("Access-Control-Allow-Origin", "*")
12 | rw.Header().Set("Access-Control-Allow-Methods", "*")
13 | rw.Header().Set("Access-Control-Allow-Headers", "Origin, Accept, Referer, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
14 | if req.Method == "OPTIONS" {
15 | rw.WriteHeader(http.StatusOK)
16 | return
17 | }
18 | next(rw, req)
19 | }
20 | }
21 | return m
22 | }
23 |
--------------------------------------------------------------------------------
/agent/adapter/http/middleware/logging.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "bufio"
5 | "errors"
6 | "net"
7 | "net/http"
8 |
9 | "time"
10 |
11 | log "github.com/seashell/drago/pkg/log"
12 | )
13 |
14 | // LoggingResponseWriter :
15 | type LoggingResponseWriter struct {
16 | http.ResponseWriter
17 | code int
18 | }
19 |
20 | // NewLoggingResponseWriter :
21 | func NewLoggingResponseWriter(rw http.ResponseWriter) *LoggingResponseWriter {
22 | return &LoggingResponseWriter{rw, http.StatusOK}
23 | }
24 |
25 | // WriteHeader :
26 | func (lrw *LoggingResponseWriter) WriteHeader(code int) {
27 | lrw.code = code
28 | lrw.ResponseWriter.WriteHeader(code)
29 | }
30 |
31 | // Hijack :
32 | func (lrw *LoggingResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
33 | h, ok := lrw.ResponseWriter.(http.Hijacker)
34 | if !ok {
35 | return nil, nil, errors.New("hijacking not supported")
36 | }
37 | return h.Hijack()
38 | }
39 |
40 | // Logging :
41 | func Logging(logger log.Logger) func(http.HandlerFunc) http.HandlerFunc {
42 |
43 | m := func(next http.HandlerFunc) http.HandlerFunc {
44 |
45 | return func(rw http.ResponseWriter, req *http.Request) {
46 |
47 | start := time.Now()
48 | url := req.RequestURI
49 |
50 | logger.Debugf("Request received (remote=%s, method=%s, path=%s)", req.RemoteAddr, req.Method, url)
51 |
52 | var status int
53 | defer func() {
54 | logger.Debugf("Request completed with status %d (method=%s, path=%s, duration=%s)", status, req.Method, url, time.Now().Sub(start))
55 | }()
56 |
57 | lrw := NewLoggingResponseWriter(rw)
58 | next(lrw, req)
59 | status = lrw.code
60 | }
61 | }
62 |
63 | return m
64 | }
65 |
--------------------------------------------------------------------------------
/agent/adapter/http/spa.go:
--------------------------------------------------------------------------------
1 | package http
2 |
3 | import (
4 | "net/http"
5 | "strings"
6 | )
7 |
8 | // SinglePageApplicationHandler :
9 | type SinglePageApplicationHandler struct {
10 | fsHandler http.Handler
11 | placeholder string
12 | }
13 |
14 | // NewSinglePageApplicationHandler : Create a new SPA handler that serves static files
15 | // from a http.FileSystem passed as argument. If the latter is nil, serve a placeholder string.
16 | func NewSinglePageApplicationHandler(fs http.FileSystem, s string) *SinglePageApplicationHandler {
17 |
18 | handler := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
19 | rw.Write([]byte(s))
20 | })
21 |
22 | if fs != nil {
23 | handler = http.FileServer(fs).ServeHTTP
24 | }
25 |
26 | return &SinglePageApplicationHandler{
27 | placeholder: s,
28 | fsHandler: handler,
29 | }
30 | }
31 |
32 | func (h *SinglePageApplicationHandler) Handle(rw http.ResponseWriter, req *http.Request) (interface{}, error) {
33 |
34 | path := req.URL.Path
35 |
36 | if path == "" || strings.HasPrefix(path, "/static") {
37 | h.fsHandler.ServeHTTP(rw, req)
38 | } else if path == "/" {
39 | h.fsHandler.ServeHTTP(rw, req)
40 | } else {
41 | req.URL.Path = "/"
42 | h.fsHandler.ServeHTTP(rw, req)
43 | }
44 |
45 | return nil, nil
46 | }
47 |
--------------------------------------------------------------------------------
/agent/adapter/http/status.go:
--------------------------------------------------------------------------------
1 | package http
2 |
3 | import (
4 | "net/http"
5 |
6 | "github.com/seashell/drago/agent/conn"
7 | "github.com/seashell/drago/drago/structs"
8 | )
9 |
10 | // StatusHandler is used to check on server status
11 | type StatusHandler struct {
12 | rpcConn conn.RPCConnection
13 | }
14 |
15 | // NewStatusHandler :
16 | func NewStatusHandler(conn conn.RPCConnection) *StatusHandler {
17 | return &StatusHandler{
18 | rpcConn: conn,
19 | }
20 | }
21 |
22 | // Handle :
23 | func (h *StatusHandler) Handle(rw http.ResponseWriter, req *http.Request) (interface{}, error) {
24 | switch req.Method {
25 | case "GET":
26 | return h.handleGet(rw, req)
27 | default:
28 | return nil, NewCodedError(405, ErrMethodNotAllowed)
29 | }
30 | }
31 |
32 | func (h *StatusHandler) handleGet(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
33 |
34 | var args structs.GenericRequest
35 |
36 | var out structs.GenericResponse
37 | if err := h.rpcConn.Call("Status.Ping", &args, &out); err != nil {
38 | return nil, err
39 | }
40 |
41 | return out, nil
42 | }
43 |
--------------------------------------------------------------------------------
/agent/banner.go:
--------------------------------------------------------------------------------
1 | package agent
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/seashell/drago/version"
7 | )
8 |
9 | // Banner is a banner to be displayed when the Drago
10 | // agent is started
11 | var Banner = fmt.Sprintf(`
12 | ====|===================>
13 | ___ ____ ____ ____ ____
14 | | \ |__/ |__| | __ | |
15 | |__/ | \ | | |__] |__|
16 |
17 | {{ .AnsiColor.Cyan }}%s{{ .AnsiColor.Default }}
18 | <===================|====
19 |
20 | `, version.GetVersion().VersionNumber())
21 |
--------------------------------------------------------------------------------
/agent/conn/conn_test.go:
--------------------------------------------------------------------------------
1 | package conn
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestCreateConn(t *testing.T) {
9 |
10 | conn := NewRPCConnection("127.0.0.1:8081", nil)
11 |
12 | if err := conn.Call("TestMethod", struct{}{}, nil); err != nil {
13 | fmt.Println(err)
14 | }
15 |
16 | t.Log("success!")
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/agent/ui.go:
--------------------------------------------------------------------------------
1 | package agent
2 |
3 | const uiStubHTML = `
4 |
5 |
6 | ====|===================>
7 | ___ ____ ____ ____ ____
8 | | \ |__/ |__| | __ | |
9 | |__/ | \ | | |__] |__|
10 |
11 | <===================|====
12 |
13 |
14 | If you're seeing this message, it means that Drago's web UI was not built properly.
15 | In order to build it, run go generate from the project root directory.
16 |
17 |
18 | `
19 |
--------------------------------------------------------------------------------
/air.conf:
--------------------------------------------------------------------------------
1 | # Config file for [Air](https://github.com/cosmtrek/air) in TOML format
2 |
3 | # Working directory
4 | # . or absolute path, please note that the directories following must be under root.
5 | root = "."
6 | tmp_dir = "tmp"
7 |
8 | [build]
9 | # Just plain old shell command. You could use `make` as well.
10 | cmd = "go build -o ./tmp/air/drago ."
11 | # Binary file yields from `cmd`.
12 | bin = "tmp/air/drago agent"
13 | # Customize binary.
14 | full_bin = "APP_ENV=dev APP_USER=air ./tmp/air/drago agent --dev"
15 | # Watch these filename extensions.
16 | include_ext = ["go", "tpl", "tmpl", "html"]
17 | # Ignore these filename extensions or directories.
18 | exclude_dir = ["assets", "tmp", "vendor", "ui", "data"]
19 | # Watch these directories if you specified.
20 | include_dir = []
21 | # Exclude files.
22 | exclude_file = []
23 | # This log file places in your tmp_dir.
24 | log = "./tmp/air.log"
25 | # It's not necessary to trigger build each time file changes if it's too frequent.
26 | delay = 500 # ms
27 | # Stop running old binary when build errors occur.
28 | stop_on_error = true
29 | # Send Interrupt signal before killing process (windows does not support this feature)
30 | send_interrupt = false
31 | # Delay after sending Interrupt signal
32 | kill_delay = 500 # ms
33 |
34 | [log]
35 | # Show log time
36 | time = false
37 |
38 | [color]
39 | # Customize each part's color. If no color found, use the raw app log.
40 | main = "magenta"
41 | watcher = "cyan"
42 | build = "yellow"
43 | runner = "green"
44 |
45 | [misc]
46 | # Delete tmp directory on exit
47 | clean_on_exit = true
--------------------------------------------------------------------------------
/air.sh:
--------------------------------------------------------------------------------
1 | go run github.com/cosmtrek/air -c "./air.conf"
--------------------------------------------------------------------------------
/api/README.md:
--------------------------------------------------------------------------------
1 | ## Drago API Client
2 |
3 | This directory contains the `api` package which aims at providing programmatic access to Drago's HTTP API.
4 |
5 | ### Documentation
6 |
7 | ...
8 |
9 | ### Usage
10 |
11 | ```go
12 | package main
13 |
14 | import "github.com/seashell/drago/api"
15 |
16 | func main() {
17 | // Get a new client
18 | client, err := api.NewClient(api.DefaultConfig())
19 | if err != nil {
20 | panic(err)
21 | }
22 |
23 | // Get a handle to the networks API
24 | networks := client.Networks()
25 |
26 | // Create a new network
27 | n := &api.Network{
28 | Name: "my-new-network",
29 | IPAddressRange: "10.1.1.0/24"
30 | }
31 |
32 | id, err := networks.Create(context.Background(), n)
33 | if err != nil {
34 | panic(err)
35 | }
36 |
37 | ...
38 | }
39 | ```
40 |
41 | To run this example, start a Drago server:
42 |
43 | ```
44 | drago agent --server
45 | ```
46 |
47 | Copy the code above into a file such as `main.go`, and run it.
48 |
49 | After running the code, you can also view the values in the Drago UI on your local machine at http://localhost:8080/ui/
50 |
--------------------------------------------------------------------------------
/api/acl.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "path"
5 |
6 | "github.com/seashell/drago/drago/structs"
7 | )
8 |
9 | const (
10 | aclPath = "/api/acl"
11 | )
12 |
13 | // ACL is a handle to the ACL API
14 | type ACL struct {
15 | client *Client
16 | }
17 |
18 | // Networks returns a handle on the networks endpoints.
19 | func (c *Client) ACL() *ACL {
20 | return &ACL{client: c}
21 | }
22 |
23 | // Boostrap :
24 | func (a *ACL) Bootstrap() (*structs.ACLToken, error) {
25 |
26 | var token structs.ACLToken
27 | err := a.client.createResource(path.Join(aclPath, "bootstrap"), nil, &token)
28 | if err != nil {
29 | return nil, err
30 | }
31 |
32 | return &token, nil
33 | }
34 |
--------------------------------------------------------------------------------
/api/acl_policy.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "path"
5 |
6 | "github.com/seashell/drago/drago/structs"
7 | )
8 |
9 | const (
10 | aclPoliciesPath = "/api/acl/policies"
11 | )
12 |
13 | // ACLPolicies is a handle to the ACL policies API
14 | type ACLPolicies struct {
15 | client *Client
16 | }
17 |
18 | // ACLPolicies returns a handle on the ACL policies endpoints.
19 | func (c *Client) ACLPolicies() *ACLPolicies {
20 | return &ACLPolicies{client: c}
21 | }
22 |
23 | // Create :
24 | func (p *ACLPolicies) Upsert(policy *structs.ACLPolicy) (*structs.ACLPolicy, error) {
25 |
26 | var rcvPolicy *structs.ACLPolicy
27 | err := p.client.createResource(aclPoliciesPath, policy, &rcvPolicy)
28 | if err != nil {
29 | return nil, err
30 | }
31 |
32 | return policy, nil
33 | }
34 |
35 | // Delete :
36 | func (p *ACLPolicies) Delete(name string) error {
37 |
38 | err := p.client.deleteResource(name, aclPoliciesPath, nil)
39 | if err != nil {
40 | return err
41 | }
42 |
43 | return nil
44 | }
45 |
46 | // Get :
47 | func (p *ACLPolicies) Get(name string) (*structs.ACLPolicy, error) {
48 |
49 | out := &structs.ACLPolicy{}
50 | err := p.client.getResource(aclPoliciesPath, name, out)
51 | if err != nil {
52 | return nil, err
53 | }
54 |
55 | return out, nil
56 | }
57 |
58 | // List :
59 | func (p *ACLPolicies) List() ([]*structs.ACLPolicyListStub, error) {
60 |
61 | var items []*structs.ACLPolicyListStub
62 | err := p.client.listResources(path.Join(aclPoliciesPath, "/"), nil, &items)
63 | if err != nil {
64 | return nil, err
65 | }
66 |
67 | return items, nil
68 | }
69 |
--------------------------------------------------------------------------------
/api/agent.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "path"
5 |
6 | "github.com/seashell/drago/drago/structs"
7 | )
8 |
9 | const (
10 | agentPath = "/api/agent"
11 | )
12 |
13 | // Agent is a handle to the agent API
14 | type Agent struct {
15 | client *Client
16 | }
17 |
18 | // Agent returns a handle on the agent endpoints.
19 | func (c *Client) Agent() *Agent {
20 | return &Agent{client: c}
21 | }
22 |
23 | // Self :
24 | func (t *Agent) Self() (*structs.Agent, error) {
25 |
26 | var agent *structs.Agent
27 | err := t.client.getResource(path.Join(agentPath, "self"), "", &agent)
28 | if err != nil {
29 | return nil, err
30 | }
31 |
32 | return agent, nil
33 | }
34 |
--------------------------------------------------------------------------------
/api/config.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "net/url"
5 | "time"
6 | )
7 |
8 | const (
9 | // DefaultAddress is the default Drago server address.
10 | DefaultAddress = "http://127.0.0.1:8080"
11 |
12 | // DefaultTimeout is the default request timeout.
13 | DefaultTimeout = 2 * time.Second
14 | )
15 |
16 | // Config contains configurations for Drago's API client.
17 | type Config struct {
18 | // URL of the Drago server (e.g. http://127.0.0.1:8080).
19 | Address string
20 |
21 | // Token to be used for authentication.
22 | Token string
23 |
24 | // Request timeout.
25 | Timeout time.Duration
26 | }
27 |
28 | // DefaultConfig returns a default configuration for Drago's API client.
29 | func DefaultConfig() *Config {
30 | config := &Config{
31 | Address: DefaultAddress,
32 | }
33 | return config
34 | }
35 |
36 | // Validate validates the configurations contained wihin the Config struct.
37 | func (c *Config) Validate() error {
38 | if _, err := url.Parse(c.Address); err != nil {
39 | return err
40 | }
41 | return nil
42 | }
43 |
44 | // Merge merges two API client configurations.
45 | func (c *Config) Merge(b *Config) *Config {
46 |
47 | if b == nil {
48 | return c
49 | }
50 |
51 | result := *c
52 |
53 | if b.Address != "" {
54 | result.Address = b.Address
55 | }
56 | if b.Token != "" {
57 | result.Token = b.Token
58 | }
59 | if b.Timeout != 0 {
60 | result.Timeout = b.Timeout
61 | }
62 |
63 | return &result
64 | }
65 |
--------------------------------------------------------------------------------
/api/error.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import "fmt"
4 |
5 | type CodedError struct {
6 | Message string
7 | Code int
8 | }
9 |
10 | func (e CodedError) Error() string {
11 | return fmt.Sprintf("%d (%s)", e.Code, e.Message)
12 | }
13 |
--------------------------------------------------------------------------------
/api/network.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "path"
5 |
6 | "github.com/seashell/drago/drago/structs"
7 | )
8 |
9 | const (
10 | networksPath = "/api/networks"
11 | )
12 |
13 | // Networks is a handle to the nodes API
14 | type Networks struct {
15 | client *Client
16 | }
17 |
18 | // Networks returns a handle on the networks endpoints.
19 | func (c *Client) Networks() *Networks {
20 | return &Networks{client: c}
21 | }
22 |
23 | // Create :
24 | func (n *Networks) Create(network *structs.Network) error {
25 |
26 | err := n.client.createResource(networksPath, network, nil)
27 | if err != nil {
28 | return err
29 | }
30 |
31 | return err
32 | }
33 |
34 | // Delete :
35 | func (n *Networks) Delete(id string) error {
36 |
37 | err := n.client.deleteResource(id, networksPath, nil)
38 | if err != nil {
39 | return err
40 | }
41 |
42 | return nil
43 | }
44 |
45 | // Get :
46 | func (n *Networks) Get(id string) (*structs.Network, error) {
47 |
48 | var network *structs.Network
49 | err := n.client.getResource(networksPath, id, &network)
50 | if err != nil {
51 | return nil, err
52 | }
53 |
54 | return network, nil
55 | }
56 |
57 | // List :
58 | func (n *Networks) List() ([]*structs.NetworkListStub, error) {
59 |
60 | var items []*structs.NetworkListStub
61 | err := n.client.listResources(path.Join(networksPath, "/"), nil, &items)
62 | if err != nil {
63 | return nil, err
64 | }
65 |
66 | return items, nil
67 | }
68 |
--------------------------------------------------------------------------------
/api/node.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "path"
5 |
6 | "github.com/seashell/drago/drago/structs"
7 | )
8 |
9 | const (
10 | nodesPath = "/api/nodes"
11 | )
12 |
13 | // Nodes is a handle to the nodes API
14 | type Nodes struct {
15 | client *Client
16 | }
17 |
18 | // Nodes returns a handle on the nodes endpoints.
19 | func (c *Client) Nodes() *Nodes {
20 | return &Nodes{client: c}
21 | }
22 |
23 | // Get :
24 | func (t *Nodes) Get(id string) (*structs.Node, error) {
25 |
26 | var node *structs.Node
27 | err := t.client.getResource(nodesPath, id, &node)
28 | if err != nil {
29 | return nil, err
30 | }
31 |
32 | return node, nil
33 | }
34 |
35 | // List :
36 | func (t *Nodes) List(filters map[string][]string) ([]*structs.NodeListStub, error) {
37 |
38 | var items []*structs.NodeListStub
39 | err := t.client.listResources(path.Join(nodesPath, "/"), filters, &items)
40 | if err != nil {
41 | return nil, err
42 | }
43 |
44 | return items, nil
45 | }
46 |
--------------------------------------------------------------------------------
/build/Dockerfile.builder:
--------------------------------------------------------------------------------
1 | FROM golang:1.16.2-stretch as drago-builder
2 |
3 | ARG HOST_UID=${HOST_UID}
4 | ARG HOST_USER=${HOST_USER}
5 |
6 | RUN curl -sS http://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
7 | echo "deb http://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
8 | curl -sL http://deb.nodesource.com/setup_15.x | bash - && \
9 | apt-get install -y nodejs && \
10 | apt-get update && \
11 | apt-get remove cmdtest && \
12 | apt-get install -y yarn
13 |
14 | RUN apt-get install -y gcc-arm-linux-gnueabihf libc6-dev-armhf-cross \
15 | gcc-aarch64-linux-gnu libc6-dev-arm64-cross
16 |
17 | RUN if [ "${HOST_USER}" != "root" ]; then \
18 | (adduser -q --gecos "" --home /home/${HOST_USER} --disabled-password -u ${HOST_UID} ${HOST_USER} \
19 | && chown -R "${HOST_UID}:${HOST_UID}" /home/${HOST_USER}); \
20 | fi
21 |
22 | USER ${HOST_USER}
--------------------------------------------------------------------------------
/build/Dockerfile.linux_amd64:
--------------------------------------------------------------------------------
1 | FROM alpine:3.13
2 |
3 | WORKDIR /home
4 |
5 | RUN apk add -U wireguard-tools
6 |
7 | COPY ./bin/linux_amd64/drago ./drago
8 |
9 | ENTRYPOINT [ "./drago" ]
--------------------------------------------------------------------------------
/client/nic/controller_test.go:
--------------------------------------------------------------------------------
1 | package nic
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 |
7 | structs "github.com/seashell/drago/drago/structs"
8 | )
9 |
10 | func TestCreateInterface(t *testing.T) {
11 | config := &Config{
12 | InterfacesPrefix: "drago",
13 | WireguardPath: "./wireguard",
14 | }
15 |
16 | c, err := NewController(config)
17 | if err != nil {
18 | t.Fatal(err)
19 | }
20 |
21 | key, err := c.GenerateKey()
22 | if err != nil {
23 | t.Error()
24 | }
25 |
26 | err = c.CreateInterfaceWithKey(&structs.Interface{
27 | Name: "1234567890abcd",
28 | Address: "192.168.2.1/24",
29 | Peers: []*structs.Peer{},
30 | }, key)
31 | if err != nil {
32 | t.Error(err)
33 | }
34 | }
35 |
36 | func TestListInterfaces(t *testing.T) {
37 |
38 | config := &Config{
39 | InterfacesPrefix: "drago",
40 | WireguardPath: "./wireguard",
41 | }
42 |
43 | c, err := NewController(config)
44 | if err != nil {
45 | t.Fatal(err)
46 | }
47 |
48 | ifaces, err := c.Interfaces()
49 | if err != nil {
50 | t.Fatal(err)
51 | }
52 |
53 | for _, i := range ifaces {
54 | fmt.Printf("%s (%s)", i.Name, i.Address)
55 | }
56 | }
57 |
58 | func TestDeleteAllInterfaces(t *testing.T) {
59 | return
60 | config := &Config{
61 | InterfacesPrefix: "drago",
62 | WireguardPath: "./wireguard",
63 | }
64 |
65 | c, err := NewController(config)
66 | if err != nil {
67 | t.Fatal(err)
68 | }
69 |
70 | err = c.DeleteAllInterfaces()
71 | if err != nil {
72 | t.Fatal(err)
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/client/nic/nic.go:
--------------------------------------------------------------------------------
1 | package nic
2 |
3 | import (
4 | structs "github.com/seashell/drago/drago/structs"
5 | )
6 |
7 | // NetworkInterfaceController provides network configuration capabilities.
8 | type NetworkInterfaceController interface {
9 | Interfaces() ([]*structs.Interface, error)
10 | CreateInterface(iface *structs.Interface) error
11 | UpdateInterface(iface *structs.Interface) error
12 | DeleteInterfaceByAlias(s string) error
13 | DeleteInterfaceByName(s string) error
14 | DeleteAllInterfaces() error
15 | }
16 |
17 | type PrivateKeyStore interface {
18 | KeyByID(id string) (*PrivateKey, error)
19 | UpsertKey(key *PrivateKey) error
20 | DeleteKey(id string) error
21 | }
22 |
23 | type PrivateKey struct {
24 | ID string
25 | Key string
26 | CreatedAt int64
27 | }
28 |
--------------------------------------------------------------------------------
/client/state/inmem/inmem.go:
--------------------------------------------------------------------------------
1 | package state
2 |
3 | import (
4 | "log"
5 | "sync"
6 |
7 | "github.com/seashell/drago/drago/structs"
8 | )
9 |
10 | type Repository struct {
11 | logger log.Logger
12 |
13 | // interface_id -> value
14 | interfaces map[string]*structs.Interface
15 |
16 | mu sync.RWMutex
17 | }
18 |
19 | func NewRepository(logger log.Logger) *Repository {
20 | return &Repository{
21 | interfaces: make(map[string]*structs.Interface),
22 | logger: logger,
23 | }
24 | }
25 |
26 | func (r *Repository) Name() string {
27 | return "inmem"
28 | }
29 |
30 | func (r *Repository) Interfaces() ([]*structs.Interface, error) {
31 | r.mu.RLock()
32 | defer r.mu.RUnlock()
33 |
34 | ifaces := make([]*structs.Interface, 0, len(r.interfaces))
35 | for _, v := range r.interfaces {
36 | ifaces = append(ifaces, v)
37 | }
38 |
39 | return ifaces, nil
40 | }
41 |
42 | func (r *Repository) UpsertInterface(iface *structs.Interface) error {
43 | r.mu.Lock()
44 | defer r.mu.Unlock()
45 | r.interfaces[iface.ID] = iface
46 | return nil
47 | }
48 |
49 | func (r *Repository) DeleteInterfaces(ids []string) error {
50 | r.mu.Lock()
51 | defer r.mu.Unlock()
52 |
53 | for _, id := range ids {
54 | delete(r.interfaces, id)
55 | }
56 |
57 | return nil
58 | }
59 |
--------------------------------------------------------------------------------
/client/state/state.go:
--------------------------------------------------------------------------------
1 | package state
2 |
3 | import (
4 | "github.com/seashell/drago/client/nic"
5 | "github.com/seashell/drago/drago/structs"
6 | )
7 |
8 | // Repository :
9 | type Repository interface {
10 | Name() string
11 |
12 | // Client state
13 | Interfaces() ([]*structs.Interface, error)
14 | UpsertInterface(*structs.Interface) error
15 | DeleteInterfaces(id []string) error
16 |
17 | // Key store
18 | KeyByID(id string) (*nic.PrivateKey, error)
19 | UpsertKey(key *nic.PrivateKey) error
20 | DeleteKey(id string) error
21 | }
22 |
--------------------------------------------------------------------------------
/client/util.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "io/ioutil"
5 | "os"
6 | "reflect"
7 |
8 | "github.com/seashell/drago/drago/structs"
9 | )
10 |
11 | type Diff struct {
12 | created []string
13 | deleted []string
14 | updated []string
15 | unchanged []string
16 | }
17 |
18 | // Read file contents if they exist, or persist and return a default value otherwise.
19 | func (c *Client) readFileLazy(path string, s string) (string, error) {
20 |
21 | var out string
22 |
23 | buf, err := ioutil.ReadFile(path)
24 | if err != nil && !os.IsNotExist(err) {
25 | return "", err
26 | }
27 |
28 | if len(buf) != 0 {
29 | out = string(buf)
30 | } else {
31 | out = s
32 | if err := ioutil.WriteFile(path, []byte(s), 0700); err != nil {
33 | return "", err
34 | }
35 | }
36 |
37 | return out, nil
38 | }
39 |
40 | func interfacesDiff(old, new map[string]*structs.Interface) Diff {
41 |
42 | diff := Diff{}
43 |
44 | for _, vold := range old {
45 | if _, ok := new[vold.ID]; !ok {
46 | diff.deleted = append(diff.deleted, vold.ID)
47 | }
48 | }
49 |
50 | for _, vnew := range new {
51 | if vold, ok := old[vnew.ID]; !ok {
52 | diff.created = append(diff.created, vnew.ID)
53 | } else {
54 | vnew = vold.Merge(vnew)
55 | if !reflect.DeepEqual(vold, vnew) {
56 | diff.updated = append(diff.updated, vnew.ID)
57 | } else {
58 | diff.unchanged = append(diff.unchanged, vold.ID)
59 | }
60 | }
61 | }
62 |
63 | return diff
64 | }
65 |
--------------------------------------------------------------------------------
/command/acl.go:
--------------------------------------------------------------------------------
1 | package command
2 |
3 | import (
4 | "context"
5 | "strings"
6 |
7 | cli "github.com/seashell/drago/pkg/cli"
8 | )
9 |
10 | // ACLCommand :
11 | type ACLCommand struct {
12 | UI cli.UI
13 | }
14 |
15 | // Name :
16 | func (c *ACLCommand) Name() string {
17 | return "acl"
18 | }
19 |
20 | // Synopsis :
21 | func (c *ACLCommand) Synopsis() string {
22 | return "Interact with ACL policies and tokens"
23 | }
24 |
25 | // Run :
26 | func (c *ACLCommand) Run(ctx context.Context, args []string) int {
27 | return cli.CommandReturnCodeHelp
28 | }
29 |
30 | // Help :
31 | func (c *ACLCommand) Help() string {
32 | h := `
33 | Usage: drago acl [options] [args]
34 |
35 | This command groups subcommands for interacting with ACL policies and tokens.
36 | It can be used to bootstrap Drago's ACL system, create policies that restrict
37 | access, and generate tokens from those policies.
38 |
39 | Bootstrap ACLs:
40 |
41 | $ drago acl bootstrap
42 |
43 | Please see the individual subcommand help for detailed usage information.
44 | `
45 | return strings.TrimSpace(h)
46 | }
47 |
--------------------------------------------------------------------------------
/command/acl_policy.go:
--------------------------------------------------------------------------------
1 | package command
2 |
3 | import (
4 | "context"
5 | "strings"
6 |
7 | cli "github.com/seashell/drago/pkg/cli"
8 | )
9 |
10 | // ACLPolicyCommand :
11 | type ACLPolicyCommand struct {
12 | UI cli.UI
13 | }
14 |
15 | // Name :
16 | func (c *ACLPolicyCommand) Name() string {
17 | return "acl policy"
18 | }
19 |
20 | // Synopsis :
21 | func (c *ACLPolicyCommand) Synopsis() string {
22 | return "Interact with ACL policies"
23 | }
24 |
25 | // Run :
26 | func (c *ACLPolicyCommand) Run(ctx context.Context, args []string) int {
27 | return cli.CommandReturnCodeHelp
28 | }
29 |
30 | // Help :
31 | func (c *ACLPolicyCommand) Help() string {
32 | h := `
33 | Usage: drago acl policy [options] [args]
34 |
35 | This command groups subcommands for interacting with ACL policies.
36 |
37 | Please see the individual subcommand help for detailed usage information.
38 | `
39 | return strings.TrimSpace(h)
40 | }
41 |
--------------------------------------------------------------------------------
/command/acl_token.go:
--------------------------------------------------------------------------------
1 | package command
2 |
3 | import (
4 | "context"
5 | "strings"
6 |
7 | cli "github.com/seashell/drago/pkg/cli"
8 | )
9 |
10 | // ACLTokenCommand :
11 | type ACLTokenCommand struct {
12 | UI cli.UI
13 | }
14 |
15 | // Name :
16 | func (c *ACLTokenCommand) Name() string {
17 | return "acl token"
18 | }
19 |
20 | // Synopsis :
21 | func (c *ACLTokenCommand) Synopsis() string {
22 | return "Interact with ACL tokens"
23 | }
24 |
25 | // Run :
26 | func (c *ACLTokenCommand) Run(ctx context.Context, args []string) int {
27 | return cli.CommandReturnCodeHelp
28 | }
29 |
30 | // Help :
31 | func (c *ACLTokenCommand) Help() string {
32 | h := `
33 | Usage: drago acl token [options] [args]
34 |
35 | This command groups subcommands for interacting with ACL tokens.
36 |
37 | Please see the individual subcommand help for detailed usage information.
38 | `
39 | return strings.TrimSpace(h)
40 | }
41 |
--------------------------------------------------------------------------------
/command/connection.go:
--------------------------------------------------------------------------------
1 | package command
2 |
3 | import (
4 | "context"
5 | "strings"
6 |
7 | cli "github.com/seashell/drago/pkg/cli"
8 | )
9 |
10 | // ConnectionCommand :
11 | type ConnectionCommand struct {
12 | UI cli.UI
13 | }
14 |
15 | // Name :
16 | func (c *ConnectionCommand) Name() string {
17 | return "connection"
18 | }
19 |
20 | // Synopsis :
21 | func (c *ConnectionCommand) Synopsis() string {
22 | return "Interact with connections"
23 | }
24 |
25 | // Run :
26 | func (c *ConnectionCommand) Run(ctx context.Context, args []string) int {
27 | return cli.CommandReturnCodeHelp
28 | }
29 |
30 | // Help :
31 | func (c *ConnectionCommand) Help() string {
32 | h := `
33 | Usage: drago connection [options] [args]
34 |
35 | This command groups subcommands for interacting with connections.
36 |
37 | Please see the individual subcommand help for detailed usage information.
38 |
39 | General Options:
40 | ` + GlobalOptions() + `
41 | `
42 | return strings.TrimSpace(h)
43 | }
44 |
--------------------------------------------------------------------------------
/command/interface.go:
--------------------------------------------------------------------------------
1 | package command
2 |
3 | import (
4 | "context"
5 | "strings"
6 |
7 | cli "github.com/seashell/drago/pkg/cli"
8 | )
9 |
10 | // InterfaceCommand :
11 | type InterfaceCommand struct {
12 | UI cli.UI
13 | }
14 |
15 | // Name :
16 | func (c *InterfaceCommand) Name() string {
17 | return "interface"
18 | }
19 |
20 | // Synopsis :
21 | func (c *InterfaceCommand) Synopsis() string {
22 | return "Interact with interfaces"
23 | }
24 |
25 | // Run :
26 | func (c *InterfaceCommand) Run(ctx context.Context, args []string) int {
27 | return cli.CommandReturnCodeHelp
28 | }
29 |
30 | // Help :
31 | func (c *InterfaceCommand) Help() string {
32 | h := `
33 | Usage: drago interface [options] [args]
34 |
35 | This command groups subcommands for interacting with interfaces.
36 |
37 | Please see the individual subcommand help for detailed usage information.
38 | `
39 | return strings.TrimSpace(h)
40 | }
41 |
--------------------------------------------------------------------------------
/command/network.go:
--------------------------------------------------------------------------------
1 | package command
2 |
3 | import (
4 | "context"
5 | "strings"
6 |
7 | cli "github.com/seashell/drago/pkg/cli"
8 | )
9 |
10 | // NetworkCommand :
11 | type NetworkCommand struct {
12 | UI cli.UI
13 | }
14 |
15 | // Name :
16 | func (c *NetworkCommand) Name() string {
17 | return "network"
18 | }
19 |
20 | // Synopsis :
21 | func (c *NetworkCommand) Synopsis() string {
22 | return "Interact with networks"
23 | }
24 |
25 | // Run :
26 | func (c *NetworkCommand) Run(ctx context.Context, args []string) int {
27 | return cli.CommandReturnCodeHelp
28 | }
29 |
30 | // Help :
31 | func (c *NetworkCommand) Help() string {
32 | h := `
33 | Usage: drago network [options] [args]
34 |
35 | This command groups subcommands for interacting with networks.
36 |
37 | Please see the individual subcommand help for detailed usage information.
38 | `
39 | return strings.TrimSpace(h)
40 | }
41 |
--------------------------------------------------------------------------------
/command/node.go:
--------------------------------------------------------------------------------
1 | package command
2 |
3 | import (
4 | "context"
5 | "strings"
6 |
7 | cli "github.com/seashell/drago/pkg/cli"
8 | )
9 |
10 | // NodeCommand :
11 | type NodeCommand struct {
12 | UI cli.UI
13 | }
14 |
15 | // Name :
16 | func (c *NodeCommand) Name() string {
17 | return "node"
18 | }
19 |
20 | // Synopsis :
21 | func (c *NodeCommand) Synopsis() string {
22 | return "Interact with nodes"
23 | }
24 |
25 | // Run :
26 | func (c *NodeCommand) Run(ctx context.Context, args []string) int {
27 | return cli.CommandReturnCodeHelp
28 | }
29 |
30 | // Help :
31 | func (c *NodeCommand) Help() string {
32 | h := `
33 | Usage: drago node [options] [args]
34 |
35 | This command groups subcommands for interacting with nodes.
36 |
37 | Please see the individual subcommand help for detailed usage information.
38 | `
39 | return strings.TrimSpace(h)
40 | }
41 |
--------------------------------------------------------------------------------
/command/ui.go:
--------------------------------------------------------------------------------
1 | package command
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "os/exec"
7 | "runtime"
8 | "strings"
9 |
10 | cli "github.com/seashell/drago/pkg/cli"
11 | )
12 |
13 | // UICommand :
14 | type UICommand struct {
15 | UI cli.UI
16 |
17 | Command
18 | }
19 |
20 | // Name :
21 | func (c *UICommand) Name() string {
22 | return "acl"
23 | }
24 |
25 | // Synopsis :
26 | func (c *UICommand) Synopsis() string {
27 | return "Open the Drago web UI"
28 | }
29 |
30 | // Run :
31 | func (c *UICommand) Run(ctx context.Context, args []string) int {
32 |
33 | url := "http://127.0.0.1:8080"
34 | if c.address != "" {
35 | url = c.address
36 | }
37 |
38 | c.UI.Output(fmt.Sprintf(`Opening URL "%s"`, url))
39 |
40 | if err := openBrowser(url); err != nil {
41 | c.UI.Error(err.Error())
42 | }
43 |
44 | return 0
45 | }
46 |
47 | // Help :
48 | func (c *UICommand) Help() string {
49 | h := `
50 | Usage: drago ui [options] [args]
51 |
52 | Open the Drago web UI in the default browser.
53 |
54 | General Options:
55 | ` + GlobalOptions() + `
56 | `
57 | return strings.TrimSpace(h)
58 | }
59 |
60 | // Credits: https://gist.github.com/hyg/9c4afcd91fe24316cbf0
61 | func openBrowser(url string) error {
62 |
63 | var err error
64 |
65 | switch runtime.GOOS {
66 | case "linux":
67 | err = exec.Command("xdg-open", url).Start()
68 | case "windows":
69 | err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
70 | case "darwin":
71 | err = exec.Command("open", url).Start()
72 | default:
73 | err = fmt.Errorf("unsupported platform")
74 | }
75 | if err != nil {
76 | return err
77 | }
78 |
79 | return nil
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/command/util.go:
--------------------------------------------------------------------------------
1 | package command
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 |
7 | api "github.com/seashell/drago/api"
8 | )
9 |
10 | // Returns the node ID of the local agent, in case it is a client.
11 | // Otherwise, returns an error.
12 | func localAgentNodeID(api *api.Client) (string, error) {
13 |
14 | self, err := api.Agent().Self()
15 | if err != nil {
16 | return "", fmt.Errorf("could not retrieve agent info: %s", err)
17 | }
18 |
19 | clientStats, ok := self.Stats["client"]
20 | if !ok {
21 | return "", fmt.Errorf("not running in client mode")
22 | }
23 |
24 | nodeID, ok := clientStats["node_id"]
25 | if !ok {
26 | return "", fmt.Errorf("could not determine node id")
27 | }
28 |
29 | return nodeID, nil
30 | }
31 |
32 | func valueOrPlaceholder(s *string, p string) string {
33 | if s != nil {
34 | return *s
35 | }
36 | return p
37 | }
38 |
39 | // TODO: improve how we clean JSON strings
40 | func cleanJSONString(s string) string {
41 |
42 | s = strings.TrimSpace(s)
43 | s = strings.ReplaceAll(s, `""`, "N/A")
44 | s = strings.ReplaceAll(s, `{}`, "\n N/A")
45 |
46 | s = strings.ReplaceAll(s, " ", " ")
47 | s = strings.ReplaceAll(s, `,`, "")
48 |
49 | s = strings.ReplaceAll(s, "{", "")
50 | s = strings.ReplaceAll(s, "}", "")
51 | s = strings.ReplaceAll(s, `"`, "")
52 |
53 | s = strings.TrimLeftFunc(s, func(r rune) bool {
54 | return r == '\n'
55 | })
56 |
57 | s = strings.TrimRightFunc(s, func(r rune) bool {
58 | return r == '\n' || r == ' '
59 | })
60 |
61 | return s
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/command/version.go:
--------------------------------------------------------------------------------
1 | package command
2 |
3 | import (
4 | "context"
5 | "strings"
6 |
7 | cli "github.com/seashell/drago/pkg/cli"
8 | version "github.com/seashell/drago/version"
9 | )
10 |
11 | // VersionCommand :
12 | type VersionCommand struct {
13 | UI cli.UI
14 | }
15 |
16 | // Name :
17 | func (c *VersionCommand) Name() string {
18 | return "version"
19 | }
20 |
21 | // Synopsis :
22 | func (c *VersionCommand) Synopsis() string {
23 | return "Print the Drago version"
24 | }
25 |
26 | // Run :
27 | func (c *VersionCommand) Run(ctx context.Context, args []string) int {
28 | c.UI.Output(version.GetVersion().VersionNumber())
29 | return 0
30 | }
31 |
32 | // Help :
33 | func (c *VersionCommand) Help() string {
34 | h := `
35 | Usage: drago version [options]
36 |
37 | Version prints out the Drago version.
38 |
39 | General Options:
40 | ` + GlobalOptions() + `
41 | `
42 | return strings.TrimSpace(h)
43 | }
44 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/docs/.nojekyll
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Drago Documentation Website
2 |
3 | This subdirectory contains the source for the Drago website.
4 |
5 | ### Overview
6 | We use [Docsify](https://docsify.js.org/) for generating the website on the fly based on markdown files.
7 |
8 | ### Contributing
9 | In order to contribute to the Drago docs, simply fork the repo, edit the markdown in this directory, and submit a pull request. Also, feel to add any new assets such as diagrams and screenshots.
--------------------------------------------------------------------------------
/docs/_404.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
404 - Page not found
5 |
For more information, please contact us.
6 |
7 |
8 |
--------------------------------------------------------------------------------
/docs/_coverpage.md:
--------------------------------------------------------------------------------
1 |
2 |

4 |
5 |
6 | drago
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | > A flexible configuration manager for WireGuard networks.
18 |
19 | - Single-binary, lightweight
20 | - Encrypted node-to-node communication
21 | - Support for different WireGuard implementations
22 | - Slick management dashboard
23 | - Extensible via REST API
24 |
25 | [GitHub](https://github.com/seashell/drago/)
26 | [Get Started](/docs/)
27 |
28 |
29 |
--------------------------------------------------------------------------------
/docs/_navbar.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/docs/_navbar.md
--------------------------------------------------------------------------------
/docs/api/acl-policies.md:
--------------------------------------------------------------------------------
1 | # ACL Policies HTTP API
2 |
--------------------------------------------------------------------------------
/docs/api/acl-tokens.md:
--------------------------------------------------------------------------------
1 | # ACL Tokens HTTP API
2 |
--------------------------------------------------------------------------------
/docs/api/connections.md:
--------------------------------------------------------------------------------
1 | # Connections HTTP API
2 |
--------------------------------------------------------------------------------
/docs/api/interfaces.md:
--------------------------------------------------------------------------------
1 | # Interfaces HTTP API
2 |
--------------------------------------------------------------------------------
/docs/api/networks.md:
--------------------------------------------------------------------------------
1 | # Networks HTTP API
2 |
3 | The `/networks/` endpoints are used to manage Networks.
4 |
5 | ## List Networks
6 |
7 | This endpoint lists all Networks.
8 |
9 | | **Method** | **Path** | **Produces** |
10 | |------------|-----------------|--------------------|
11 | | `GET` | `/networks/` | `application/json` |
12 |
13 |
14 | | **ACL Required** |
15 | |-------------------------------------|
16 | | `management` for all policies. |
17 |
18 |
19 | ### Parameters
20 |
21 | ### Sample Request
22 |
23 | ### Sample Response
--------------------------------------------------------------------------------
/docs/api/nodes.md:
--------------------------------------------------------------------------------
1 | # Nodes HTTP API
2 |
--------------------------------------------------------------------------------
/docs/api/status.md:
--------------------------------------------------------------------------------
1 | # Status HTTP API
--------------------------------------------------------------------------------
/docs/api/ui.md:
--------------------------------------------------------------------------------
1 | # UI HTTP API
--------------------------------------------------------------------------------
/docs/docs/README.md:
--------------------------------------------------------------------------------
1 | # Drago Documentation
2 |
3 | Welcome to the Drago documentation. This page is meant to be a reference for all available features and options in Drago.
4 |
5 | > [!NOTE]
6 | > Drago is in active development, and its documentation is still a work-in-progress. We, therefore, apologize beforehand if information in this page is unclear or outdated. If you have any questions about a specific topic, or suggestions related to the project, please drop us a message on our [Gitter channel](https://gitter.im/seashell/drago) or create a [GitHub issue](https://github.com/seashell/drago/issues).
7 | >
8 |
--------------------------------------------------------------------------------
/docs/docs/commands/README.md:
--------------------------------------------------------------------------------
1 | # Drago Commands (CLI)
2 |
3 |
4 | Drago is controlled via a very easy to use command-line interface (CLI). Drago is only a single command-line application: `drago`, which takes subcommands such as `agent` or `status`. The complete list of subcommands is in the navigation to the left.
5 |
6 | The Drago CLI is a well-behaved command line application. In erroneous cases, a non-zero exit status will be returned. It also responds to `-h` and `--help` as you would most likely expect.
7 |
8 | To view a list of the available commands at any time, just run Drago with no arguments. To get help for any specific subcommand, run the subcommand with the `-h` argument.
9 |
10 | Each command has been conveniently documented on this website. Links to each command can be found on the left.
11 |
12 | ### Remote usage
13 |
14 | The Drago CLI can be used to interact with a remote Drago agent.
15 |
16 | To do so, set the `DRAGO_ADDR` environment variable or use the `--address=` flag when running commands.
17 |
18 | ```
19 | $ DRAGO_ADDR=https://:8080 drago agent-info
20 | $ drago agent-info --address=https://remote-address:4646
21 | ```
22 |
23 | The provided address must be reachable from your local machine.
24 |
--------------------------------------------------------------------------------
/docs/docs/commands/acl/README.md:
--------------------------------------------------------------------------------
1 | # Command: acl
2 |
3 | The `acl` command is used to interact with ACL policies and tokens.
4 |
5 | ## Usage
6 |
7 | Usage: `drago acl [options]`
8 |
9 | Run `drago acl [options] -h` for help on a specific subcommand.
10 |
11 | Available subcommands:
12 |
13 | - [`acl bootstrap`](/docs/commands/acl/bootstrap): Bootstrap the ACL system
14 | - [`acl policy apply`](/docs/commands/acl/policy-apply): Create or update an ACL policy
15 | - [`acl policy delete`](/docs/commands/acl/policy-delete): Delete an existing ACL policy
16 | - [`acl policy info`](/docs/commands/acl/policy-info): Display info on an existing ACL policy
17 | - [`acl policy list`](/docs/commands/acl/policy-list): List available ACL policies
18 | - [`acl token create`](/docs/commands/acl/token-create): Create a new ACL token
19 | - [`acl token delete`](/docs/commands/acl/token-delete): Delete an existing ACL token
20 | - [`acl token info`](/docs/commands/acl/token-info): Display info on an existing ACL token
21 | - [`acl token list`](/docs/commands/acl/token-list): List available ACL tokens
22 | - [`acl token self`](/docs/commands/acl/token-self): Display info on self ACL token
23 | - [`acl token update`](/docs/commands/acl/token-update): Update an existing ACL token
24 |
--------------------------------------------------------------------------------
/docs/docs/commands/acl/bootstrap.md:
--------------------------------------------------------------------------------
1 | # Command: acl bootstrap
2 |
3 | The `acl bootstrap` command can be used to bootstrap the initial ACL token.
4 |
5 | ## General Options
6 |
7 | - `--address=`
8 | The address of the Drago server.
9 | Overrides the `DRAGO_ADDR` environment variable if set.
10 | Defaults to `http://127.0.0.1:8080`.
11 |
12 | - `--token=`
13 | The token used to authenticate with the Drago server.
14 | Overrides the `DRAGO_TOKEN` environment variable if set.
15 | Defaults to `""`.
16 |
--------------------------------------------------------------------------------
/docs/docs/commands/acl/policy-apply.md:
--------------------------------------------------------------------------------
1 | # Command: acl policy apply
2 |
3 | The `acl policy apply` command is used to create or update ACL policies.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago acl policy apply [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
23 | ## Apply Options
24 |
25 | - `--description=`: Sets the description of the ACL policy.
26 |
--------------------------------------------------------------------------------
/docs/docs/commands/acl/policy-delete.md:
--------------------------------------------------------------------------------
1 | # Command: acl policy delete
2 |
3 | The `acl policy delete` command is used to delete an existing ACL policy.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago acl policy delete [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
--------------------------------------------------------------------------------
/docs/docs/commands/acl/policy-info.md:
--------------------------------------------------------------------------------
1 | # Command: acl policy info
2 |
3 | The `acl policy info` command is used to display detailed information about an existing ACL policy.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago acl policy info [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
23 | ## List Options
24 |
25 | - `--json`: Enable JSON output.
26 |
--------------------------------------------------------------------------------
/docs/docs/commands/acl/policy-list.md:
--------------------------------------------------------------------------------
1 | # Command: acl policy list
2 |
3 | The `acl policy list` command is used to list existing ACL policies.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago acl policy list [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
23 | ## List Options
24 |
25 | - `--json`: Enable JSON output.
26 |
--------------------------------------------------------------------------------
/docs/docs/commands/acl/token-create.md:
--------------------------------------------------------------------------------
1 | # Command: acl token create
2 |
3 | The `acl token create` command is used to issue new ACL tokens.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago acl token create [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
23 | ## Create Options
24 |
25 | - `--name=`: Sets the name of the ACL token.
26 |
27 | - `--type=`: Sets the type of the ACL token. Must be either "client" or "management". If not provided, defaults to "client".
28 |
29 | - `--policy=`: Specifies policies to associate with a client token. Can be specified multiple times.
30 |
31 | - `--json`: Enable JSON output.
32 |
--------------------------------------------------------------------------------
/docs/docs/commands/acl/token-delete.md:
--------------------------------------------------------------------------------
1 | # Command: acl token delete
2 |
3 | The `acl token delete` command is used to delete an existing ACL token.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago acl token delete
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
--------------------------------------------------------------------------------
/docs/docs/commands/acl/token-info.md:
--------------------------------------------------------------------------------
1 | # Command: acl token info
2 |
3 | The `acl token info` command is used to display detailed information on an existing ACL token.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago acl token info [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
23 | ## Info Options
24 |
25 | - `--json`: Enable JSON output.
26 |
--------------------------------------------------------------------------------
/docs/docs/commands/acl/token-list.md:
--------------------------------------------------------------------------------
1 | # Command: acl token list
2 |
3 | The `acl token list` command is used to list all available ACL tokens.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago acl token list [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
23 | ## Info Options
24 |
25 | - `--json`: Enable JSON output.
26 |
--------------------------------------------------------------------------------
/docs/docs/commands/acl/token-self.md:
--------------------------------------------------------------------------------
1 | # Command: acl token self
2 |
3 | The `acl token self` command is used to display detailed information on the currently set ACL token.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago acl token self [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
23 | ## Info Options
24 |
25 | - `--json`: Enable JSON output.
26 |
--------------------------------------------------------------------------------
/docs/docs/commands/acl/token-update.md:
--------------------------------------------------------------------------------
1 | # Command: acl token update
2 |
3 | The `acl token update` command is used to update an existing ACL token.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago acl token update [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
23 | ## Update Options
24 |
25 | - `--name=`: Sets the name of the ACL token.
26 |
27 | - `--type=`: Sets the type of the ACL token. Must be either "client" or "management". If not provided, defaults to "client".
28 |
29 | - `--policy=`: Specifies policies to associate with a client token. Can be specified multiple times.
30 |
31 | - `--json`: Enable JSON output.
32 |
--------------------------------------------------------------------------------
/docs/docs/commands/agent-info.md:
--------------------------------------------------------------------------------
1 | # Command: agent-info
2 |
3 | The `agent-info` command is used to display configurations and status from the agent to which the CLI is connected.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago agent-info [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
23 | ## Agent Info Options
24 |
25 | - `--json`: Enable JSON output.
26 |
--------------------------------------------------------------------------------
/docs/docs/commands/agent.md:
--------------------------------------------------------------------------------
1 | # Command: agent
2 |
3 | The `agent` command is likely Drago's most important command. It is used to start client and/or server agent.
4 |
5 | ## Command-line Options
6 |
7 | The `agent` command accepts the following arguments (note that some of them can be overriden by CLI flags):
8 |
9 | - `--client`: Enable client mode on the local agent.
10 |
11 | - `--config=`: Indicate the path to a configuration file containing configurations to be used by the Drago agent. Can be speficied multiple times.
12 |
13 | - `--data-dir=`:
14 |
15 | - `--dev`: Enable development mode on the local agent. This means the agent will execute both as a client and as a server, with sane configurations that are ideal for development.
16 |
17 | - `--node=`:
18 |
19 | - `--server`: Enable server mode on the local agent.
20 |
21 | - `--servers=`:
22 |
23 | - `--wireguard-path`: Path to a userspace WireGuard implementation. Both `wireguard-go` and `cloudflare/boringtun` are supported. If not provided, the WireGuard kernel module will be used.
24 |
25 | ## Example
26 |
27 | ```
28 | $ drago agent --server
29 | ```
30 |
--------------------------------------------------------------------------------
/docs/docs/commands/connection/create.md:
--------------------------------------------------------------------------------
1 | # Command: connection create
2 |
3 | The `connection create` command is used to create a connection.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago connection create [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
23 | ## Info Options
24 |
25 | - `--json`: Enable JSON output.
26 |
27 | - `--allow-all`: Enables routing of all traffic in this connection.
28 |
29 | - `--keepalive=`: Time interval between persistent keepalive packets. Defaults to 0, which disables the feature.
30 |
--------------------------------------------------------------------------------
/docs/docs/commands/connection/list.md:
--------------------------------------------------------------------------------
1 | # Command: connection list
2 |
3 | The `connection list` command is used to list all available connections.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago connection list [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
23 | ## Info Options
24 |
25 | - `--json`: Enable JSON output.
26 |
--------------------------------------------------------------------------------
/docs/docs/commands/connection/update.md:
--------------------------------------------------------------------------------
1 | # Command: connection update
2 |
3 | The `connection update` command is used to update an existing connection.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago connection update [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
--------------------------------------------------------------------------------
/docs/docs/commands/interface/list.md:
--------------------------------------------------------------------------------
1 | # Command: interface list
2 |
3 | The `interface list` command is used to list all available interfaces.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago interface list [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
23 | ## Info Options
24 |
25 | - `--json`: Enable JSON output.
26 |
27 | - `--self`: Filter results by the local node ID. Can not be used with the --node filter flag.
28 |
29 | - `--node=`: Filter results by node ID. Can not be used with the --self filter flag.
30 |
31 | - `--network=`: Filter results by network.
32 |
--------------------------------------------------------------------------------
/docs/docs/commands/interface/update.md:
--------------------------------------------------------------------------------
1 | # Command: interface update
2 |
3 | The `interface update` command is used to update an existing interface.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago interface update [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
23 | ## Update Options
24 |
25 | - `--address`: Interface IP address in CIDR notation
26 |
--------------------------------------------------------------------------------
/docs/docs/commands/network/create.md:
--------------------------------------------------------------------------------
1 | # Command: network create
2 |
3 | The `network create` command is used to create a new network.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago network create [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
23 | ## Create Options
24 |
25 | - `--range=`: Network IP address range in CIDR notation.
26 |
--------------------------------------------------------------------------------
/docs/docs/commands/network/delete.md:
--------------------------------------------------------------------------------
1 | # Command: network delete
2 |
3 | The `network delete` command is used to delete an existing network.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago network delete [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
--------------------------------------------------------------------------------
/docs/docs/commands/network/info.md:
--------------------------------------------------------------------------------
1 | # Command: network info
2 |
3 | The `network info` command is used to display detailed information about an existing network.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago network info [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
23 | ## Info Options
24 |
25 | - `--json`: Enable JSON output.
26 |
--------------------------------------------------------------------------------
/docs/docs/commands/network/list.md:
--------------------------------------------------------------------------------
1 | # Command: network list
2 |
3 | The `network list` command is used to list all available networks.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago network list [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
23 | ## List Options
24 |
25 | - `--json`: Enable JSON output.
26 |
--------------------------------------------------------------------------------
/docs/docs/commands/node/info.md:
--------------------------------------------------------------------------------
1 | # Command: node info
2 |
3 | The `node info` command is used to display detailed information about an existing node.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago node info [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
23 | ## Info Options
24 |
25 | - `--self`: Query the status of the local node.
26 |
27 | - `--json`: Enable JSON output.
28 |
--------------------------------------------------------------------------------
/docs/docs/commands/node/join.md:
--------------------------------------------------------------------------------
1 | # Command: node join
2 |
3 | The `node join` command is used to have the local client node join an existing network.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago node join [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
--------------------------------------------------------------------------------
/docs/docs/commands/node/leave.md:
--------------------------------------------------------------------------------
1 | # Command: node leave
2 |
3 | The `node leave` command is used to have the local client node leave a specific network.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago node leave [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
--------------------------------------------------------------------------------
/docs/docs/commands/node/list.md:
--------------------------------------------------------------------------------
1 | # Command: node list
2 |
3 | The `node list` command is used to list all available nodes.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago node list [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
23 | ## Info Options
24 |
25 | - `--json`: Enable JSON output.
26 |
27 | - `--meta=`: Filter nodes by metadata.
28 |
29 | - `--status=`: Filter nodes by status.
30 |
--------------------------------------------------------------------------------
/docs/docs/commands/node/status.md:
--------------------------------------------------------------------------------
1 | # Command: node status
2 |
3 | The `node status` command is used to list the status of one or more registered client nodes.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago node status [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address=`
14 | The address of the Drago server.
15 | Overrides the `DRAGO_ADDR` environment variable if set.
16 | Defaults to `http://127.0.0.1:8080`.
17 |
18 | - `--token=`
19 | The token used to authenticate with the Drago server.
20 | Overrides the `DRAGO_TOKEN` environment variable if set.
21 | Defaults to `""`.
22 |
23 | ## Info Options
24 |
25 | - `--self`: Query the status of the local node.
26 |
27 | - `--json`: Enable JSON output.
28 |
--------------------------------------------------------------------------------
/docs/docs/commands/ui.md:
--------------------------------------------------------------------------------
1 | # Command: ui
2 |
3 | The `ui` command is used to open Drago's Web UI.
4 |
5 | ## Usage
6 |
7 | ```
8 | drago ui [options]
9 | ```
10 |
11 | ## General Options
12 |
13 | - `--address`:
14 | - `--token`:
15 |
--------------------------------------------------------------------------------
/docs/docs/configuration/README.md:
--------------------------------------------------------------------------------
1 | # Drago Configuration
2 |
3 | Drago agents can be configured via HCL configuration files or command-line flags.
4 |
--------------------------------------------------------------------------------
/docs/docs/configuration/acl.md:
--------------------------------------------------------------------------------
1 | # `acl` Block
2 |
3 | The `acl`block is used to configure the Drago ACL system.
4 |
5 | ## `acl` Parameters
6 |
7 | - `enabled` `(bool: false)` - Defines whether if ACL is enabled or not. All other configurations in this section are only applied if `enabled` is set to `true`.
8 |
--------------------------------------------------------------------------------
/docs/docs/configuration/client.md:
--------------------------------------------------------------------------------
1 | # `client` Block
2 |
3 | The `client`block is used to configure the Drago agent when operating in client mode.
4 |
5 | ## `client` Parameters
6 |
7 | - `enabled` `(bool: false)` - Specify if the agent will run in client mode.
8 |
--------------------------------------------------------------------------------
/docs/docs/configuration/server.md:
--------------------------------------------------------------------------------
1 | # `server` Block
2 |
3 | The `server`block is used to configure the Drago agent when operating in server mode.
4 |
5 | ## `server` Parameters
6 |
7 | - `enabled` `(bool: false)` - Specify if the agent will run in server mode.
8 |
--------------------------------------------------------------------------------
/docs/docs/installing/README.md:
--------------------------------------------------------------------------------
1 | # Installing Drago
2 |
3 | We are working to make Drago available as a pre-compiled binary or as a package for several operating systems.
4 |
5 | Meanwhile, you can build Drago from source.
6 |
7 | ## Compiling from Source
8 |
9 | To compile from source, you will need Go installed and configured properly.
10 |
11 | 1. Clone the Drago repository:
12 | ```bash
13 | $ git clone https://github.com/seashell/drago.git
14 | $ cd drago
15 | ```
16 | 2. Download all necessary Go modules into the module cache:
17 |
18 | ```bash
19 | $ go mod download
20 | ```
21 | 3. Build Drago for your current system.
22 |
23 | ```bash
24 | $ go build -o ./build/drago
25 | ```
26 |
27 | ## Verify the binary
28 |
29 | To verify Drago was built correctly, run the binary:
30 |
31 | ```bash
32 | $ ./build/drago
33 | ```
34 |
--------------------------------------------------------------------------------
/docs/docs/installing/quickstart.md:
--------------------------------------------------------------------------------
1 | # Quickstart
2 |
3 | This page describes methods for installing Drago in different environments.
4 |
5 | > [!WARNING]
6 | > This section is still a work-in-progress. If you think you can contribute, please see our [contribution guidelines](docs/../../../contributing.md).
7 |
--------------------------------------------------------------------------------
/docs/docs/internals/README.md:
--------------------------------------------------------------------------------
1 | # Drago Internals
2 |
3 | This section covers the internals of Drago and explains the technical details of how Drago functions, its architecture, and sub-systems.
4 |
5 | > [!NOTE]
6 | > Knowledge of Drago internals is not required to use Drago. If you aren't interested in the internals of Drago, you may safely skip this section. If you are operating Drago, we recommend understanding the internals.
7 |
--------------------------------------------------------------------------------
/docs/docs/overview.md:
--------------------------------------------------------------------------------
1 | # Overview
2 |
3 | > [!WARNING]
4 | > This section is still a work-in-progress. If you think you can contribute, please see our [contribution guidelines](docs/../../../contributing.md).
5 |
6 |
7 | Drago is a flexible configuration manager for WireGuard networks that is designed to make it simple to configure network overlays spanning heterogeneous nodes distributed across different clouds and physical locations.
8 |
9 | Drago is meant to be simple and provide a solid foundation for higher-level functionality. Need automatic IP assignment, dynamic firewall rules, or some kind of telemetry? You are free to implement it on top of the already existing APIs.
10 |
11 | ## Use-cases
12 |
13 | - Secure home automation, SSH access, etc
14 | - Establish secure VPNs for your company
15 | - Manage access to sensitive services deployed to private hosts
16 | - Expose development servers for debugging and demonstration purposes
17 | - Establish multi-cloud clusters with ease
18 | - Build your own cloud with RaspberryPIs
19 |
20 | ## Main features
21 |
22 | - Single-binary, lightweight
23 | - Encrypted node-to-node communication
24 | - Support for different WireGuard implementations
25 | - Slick management dashboard
26 | - Extensible via REST API
--------------------------------------------------------------------------------
/docs/faq.md:
--------------------------------------------------------------------------------
1 | # FAQ
2 |
3 | > [!WARNING]
4 | > This section is still a work-in-progress. If you think you can contribute, please see our [contribution guidelines](docs/../../../contributing.md).
5 |
6 | ## Why Drago?
7 |
8 | WireGuard® is an extremely simple yet fast and modern VPN that utilizes state-of-the-art cryptography. It aims to be faster, simpler, leaner, and more useful than IPsec, while avoiding the massive headache. It intends to be considerably more performant than OpenVPN. WireGuard is designed as a general purpose VPN for running on embedded interfaces and super computers alike, fit for many different circumstances. Initially released for the Linux kernel, it is now cross-platform and widely deployable, being regarded as the most secure, easiest to use, and simplest VPN solution in the industry.
9 |
10 | WireGuard presents several advantages over other VPN solutions, but it does not allow for the dynamic configuration of network parameters such as IP addresses and firewall rules. Drago builds on top of WireGuard, allowing users to dynamically manage the configuration of their VPN networks, providing a unified control plane for overlays spanning from containers to virtual machines to IoT devices.
--------------------------------------------------------------------------------
/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/docs/favicon.ico
--------------------------------------------------------------------------------
/docs/license.md:
--------------------------------------------------------------------------------
1 | # License
2 |
3 | Drago is released under the [Apache 2.0 license](https://github.com/seashell/drago/blob/master/LICENSE).
4 |
5 |
--------------------------------------------------------------------------------
/drago/auth/auth.go:
--------------------------------------------------------------------------------
1 | package auth
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/seashell/drago/pkg/acl"
7 | )
8 |
9 | // AuthorizationHandler abstracts a handler capable of
10 | // authorizing subjects to perform specific operations on
11 | // resources at a given path
12 | type AuthorizationHandler interface {
13 | Authorize(ctx context.Context, sub, res, path, op string) error
14 | }
15 |
16 | // authorizationHandler implements the AuthorizationHandler
17 | // interface, thus being capable of authorizing operations.
18 | type authorizationHandler struct {
19 | resolver *acl.Resolver
20 | }
21 |
22 | // NewAuthorizationHandler returns a new AuthorizationHandler
23 | func NewAuthorizationHandler(
24 | model *acl.Model,
25 | secretResolver acl.SecretResolverFunc,
26 | policyResolver acl.PolicyResolverFunc) AuthorizationHandler {
27 |
28 | aclResolver, _ := acl.NewResolver(&acl.ResolverConfig{
29 | Model: model,
30 | SecretResolver: secretResolver,
31 | PolicyResolver: policyResolver,
32 | })
33 |
34 | return &authorizationHandler{
35 | resolver: aclResolver,
36 | }
37 | }
38 |
39 | // Authorize checks whether or not the specified operation is authorized or
40 | // not on the targeted resource and path, potentially returning an error.
41 | func (h *authorizationHandler) Authorize(ctx context.Context, sub, res, path, op string) error {
42 |
43 | acl, err := h.resolver.ResolveSecret(ctx, sub)
44 | if err != nil {
45 | return err
46 | }
47 |
48 | return acl.CheckAuthorized(ctx, res, path, op)
49 | }
50 |
--------------------------------------------------------------------------------
/drago/auth/policy.go:
--------------------------------------------------------------------------------
1 | package auth
2 |
3 | import "github.com/seashell/drago/pkg/acl"
4 |
5 | // Policy implements the acl.Policy interface.
6 | type Policy struct {
7 | name string
8 | rules []acl.Rule
9 | }
10 |
11 | // NewPolicy :
12 | func NewPolicy(name string, rules []acl.Rule) *Policy {
13 | return &Policy{name, rules}
14 | }
15 |
16 | // Name returns the name of the policy.
17 | func (p *Policy) Name() string {
18 | return p.name
19 | }
20 |
21 | // Rules return the policy rules.
22 | func (p *Policy) Rules() []acl.Rule {
23 | return p.rules
24 | }
25 |
26 | // AddRule appends an acl.Rule to the policy rules, returning the resulting slice.
27 | func (p *Policy) AddRule(r acl.Rule) []acl.Rule {
28 | p.rules = append(p.rules, r)
29 | return p.rules
30 | }
31 |
--------------------------------------------------------------------------------
/drago/auth/rule.go:
--------------------------------------------------------------------------------
1 | package auth
2 |
3 | // Rule implements the acl.Rule interface.
4 | type Rule struct {
5 | resource string
6 | path string
7 | capabilities []string
8 | }
9 |
10 | // NewRule :
11 | func NewRule(res, path string, caps []string) *Rule {
12 | return &Rule{res, path, caps}
13 | }
14 |
15 | // Resource returns the type of resource targeted
16 | // by the rule.
17 | func (r *Rule) Resource() string {
18 | return r.resource
19 | }
20 |
21 | // Path returns the pattern this rule uses to
22 | // match targeted specific resource instances.
23 | func (r *Rule) Path() string {
24 | return r.path
25 | }
26 |
27 | // Capabilities return a slice of capabilities enabled
28 | // on the targeted resource instances.
29 | func (r *Rule) Capabilities() []string {
30 | return r.capabilities
31 | }
32 |
--------------------------------------------------------------------------------
/drago/auth/token.go:
--------------------------------------------------------------------------------
1 | package auth
2 |
3 | // Token implements the acl.Token interface
4 | type Token struct {
5 | privileged bool
6 | policies []string
7 | }
8 |
9 | // NewToken :
10 | func NewToken(privileged bool, policies []string) *Token {
11 | return &Token{privileged, policies}
12 | }
13 |
14 | // Policies returns a slice of policies associated with
15 | // the token.
16 | func (t *Token) Policies() []string {
17 | return t.policies
18 | }
19 |
20 | // IsPrivileged returns true if the token has privileged access,
21 | // and false otherwise.
22 | func (t *Token) IsPrivileged() bool {
23 | return t.privileged
24 | }
25 |
--------------------------------------------------------------------------------
/drago/state/etcd/acl_state.go:
--------------------------------------------------------------------------------
1 | package etcd
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "fmt"
7 |
8 | "github.com/seashell/drago/drago/structs"
9 | )
10 |
11 | // ACLState :
12 | func (r *StateRepository) ACLState(ctx context.Context) (*structs.ACLState, error) {
13 |
14 | key := aclStateKey()
15 |
16 | res, err := r.client.Get(ctx, key)
17 | if err != nil {
18 | return nil, err
19 | }
20 |
21 | if res.Count == 0 {
22 | return nil, errors.New("not found")
23 | }
24 |
25 | state := &structs.ACLState{}
26 |
27 | err = decodeValue(res.Kvs[0].Value, state)
28 | if err != nil {
29 | return nil, err
30 | }
31 |
32 | return state, nil
33 | }
34 |
35 | // ACLSetState :
36 | func (r *StateRepository) ACLSetState(ctx context.Context, s *structs.ACLState) error {
37 |
38 | key := aclStateKey()
39 |
40 | _, err := r.client.Put(ctx, key, encodeValue(s))
41 | if err != nil {
42 | return err
43 | }
44 |
45 | return nil
46 | }
47 |
48 | func aclStateKey() string {
49 | return fmt.Sprintf("%s/%s/global/%s", defaultPrefix, "acl", "state")
50 | }
51 |
--------------------------------------------------------------------------------
/drago/state/inmem/acl_policy.go:
--------------------------------------------------------------------------------
1 | package inmem
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "strings"
7 |
8 | structs "github.com/seashell/drago/drago/structs"
9 | )
10 |
11 | const (
12 | resourceTypePolicy = "policy"
13 | )
14 |
15 | // ACLPolicies :
16 | func (r *StateRepository) ACLPolicies(ctx context.Context) ([]*structs.ACLPolicy, error) {
17 | prefix := resourcePrefix(resourceTypePolicy)
18 |
19 | items := []*structs.ACLPolicy{}
20 | for el := range r.kv.Iter() {
21 | if strings.HasPrefix(el.Key, prefix) {
22 | if t, ok := el.Value.(*structs.ACLPolicy); ok {
23 | items = append(items, t)
24 | }
25 | }
26 | }
27 |
28 | return items, nil
29 | }
30 |
31 | // ACLPolicyByName :
32 | func (r *StateRepository) ACLPolicyByName(ctx context.Context, name string) (*structs.ACLPolicy, error) {
33 | key := resourceKey(resourceTypePolicy, name)
34 | if v, found := r.kv.Get(key); found {
35 | return v.(*structs.ACLPolicy), nil
36 | }
37 | return nil, errors.New("not found")
38 | }
39 |
40 | // UpsertACLPolicy :
41 | func (r *StateRepository) UpsertACLPolicy(ctx context.Context, p *structs.ACLPolicy) error {
42 | key := resourceKey(resourceTypePolicy, p.Name)
43 | r.kv.Set(key, p)
44 | return nil
45 | }
46 |
47 | // DeleteACLPolicies :
48 | func (r *StateRepository) DeleteACLPolicies(ctx context.Context, names []string) error {
49 | for _, name := range names {
50 | key := resourceKey(resourceTypePolicy, name)
51 | r.kv.Delete((key))
52 | }
53 | return nil
54 | }
55 |
--------------------------------------------------------------------------------
/drago/state/inmem/acl_state.go:
--------------------------------------------------------------------------------
1 | package inmem
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "fmt"
7 |
8 | structs "github.com/seashell/drago/drago/structs"
9 | )
10 |
11 | // ACLState :
12 | func (r *StateRepository) ACLState(ctx context.Context) (*structs.ACLState, error) {
13 | key := aclStateKey()
14 | if v, found := r.kv.Get(key); found {
15 | return v.(*structs.ACLState), nil
16 | }
17 | return nil, errors.New("not found")
18 | }
19 |
20 | // ACLSetState :
21 | func (r *StateRepository) ACLSetState(ctx context.Context, s *structs.ACLState) error {
22 | key := aclStateKey()
23 | r.kv.Set(key, s)
24 | return nil
25 | }
26 |
27 | func aclStateKey() string {
28 | return fmt.Sprintf("%s/%s/global/%s", defaultPrefix, "acl", "state")
29 | }
30 |
--------------------------------------------------------------------------------
/drago/status.go:
--------------------------------------------------------------------------------
1 | package drago
2 |
3 | import (
4 | auth "github.com/seashell/drago/drago/auth"
5 | state "github.com/seashell/drago/drago/state"
6 | structs "github.com/seashell/drago/drago/structs"
7 | )
8 |
9 | // StatusService is used to check on server status
10 | type StatusService struct {
11 | config *Config
12 | state state.Repository
13 | authHandler auth.AuthorizationHandler
14 | }
15 |
16 | // NewStatusService ...
17 | func NewStatusService(config *Config, state state.Repository, authHandler auth.AuthorizationHandler) *StatusService {
18 | return &StatusService{
19 | config: config,
20 | state: state,
21 | authHandler: authHandler,
22 | }
23 | }
24 |
25 | // Ping is used to check for connectivity
26 | func (s *StatusService) Ping(args structs.GenericRequest, out *structs.GenericResponse) error {
27 | return nil
28 | }
29 |
30 | // Version returns the version of the server
31 | func (s *StatusService) Version(in structs.GenericRequest, out *structs.StatusVersionResponse) error {
32 | out.Version = s.config.Version.VersionNumber()
33 | return nil
34 | }
35 |
--------------------------------------------------------------------------------
/drago/structs/acl.go:
--------------------------------------------------------------------------------
1 | package structs
2 |
3 | // ACLState ...
4 | type ACLState struct {
5 | RootTokenID string
6 | RootTokenResetIndex int
7 | }
8 |
9 | // Update ...
10 | func (s *ACLState) Update(rootID string) error {
11 | s.RootTokenID = rootID
12 | s.RootTokenResetIndex++
13 | return nil
14 | }
15 |
16 | // ACLBootstrapRequest :
17 | type ACLBootstrapRequest struct {
18 | ResetIndex uint64
19 |
20 | WriteRequest
21 | }
22 |
23 | // ResolveACLTokenRequest :
24 | type ResolveACLTokenRequest struct {
25 | Secret string
26 |
27 | QueryOptions
28 | }
29 |
30 | // ResolveACLTokenResponse :
31 | type ResolveACLTokenResponse struct {
32 | ACLToken *ACLToken
33 |
34 | Response
35 | }
36 |
--------------------------------------------------------------------------------
/drago/structs/agent.go:
--------------------------------------------------------------------------------
1 | package structs
2 |
3 | // Agent :
4 | type Agent struct {
5 | Config map[string]interface{}
6 | Stats map[string]map[string]string
7 | }
8 |
--------------------------------------------------------------------------------
/drago/structs/config/acl.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/seashell/drago/pkg/acl"
7 | )
8 |
9 | // ACLConfig :
10 | type ACLConfig struct {
11 | Enabled bool
12 | // TokenTTL controls for how long we keep ACL tokens in cache.
13 | TokenTTL time.Duration
14 |
15 | // Model contains the ACL model
16 | Model *acl.Model
17 | }
18 |
19 | // DefaultACLConfig :
20 | func DefaultACLConfig() *ACLConfig {
21 | return &ACLConfig{
22 | Enabled: false,
23 | TokenTTL: 30 * time.Second,
24 | Model: acl.NewModel(),
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/drago/structs/errors.go:
--------------------------------------------------------------------------------
1 | package structs
2 |
3 | import (
4 | "fmt"
5 |
6 | "errors"
7 | )
8 |
9 | const (
10 | errPermissionDenied = "Permission denied"
11 | errTokenNotFound = "ACL Token not found"
12 | errACLDisabled = "ACL disabled"
13 | errACLAlreadyBootstrapped = "ACL already bootstrapped"
14 | errInvalidInput = "Invalid input"
15 | errNotFound = "Resource not found"
16 | errInternal = "Internal error"
17 | )
18 |
19 | var (
20 | // ErrPermissionDenied :
21 | ErrPermissionDenied = errors.New(errPermissionDenied)
22 |
23 | // ErrACLAlreadyBootstrapped ...
24 | ErrACLAlreadyBootstrapped = errors.New(errACLAlreadyBootstrapped)
25 |
26 | // ErrACLDisabled ...
27 | ErrACLDisabled = errors.New(errACLDisabled)
28 |
29 | // ErrInternal ...
30 | ErrInternal = errors.New(errInternal)
31 |
32 | // ErrInvalidInput ...
33 | ErrInvalidInput = errors.New(errInvalidInput)
34 |
35 | // ErrNotFound ...
36 | ErrNotFound = errors.New(errNotFound)
37 | )
38 |
39 | // Error :
40 | type Error struct {
41 | Message string
42 | }
43 |
44 | // NewError ...
45 | func NewError(base error, extra ...interface{}) error {
46 | msg := base.Error()
47 | for _, v := range extra {
48 | msg = fmt.Sprintf("%s : %v", msg, v)
49 | }
50 | return &Error{
51 | Message: msg,
52 | }
53 | }
54 |
55 | func (e Error) Error() string {
56 | return e.Message
57 | }
58 |
59 | func NewInternalError(msg string) error {
60 | return NewError(ErrInternal, msg)
61 | }
62 |
63 | func NewInvalidInputError(msg string) error {
64 | return NewError(ErrInvalidInput, msg)
65 | }
66 |
--------------------------------------------------------------------------------
/drago/structs/status.go:
--------------------------------------------------------------------------------
1 | package structs
2 |
3 | // StatusVersionResponse ...
4 | type StatusVersionResponse struct {
5 | Version string
6 |
7 | QueryOptions
8 | }
9 |
--------------------------------------------------------------------------------
/drago/structs/structs.go:
--------------------------------------------------------------------------------
1 | package structs
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/pkg/errors"
7 | "github.com/seashell/drago/pkg/validator"
8 | )
9 |
10 | type Filters map[string][]string
11 |
12 | func (f Filters) Get(k string) []string {
13 | if v, ok := f[k]; ok {
14 | return v
15 | }
16 | return []string{}
17 | }
18 |
19 | func (f Filters) Add(k, v string) {
20 | f[k] = append(f[k], []string{v}...)
21 | }
22 |
23 | // QueryOptions contains information that is common to all read requests.
24 | type QueryOptions struct {
25 | AuthToken string
26 | Filters Filters
27 | }
28 |
29 | // WriteRequest contains information that is common to all write requests.
30 | type WriteRequest struct {
31 | AuthToken string
32 | }
33 |
34 | // Response contains information that is common to all responses.
35 | type Response struct {
36 | }
37 |
38 | // GenericRequest is used to request where no
39 | // specific information is needed.
40 | type GenericRequest struct {
41 | QueryOptions
42 | }
43 |
44 | // GenericResponse is used to respond to a request where no
45 | // specific response information is needed.
46 | type GenericResponse struct {
47 | Response
48 | }
49 |
50 | // Validate validates a struct/DTO, returning an error in case its
51 | // attributes are not compliant with the allowed values.
52 | func Validate(s interface{}) error {
53 |
54 | ctx := context.Background()
55 | v, err := validator.New(ctx)
56 | if err != nil {
57 | return err
58 | }
59 |
60 | err = v.Validate(s)
61 | if err != nil {
62 | return errors.Wrap(errors.New("invalid struct"), err.Error())
63 | }
64 |
65 | return nil
66 | }
67 |
--------------------------------------------------------------------------------
/drago/test/interface_test.go:
--------------------------------------------------------------------------------
1 | package test
2 |
--------------------------------------------------------------------------------
/drago/test/node_test.go:
--------------------------------------------------------------------------------
1 | package test
2 |
3 | import "testing"
4 |
5 | func TestNodeRegister(t *testing.T) {
6 |
7 | }
8 |
9 | func TestNodeStateUpdate(t *testing.T) {
10 |
11 | }
12 |
13 | func TestNodeInterfacesUpdate(t *testing.T) {
14 |
15 | }
16 | func TestNodeJoinNetwork(t *testing.T) {
17 |
18 | }
19 |
20 | func TestNodeLeaveNetwork(t *testing.T) {
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/example/client1.hcl:
--------------------------------------------------------------------------------
1 | data_dir = "/tmp/drago"
2 | bind_addr = "0.0.0.0"
3 |
4 | name = "node-1"
5 |
6 | advertise {
7 | peer = "192.168.100.13"
8 | }
9 |
10 | server {
11 | enabled = false
12 | }
13 |
14 | client {
15 | enabled = true
16 | servers = ["192.168.99.1:8081"]
17 | wireguard_path = "./boringtun"
18 | }
19 |
--------------------------------------------------------------------------------
/example/client2.hcl:
--------------------------------------------------------------------------------
1 | data_dir = "/tmp/drago"
2 | bind_addr = "0.0.0.0"
3 |
4 | name = "node-2"
5 |
6 | advertise {
7 | peer = "192.168.100.14"
8 | }
9 |
10 | server {
11 | enabled = false
12 | }
13 |
14 | client {
15 | enabled = true
16 | servers = ["192.168.100.12:8081"]
17 | wireguard_path = "./wireguard"
18 | meta = {
19 | test_meta = "test_meta_value"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/example/server.hcl:
--------------------------------------------------------------------------------
1 | data_dir = "/tmp/drago"
2 | bind_addr = "0.0.0.0"
3 | ui = true
4 |
5 | server {
6 | enabled = true
7 | }
8 |
9 | client {
10 | enabled = false
11 | }
12 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/seashell/drago
2 |
3 | go 1.16
4 |
5 | require (
6 | github.com/caarlos0/env v3.5.0+incompatible
7 | github.com/dimiro1/banner v1.1.0
8 | github.com/fatih/color v1.10.0
9 | github.com/go-playground/validator/v10 v10.4.1
10 | github.com/hashicorp/go-cleanhttp v0.5.2
11 | github.com/hashicorp/hcl/v2 v2.9.1
12 | github.com/imdario/mergo v0.3.12
13 | github.com/joho/godotenv v1.3.0
14 | github.com/pkg/errors v0.9.1
15 | github.com/rodaine/table v1.0.1
16 | github.com/sirupsen/logrus v1.8.1
17 | github.com/spf13/pflag v1.0.5
18 | github.com/vishvananda/netlink v1.1.1-0.20200604160102-dc0e1b988c57
19 | github.com/vmihailenco/msgpack v4.0.4+incompatible
20 | go.etcd.io/bbolt v1.3.5
21 | go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738
22 | go.uber.org/zap v1.16.0
23 | golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200609130330-bd2cb7843e1b
24 | )
25 |
--------------------------------------------------------------------------------
/init/README.md:
--------------------------------------------------------------------------------
1 | # init
2 |
3 | The `init` folder contains system init (systemd, upstart, sysv) and process manager/supervisor (runit, supervisord) configs for multiple platforms.
4 |
5 | ## Conventions
6 |
7 | On unix systems agent configurations shall be kept under `/etc/drago` and data under `/var/lib/drago/`. These directories are assumed to exist, and the Drago binary is assumed to be located at `/usr/bin/drago`.
8 |
9 | ## Agent configuration
10 |
11 | The following configuration files are provided as examples:
12 |
13 | * `demo/server.yml`
14 | * `demo/client.yml`
15 |
16 | Place one of these under `/etc/drago.d` depending on the host's role. You should use `server.yml` to configure a host as a server or `client.yml` to configure a host as a client.
17 |
18 | ## systemd
19 |
20 | On systems using `systemd`, the basic unit file under `systemd/drago.service` starts and stops the drago agent. Place it under `/etc/systemd/system/drago.service`.
21 |
22 | You can control Drago with `systemctl start|stop|restart drago`.
--------------------------------------------------------------------------------
/init/systemd/drago.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Drago
3 | Wants=network-online.target
4 | After=network-online.target
5 |
6 | [Service]
7 | ExecReload=/bin/kill -HUP $MAINPID
8 | ExecStart=/usr/local/bin/drago agent --config /etc/drago.d
9 | KillMode=process
10 | KillSignal=SIGINT
11 | LimitNOFILE=65536
12 | LimitNPROC=infinity
13 | Restart=on-failure
14 | RestartSec=2
15 | StartLimitBurst=3
16 | StartLimitIntervalSec=10
17 | TasksMax=infinity
18 | OOMScoreAdjust=-1000
19 |
20 | [Install]
21 | WantedBy=multi-user.target
--------------------------------------------------------------------------------
/pkg/acl/config.go:
--------------------------------------------------------------------------------
1 | package acl
2 |
3 | import (
4 | "github.com/seashell/drago/pkg/log"
5 | )
6 |
7 | // ResolverConfig contains configurations for an ACL Resolver.
8 | type ResolverConfig struct {
9 | Logger log.Logger
10 | Model *Model
11 | SecretResolver SecretResolverFunc
12 | PolicyResolver PolicyResolverFunc
13 | }
14 |
15 | // DefaultResolverConfig returns default configurations
16 | // for an ACL Resolver.
17 | func DefaultResolverConfig() *ResolverConfig {
18 | return &ResolverConfig{
19 | Logger: &simpleLogger{
20 | fields: map[string]interface{}{},
21 | options: log.LoggerOptions{
22 | Prefix: "acl",
23 | Level: levelInfo,
24 | },
25 | },
26 | }
27 | }
28 |
29 | // Merge merges two ResolverConfig structs, returning the result.
30 | func (c *ResolverConfig) Merge(in *ResolverConfig) *ResolverConfig {
31 | res := *c
32 | if in.Logger != nil {
33 | res.Logger = in.Logger
34 | }
35 | if in.Model != nil {
36 | res.Model = in.Model
37 | }
38 | if in.SecretResolver != nil {
39 | res.SecretResolver = in.SecretResolver
40 | }
41 | if in.PolicyResolver != nil {
42 | res.PolicyResolver = in.PolicyResolver
43 | }
44 | return &res
45 | }
46 |
--------------------------------------------------------------------------------
/pkg/acl/policy.go:
--------------------------------------------------------------------------------
1 | package acl
2 |
3 | // Policy represents a named set of rules for allowing
4 | // operations on specific resources.
5 | type Policy interface {
6 | Name() string
7 | Rules() []Rule
8 | }
9 |
10 | // Rule is used to allow operations on specific resources. In
11 | // addition to the name of the target resource type and the allowed
12 | // operations, a rule also specifies an optional glob pattern for
13 | // targeting specific instances of the resource.
14 | type Rule interface {
15 | // Resource targeted by this rule.
16 | Resource() string
17 | // Path used to target specific instances
18 | // of the target resource, if applicable.
19 | Path() string
20 | // Capabilities contains the actions allowed on
21 | // instances of a resource matching this rule.
22 | Capabilities() []string
23 | }
24 |
--------------------------------------------------------------------------------
/pkg/acl/token.go:
--------------------------------------------------------------------------------
1 | package acl
2 |
3 | // Token ...
4 | type Token interface {
5 | IsPrivileged() bool
6 | Policies() []string
7 | }
8 |
--------------------------------------------------------------------------------
/pkg/cli/command.go:
--------------------------------------------------------------------------------
1 | package cli
2 |
3 | import "context"
4 |
5 | const (
6 | CommandReturnCodeHelp = -18511
7 | )
8 |
9 | // A Command is a runnable sub-command of a CLI
10 | type Command interface {
11 | // Help should return long-form help text that includes the command-line
12 | // usage, a brief few sentences explaining the function of the command,
13 | // and the complete list of flags the command accepts.
14 | Help() string
15 |
16 | // Synopsis should return a one-line, short synopsis of the command.
17 | // This should be less than 50 characters ideally.
18 | Synopsis() string
19 |
20 | // Run should run the actual command with the given CLI instance and
21 | // command-line arguments. It should return the exit status when it is
22 | // finished.
23 | Run(ctx context.Context, args []string) int
24 | }
25 |
26 | // A NamedCommand is a runnable sub-command of a CLI with a name
27 | type NamedCommand interface {
28 | Command
29 | Name() string
30 | }
31 |
--------------------------------------------------------------------------------
/pkg/cli/command_mock.go:
--------------------------------------------------------------------------------
1 | package cli
2 |
3 | import "context"
4 |
5 | // MockCommand is an implementation of Command that can be used for testing.
6 | // It is also used for automatically populating missing parent commands.
7 | type MockCommand struct {
8 | HelpText string
9 | SynopsisText string
10 | RunReturnCode int
11 | // Set by the command
12 | RunCalled bool
13 | RunArgs []string
14 | }
15 |
16 | func (c *MockCommand) Help() string {
17 | return c.HelpText
18 | }
19 |
20 | func (c *MockCommand) Run(ctx context.Context, args []string) int {
21 | c.RunCalled = true
22 | c.RunArgs = args
23 | return c.RunReturnCode
24 | }
25 |
26 | func (c *MockCommand) Synopsis() string {
27 | return c.SynopsisText
28 | }
29 |
--------------------------------------------------------------------------------
/pkg/concurrent/map.go:
--------------------------------------------------------------------------------
1 | package concurrent
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | // Map type that can be safely shared between
8 | // goroutines that require read/write access to a map
9 | type Map struct {
10 | sync.RWMutex
11 | items map[string]interface{}
12 | }
13 |
14 | func NewMap() *Map {
15 | return &Map{
16 | items: map[string]interface{}{},
17 | }
18 | }
19 |
20 | // Concurrent map item
21 | type MapItem struct {
22 | Key string
23 | Value interface{}
24 | }
25 |
26 | // Sets a key in a concurrent map
27 | func (cm *Map) Set(key string, value interface{}) {
28 | cm.Lock()
29 | defer cm.Unlock()
30 | cm.items[key] = value
31 |
32 | }
33 |
34 | // Gets a key from a concurrent map
35 | func (cm *Map) Get(key string) (interface{}, bool) {
36 | cm.RLock()
37 | defer cm.RUnlock()
38 | value, ok := cm.items[key]
39 | return value, ok
40 | }
41 |
42 | // Returns the length of a concurrent map
43 | func (cm *Map) Len() int {
44 | cm.RLock()
45 | defer cm.RUnlock()
46 | return len(cm.items)
47 | }
48 |
49 | // Deletes a key from a concurrent map
50 | func (cm *Map) Delete(key string) {
51 | cm.Lock()
52 | defer cm.Unlock()
53 | delete(cm.items, key)
54 | }
55 |
56 | // Iterates over the items in a concurrent map
57 | // Each item is sent over a channel, so that we can iterate
58 | // over the map using the builtin range keyword
59 | func (cm *Map) Iter() <-chan MapItem {
60 | c := make(chan MapItem)
61 | f := func() {
62 | snapshot := map[string]MapItem{}
63 | cm.RLock()
64 | for k, v := range cm.items {
65 | snapshot[k] = MapItem{Key: k, Value: v}
66 | }
67 | cm.RUnlock()
68 | for _, item := range snapshot {
69 | c <- item
70 | }
71 | close(c)
72 | }
73 | go f()
74 |
75 | return c
76 | }
77 |
--------------------------------------------------------------------------------
/pkg/http/config.go:
--------------------------------------------------------------------------------
1 | package http
2 |
3 | import (
4 | log "github.com/seashell/drago/pkg/log"
5 | )
6 |
7 | // Config contains configurations for the http module
8 | type Config struct {
9 | // Server bind address in the form host:port
10 | BindAddress string
11 |
12 | // Handlers contains a mapping of paths to http.HandlerFunc
13 | Handlers map[string]Handler
14 |
15 | // Handlers contains middleware functions to be applied to handlers
16 | Middleware []Middleware
17 |
18 | // Logger
19 | Logger log.Logger
20 | }
21 |
22 | // DefaultConfig :
23 | func DefaultConfig() *Config {
24 | return &Config{
25 | BindAddress: "0.0.0.0:9876",
26 | Handlers: map[string]Handler{},
27 | Middleware: []Middleware{},
28 | }
29 | }
30 |
31 | // Merge :
32 | func (s *Config) Merge(b *Config) *Config {
33 | result := *s
34 | if b.BindAddress != "" {
35 | result.BindAddress = b.BindAddress
36 | }
37 | if b.Logger != nil {
38 | result.Logger = b.Logger
39 | }
40 | if b.Handlers != nil {
41 | result.Handlers = b.Handlers
42 | }
43 | if b.Middleware != nil {
44 | result.Middleware = b.Middleware
45 | }
46 | return &result
47 | }
48 |
--------------------------------------------------------------------------------
/pkg/http/handler.go:
--------------------------------------------------------------------------------
1 | package http
2 |
3 | import (
4 | "net/http"
5 | )
6 |
7 | // Handler :
8 | type Handler interface {
9 | Handle(http.ResponseWriter, *http.Request) (interface{}, error)
10 | }
11 |
12 | // HandlerFunc is a custom HTTP handler function that returns a struct
13 | // and an error that will be encoded and returned to the client
14 | type HandlerFunc func(http.ResponseWriter, *http.Request) (interface{}, error)
15 |
--------------------------------------------------------------------------------
/pkg/http/middleware.go:
--------------------------------------------------------------------------------
1 | package http
2 |
3 | import "net/http"
4 |
5 | // Middleware :
6 | type Middleware func(http.HandlerFunc) http.HandlerFunc
7 |
--------------------------------------------------------------------------------
/pkg/log/log.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | // Fields : Log fields
4 | type Fields map[string]interface{}
5 |
6 | // Options contains logging options which are
7 | // common to any logger
8 | type LoggerOptions struct {
9 | Prefix string
10 | Level string
11 | }
12 |
13 | // Logger : Logger interface
14 | type Logger interface {
15 | Debugf(format string, args ...interface{})
16 | Infof(format string, args ...interface{})
17 | Warnf(format string, args ...interface{})
18 | Errorf(format string, args ...interface{})
19 | Fatalf(format string, args ...interface{})
20 | Panicf(format string, args ...interface{})
21 | WithFields(fields Fields) Logger
22 | WithName(name string) Logger
23 | }
24 |
--------------------------------------------------------------------------------
/pkg/radix/README.md:
--------------------------------------------------------------------------------
1 | # radix
2 |
3 | Simple implementation of radix trees in Go.
4 |
5 | A radix tree is a space-optimized/compressed version of a standard [trie](https://en.wikipedia.org/wiki/Trie), with every node that is a single child being merged with their parent. Unlike regular tries, the edges of a radix tree can hold strings, not only single characters.
6 |
7 | ### References
8 | [Wikipedia article about Radix trees](https://en.wikipedia.org/wiki/Radix_tree)
9 | [Compressing radix trees without too many tears](https://medium.com/basecs/compressing-radix-trees-without-too-many-tears-a2e658adb9a0)
10 |
11 | ### Related projects
12 | [armon/go-radix](https://github.com/armon/go-radix)
13 | [asergeyev/nradix/](https://github.com/asergeyev/nradix/)
14 | [zmap/go-iptree](https://github.com/zmap/go-iptree)
--------------------------------------------------------------------------------
/pkg/radix/node.go:
--------------------------------------------------------------------------------
1 | package radix
2 |
3 | import "sort"
4 |
5 | type node struct {
6 | leaf *leaf
7 | edges []*edge
8 | }
9 |
10 | type leaf struct {
11 | key string
12 | value interface{}
13 | }
14 |
15 | func (n *node) isLeaf() bool {
16 | return n.leaf != nil
17 | }
18 |
19 | func (n *node) addEdge(e *edge) {
20 | n.edges = append(n.edges, e)
21 | n.sortEdges()
22 | }
23 |
24 | func (n *node) deleteEdge(s string) {
25 | // Apply binary search, since our edges sorted
26 | idx := sort.Search(len(n.edges), func(i int) bool {
27 | return n.edges[i].label >= s
28 | })
29 | n.edges = append(n.edges[:idx], n.edges[idx+1:]...)
30 | }
31 |
32 | func (n *node) sortEdges() {
33 | sort.Slice(n.edges, func(i, j int) bool {
34 | return n.edges[i].label < n.edges[j].label
35 | })
36 | }
37 |
38 | // getEdgeWithLongestCommonPrefix takes a query string 's' and finds
39 | // amongst all edges in the node the longest prefix they have in
40 | // common with 's', returning both the prefix and the edge.
41 | //
42 | // Example:
43 | //
44 | // query = "abcdef", edges = [{"aaa"},{"abcde"},{"de"},{"bde"}]
45 | //
46 | // output = "ab", {"abcde"}
47 | func (n *node) getEdgeWithLongestCommonPrefix(s string) (string, *edge) {
48 | for _, e := range n.edges {
49 | if p := longestCommonPrefix(s, e.label); p != "" {
50 | return p, e
51 | }
52 | }
53 | return "", nil
54 | }
55 |
--------------------------------------------------------------------------------
/pkg/rpc/config.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "time"
5 |
6 | log "github.com/seashell/drago/pkg/log"
7 | )
8 |
9 | type ServerConfig struct {
10 | //BindAddress
11 | BindAddress string
12 |
13 | // Logger
14 | Logger log.Logger
15 |
16 | // Receivers
17 | Receivers map[string]interface{}
18 | }
19 |
20 | func DefaultConfig() *ServerConfig {
21 | return &ServerConfig{
22 | BindAddress: "0.0.0.0:8081",
23 | Receivers: map[string]interface{}{},
24 | }
25 | }
26 |
27 | func (s *ServerConfig) Merge(b *ServerConfig) *ServerConfig {
28 | result := *s
29 | if b.BindAddress != "" {
30 | result.BindAddress = b.BindAddress
31 | }
32 | if b.Logger != nil {
33 | result.Logger = b.Logger
34 | }
35 | if b.Receivers != nil {
36 | result.Receivers = b.Receivers
37 | }
38 | return &result
39 | }
40 |
41 | type ClientConfig struct {
42 | // Logger
43 | Logger log.Logger
44 |
45 | // URL of the Drago server (e.g. http://127.0.0.1:8081).
46 | Address string
47 |
48 | // Timeout when dialing.
49 | DialTimeout time.Duration
50 | }
51 |
52 | func DefaultClientConfig() *ClientConfig {
53 | return &ClientConfig{
54 | Address: "0.0.0.0:8081",
55 | DialTimeout: 2 * time.Second,
56 | }
57 | }
58 |
59 | func (c *ClientConfig) Merge(b *ClientConfig) *ClientConfig {
60 | result := *c
61 | if b.Address != "" {
62 | result.Address = b.Address
63 | }
64 | if b.Logger != nil {
65 | result.Logger = b.Logger
66 | }
67 | if b.DialTimeout != 0 {
68 | result.DialTimeout = b.DialTimeout
69 | }
70 | return &result
71 | }
72 |
--------------------------------------------------------------------------------
/pkg/string/string.go:
--------------------------------------------------------------------------------
1 | package string
2 |
3 | import "strings"
4 |
5 | // SliceToString : Takes a slice containing strings and returns a comma-separated string
6 | func SliceToString(s []string) string {
7 | return strings.Join(s, ",")
8 | }
9 |
10 | // StringToSlice : Takes a comma-separated string and returns a slice
11 | func StringToSlice(s string) []string {
12 | return strings.Fields(strings.Replace(s, ",", " ", -1))
13 | }
14 |
--------------------------------------------------------------------------------
/pkg/util/util.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | func StrToPtr(s string) *string {
4 | return &s
5 | }
6 |
7 | func BoolToPtr(b bool) *bool {
8 | return &b
9 | }
10 |
11 | func IntToPtr(i int) *int {
12 | return &i
13 | }
14 |
15 | func Uint16ToPtr(i uint16) *uint16 {
16 | return &i
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/uuid/uuid.go:
--------------------------------------------------------------------------------
1 | package uuid
2 |
3 | import (
4 | "crypto/rand"
5 | "fmt"
6 | )
7 |
8 | func Generate() string {
9 | buf := make([]byte, 16)
10 | if _, err := rand.Read(buf); err != nil {
11 | panic(fmt.Errorf("failed to read random bytes: %v", err))
12 | }
13 | return fmt.Sprintf("%x-%x-%x-%x-%x",
14 | buf[0:4],
15 | buf[4:6],
16 | buf[6:8],
17 | buf[8:10],
18 | buf[10:16])
19 | }
20 |
--------------------------------------------------------------------------------
/pkg/validator/validator.go:
--------------------------------------------------------------------------------
1 | package validator
2 |
3 | import (
4 | "context"
5 | "regexp"
6 |
7 | "github.com/go-playground/validator/v10"
8 | )
9 |
10 | func dashedAlphanumValidator(fl validator.FieldLevel) bool {
11 | re := regexp.MustCompile("^[a-z0-9][a-z0-9_-]*$")
12 |
13 | return re.MatchString(fl.Field().String())
14 | }
15 |
16 | type Validator struct {
17 | v *validator.Validate
18 | }
19 |
20 | func New(ctx context.Context) (*Validator, error) {
21 | v := validator.New()
22 | v.RegisterValidation("dashed-alphanumeric", dashedAlphanumValidator)
23 |
24 | return &Validator{
25 | v: v,
26 | }, nil
27 | }
28 |
29 | func (v Validator) Validate(i interface{}) error {
30 | return v.v.Struct(i)
31 | }
32 |
--------------------------------------------------------------------------------
/plugin/admission/admission.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "net/rpc"
6 | )
7 |
8 | // AdmissionPlugin :
9 | type AdmissionPlugin struct {
10 | logger log.Logger
11 | rpcServer *rpc.Server
12 | }
13 |
14 | // Config :
15 | type Config struct {
16 | }
17 |
18 | // NewAdmissionPlugin : Creates a new admission plugin object parameterized according to the provided configurations.
19 | func NewAdmissionPlugin(config *Config) (*AdmissionPlugin, error) {
20 | p := &AdmissionPlugin{}
21 | return p, nil
22 | }
23 |
24 | func main() {
25 | _, err := NewAdmissionPlugin(&Config{})
26 | if err != nil {
27 | panic(err)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/plugin/lease/lease.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "net/rpc"
6 | )
7 |
8 | // LeasePlugin :
9 | type LeasePlugin struct {
10 | logger log.Logger
11 | rpcServer *rpc.Server
12 | }
13 |
14 | // Config :
15 | type Config struct {
16 | }
17 |
18 | // NewLeasePlugin : Creates a new lease plugin object parameterized according to the provided configurations.
19 | func NewLeasePlugin(config *Config) (*LeasePlugin, error) {
20 | p := &LeasePlugin{}
21 | return p, nil
22 | }
23 |
24 | func main() {
25 | _, err := NewLeasePlugin(&Config{})
26 | if err != nil {
27 | panic(err)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/plugin/mesh/mesh.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "net/rpc"
5 |
6 | log "github.com/seashell/drago/pkg/log"
7 | )
8 |
9 | // TODO: Implementation of the drago.Plugin interface, with services exposed through RPC
10 | // Whenever a new interface is created (event InterfaceCreated), this plugin interacts with the
11 | // Drago RPC API in order to Link this interface with every other publicly exposed interface in
12 | // the same network, thus creating a mesh overlay.
13 |
14 | // MeshPlugin :
15 | type MeshPlugin struct {
16 | logger log.Logger
17 | rpcServer *rpc.Server
18 | }
19 |
20 | // Config :
21 | type Config struct {
22 | }
23 |
24 | // NewMeshPlugin : Creates a new mesh plugin object parameterized according to the provided configurations.
25 | func NewMeshPlugin(config *Config) (*MeshPlugin, error) {
26 | p := &MeshPlugin{}
27 | return p, nil
28 | }
29 |
30 | func main() {
31 | _, err := NewMeshPlugin(&Config{})
32 | if err != nil {
33 | panic(err)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/plugin/notification/notification.go:
--------------------------------------------------------------------------------
1 | package notification
2 |
3 | import (
4 | "net/rpc"
5 |
6 | log "github.com/seashell/drago/pkg/log"
7 | )
8 |
9 | // TODO: Implementation of the drago.Plugin interface, with services exposed through RPC
10 | // Whenever an event occurs in the Drago server (e.g. a new node is registered, or becomes online),
11 | // this plugin interacts with the Drago RPC API in order to obtain information about this event
12 | // and notify users via email, Telegram, slack, etc.
13 |
14 | // NotificationPlugin :
15 | type NotificationPlugin struct {
16 | logger log.Logger
17 | rpcServer *rpc.Server
18 | }
19 |
20 | // Config :
21 | type Config struct {
22 | }
23 |
24 | // NewNotificationPlugin : Creates a new mesh plugin object parameterized according to the provided configurations.
25 | func NewNotificationPlugin(config *Config) (*NotificationPlugin, error) {
26 | p := &NotificationPlugin{}
27 | return p, nil
28 | }
29 |
30 | func main() {
31 | _, err := NewNotificationPlugin(&Config{})
32 | if err != nil {
33 | panic(err)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/plugin/plugin.go:
--------------------------------------------------------------------------------
1 | package plugin
2 |
3 | type Plugin interface{}
4 |
--------------------------------------------------------------------------------
/ui/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-react"
4 | ]
5 | }
--------------------------------------------------------------------------------
/ui/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es2021": true
5 | },
6 | "extends": ["react-app","plugin:react/recommended", "airbnb", "prettier", "prettier/react"],
7 | "parserOptions": {
8 | "ecmaFeatures": {
9 | "jsx": true
10 | },
11 | "ecmaVersion": 12,
12 | "sourceType": "module"
13 | },
14 | "plugins": ["react"],
15 | "rules": {
16 | "no-unused-vars": "warn",
17 | "react/jsx-curly-brace-presence": "off",
18 | "no-param-reassign": "off",
19 | "no-use-before-define": "off",
20 | "no-nested-ternary": "off",
21 | "react/jsx-filename-extension": "off",
22 | "import/no-extraneous-dependencies": "off",
23 | "jsx-a11y/anchor-is-valid": "off",
24 | "react-hooks/exhaustive-deps": "off",
25 | "react/jsx-props-no-spreading": "off",
26 | "react/destructuring-assignment": "off",
27 | "react/state-in-constructor": "off",
28 | "react/static-property-placement":"off"
29 | },
30 | "settings": {
31 | "import/resolver": {
32 | "webpack": {
33 | "config": "config/webpack/eslint.js"
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/ui/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "parser": "babel",
4 | "printWidth": 100,
5 | "arrowParens": "always",
6 | "singleQuote": true,
7 | "trailingComma": "es5",
8 | "bracketSpacing": true,
9 | "jsxBracketSameLine": false
10 | }
--------------------------------------------------------------------------------
/ui/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.workingDirectories": ["./ui/"],
3 | "eslint.alwaysShowStatus": true,
4 | "eslint.packageManager": "yarn",
5 | "eslint.run": "onType",
6 |
7 | "prettier.prettierPath": "./node_modules/prettier",
8 | "prettier.configPath": "./.prettierrc",
9 | "prettier.packageManager": "yarn",
10 |
11 | "[javascript]": {
12 | "editor.defaultFormatter": "esbenp.prettier-vscode",
13 | "editor.formatOnSave": true,
14 | "editor.codeActionsOnSave": {
15 | "source.organizeImports": true,
16 | "source.fixAll.eslint": true,
17 | "source.fixAll.stylelint": true
18 | }
19 | },
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/ui/build/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/build/.keep
--------------------------------------------------------------------------------
/ui/config/rewired/index.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | const { override, useBabelRc, useEslintRc, addWebpackResolve } = require('customize-cra')
4 |
5 | const rewireStyledComponents = require('react-app-rewire-styled-components')
6 | const resolve = require('../webpack/resolve')
7 |
8 | module.exports = {
9 | webpack: override(
10 | addWebpackResolve(resolve),
11 | // eslint-disable-next-line react-hooks/rules-of-hooks
12 | // useBabelRc(path.resolve(__dirname, '..', '..', '.babelrc')),
13 | // eslint-disable-next-line react-hooks/rules-of-hooks
14 | // useEslintRc(path.resolve(__dirname, '..', '..', '.eslintrc')),
15 | (config, env) => rewireStyledComponents(config, env, {})
16 | ),
17 | }
18 |
--------------------------------------------------------------------------------
/ui/config/webpack/eslint.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | const resolve = require('./resolve')
4 |
5 | module.exports = {
6 | entry: ['../src/index'],
7 | output: {
8 | path: path.join(__dirname, 'dist'),
9 | filename: 'bundle.js',
10 | publicPath: '/static/',
11 | },
12 | plugins: [],
13 | resolve,
14 | }
15 |
--------------------------------------------------------------------------------
/ui/config/webpack/resolve.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | module.exports = {
4 | alias: {
5 | _assets: path.resolve(__dirname, '..', '..', 'src/assets/'),
6 | _components: path.resolve(__dirname, '..', '..', 'src/components/'),
7 | _containers: path.resolve(__dirname, '..', '..', 'src/containers/'),
8 | _modals: path.resolve(__dirname, '..', '..', 'src/modals/'),
9 | _graphql: path.resolve(__dirname, '..', '..', 'src/graphql/'),
10 | _utils: path.resolve(__dirname, '..', '..', 'src/utils/'),
11 | _views: path.resolve(__dirname, '..', '..', 'src/views/'),
12 | },
13 | }
14 |
--------------------------------------------------------------------------------
/ui/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "experimentalDecorators": true,
4 | "baseUrl": "src",
5 | "paths": {
6 | "_components/*": ["components/*"],
7 | "_containers/*": ["containers/*"],
8 | "_modals/*": ["modals/*"],
9 | "_views/*": ["views/*"],
10 | "_graphql/*": ["graphql/*"],
11 | "_utils/*": ["utils/*"],
12 | "_assets/*": ["assets/*"]
13 | }
14 | },
15 | "exclude": ["node_modules", "dist", "dist-server"]
16 | }
17 |
--------------------------------------------------------------------------------
/ui/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/public/favicon.ico
--------------------------------------------------------------------------------
/ui/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
22 | Drago
23 |
24 |
25 |
26 |
27 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/ui/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "drago",
3 | "name": "drago",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#fafbfc"
15 | }
16 |
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Lato-Black.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Lato-Black.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Lato-Black.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Lato-Black.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Lato-BlackItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Lato-BlackItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Lato-BlackItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Lato-BlackItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Lato-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Lato-Bold.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Lato-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Lato-Bold.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Lato-BoldItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Lato-BoldItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Lato-BoldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Lato-BoldItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Lato-Hairline.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Lato-Hairline.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Lato-Hairline.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Lato-Hairline.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Lato-HairlineItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Lato-HairlineItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Lato-HairlineItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Lato-HairlineItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Lato-Italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Lato-Italic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Lato-Italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Lato-Italic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Lato-Light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Lato-Light.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Lato-Light.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Lato-Light.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Lato-LightItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Lato-LightItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Lato-LightItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Lato-LightItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Lato-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Lato-Regular.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Lato-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Lato-Regular.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-Black.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-Black.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-Black.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-Black.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-BlackItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-BlackItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-BlackItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-BlackItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-Bold.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-Bold.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-BoldItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-BoldItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-BoldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-BoldItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-ExtraBold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-ExtraBold.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-ExtraBold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-ExtraBold.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-ExtraBoldItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-ExtraBoldItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-ExtraBoldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-ExtraBoldItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-ExtraLight.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-ExtraLight.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-ExtraLight.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-ExtraLight.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-ExtraLightItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-ExtraLightItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-ExtraLightItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-ExtraLightItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-Italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-Italic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-Italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-Italic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-Light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-Light.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-Light.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-Light.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-LightItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-LightItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-LightItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-LightItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-Medium.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-Medium.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-Medium.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-Medium.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-MediumItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-MediumItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-MediumItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-MediumItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-Regular.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-Regular.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-SemiBold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-SemiBold.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-SemiBold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-SemiBold.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-SemiBoldItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-SemiBoldItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-SemiBoldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-SemiBoldItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-Thin.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-Thin.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-Thin.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-Thin.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-ThinItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-ThinItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Montserrat-ThinItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Montserrat-ThinItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-Black.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-Black.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-Black.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-Black.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-BlackItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-BlackItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-BlackItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-BlackItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-Bold.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-Bold.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-BoldItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-BoldItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-BoldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-BoldItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-ExtraBold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-ExtraBold.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-ExtraBold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-ExtraBold.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-ExtraBoldItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-ExtraBoldItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-ExtraBoldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-ExtraBoldItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-ExtraLight.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-ExtraLight.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-ExtraLight.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-ExtraLight.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-ExtraLightItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-ExtraLightItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-ExtraLightItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-ExtraLightItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-Italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-Italic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-Italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-Italic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-Light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-Light.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-Light.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-Light.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-LightItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-LightItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-LightItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-LightItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-Medium.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-Medium.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-Medium.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-Medium.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-MediumItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-MediumItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-MediumItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-MediumItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-Regular.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-Regular.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-SemiBold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-SemiBold.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-SemiBold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-SemiBold.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-SemiBoldItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-SemiBoldItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-SemiBoldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-SemiBoldItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-Thin.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-Thin.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-Thin.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-Thin.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-ThinItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-ThinItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Raleway-ThinItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Raleway-ThinItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-Black.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-Black.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-Black.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-Black.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-BlackItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-BlackItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-BlackItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-BlackItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-Bold.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-Bold.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-BoldItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-BoldItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-BoldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-BoldItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-Italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-Italic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-Italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-Italic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-Light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-Light.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-Light.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-Light.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-LightItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-LightItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-LightItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-LightItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-Medium.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-Medium.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-Medium.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-Medium.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-MediumItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-MediumItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-MediumItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-MediumItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-Regular.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-Regular.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-Thin.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-Thin.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-Thin.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-Thin.woff2
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-ThinItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-ThinItalic.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/Roboto-ThinItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/assets/fonts/Roboto-ThinItalic.woff2
--------------------------------------------------------------------------------
/ui/src/assets/icons/down.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/ui/src/assets/icons/error.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/ui/src/assets/icons/external-link.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/ui/src/assets/icons/leave.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/ui/src/assets/icons/left.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/ui/src/assets/icons/link.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ui/src/assets/icons/plus.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/ui/src/assets/icons/refresh.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/ui/src/assets/icons/right.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/ui/src/assets/icons/search.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/ui/src/assets/icons/success.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/ui/src/assets/icons/times.svg:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/ui/src/assets/icons/unknown.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/ui/src/assets/icons/up.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/ui/src/assets/icons/warning.svg:
--------------------------------------------------------------------------------
1 |
2 |
13 |
--------------------------------------------------------------------------------
/ui/src/assets/illustrations/index.js:
--------------------------------------------------------------------------------
1 | import { ReactComponent as EmptyIllustration } from './empty.svg'
2 | import { ReactComponent as ErrorIllustration } from './error.svg'
3 | import { ReactComponent as NotFoundIllustration } from './not-found.svg'
4 | import { ReactComponent as UnauthorizedIllustration } from './unauthorized.svg'
5 |
6 | export default {
7 | NotFound: NotFoundIllustration,
8 | Error: ErrorIllustration,
9 | Empty: EmptyIllustration,
10 | Unauthorized: UnauthorizedIllustration,
11 | }
12 |
--------------------------------------------------------------------------------
/ui/src/assets/index.js:
--------------------------------------------------------------------------------
1 | import icons from './icons'
2 | import illustrations from './illustrations'
3 |
4 | export { icons, illustrations }
5 |
--------------------------------------------------------------------------------
/ui/src/components/avatar/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import { layout, space } from 'styled-system'
4 |
5 | import ReactAvatar, { ConfigProvider } from 'react-avatar'
6 |
7 | const StyledReactAvatar = styled(ReactAvatar)`
8 | ${layout}
9 | ${space}
10 | `
11 |
12 | const Avatar = props => (
13 |
14 |
15 |
16 | )
17 |
18 | export default Avatar
19 |
--------------------------------------------------------------------------------
/ui/src/components/back-link/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import styled from 'styled-components'
4 | import { icons } from '_assets/'
5 | import Box from '_components/box'
6 | import Icon from '_components/icon'
7 | import Link from '_components/link'
8 | import Text from '_components/text'
9 |
10 | const Container = styled(Box)`
11 | width: max-content;
12 | opacity: 0.3;
13 | :hover {
14 | opacity: 1;
15 | }
16 | `
17 |
18 | const BackLink = ({ text, to, ...props }) => (
19 |
20 |
21 |
22 | } color="foreground2" size="14px" />
23 |
24 | {text}
25 |
26 |
27 |
28 |
29 | )
30 |
31 | BackLink.propTypes = {
32 | text: PropTypes.string.isRequired,
33 | to: PropTypes.string.isRequired,
34 | }
35 |
36 | export default BackLink
37 |
--------------------------------------------------------------------------------
/ui/src/components/box/index.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { border, borderStyle, color, flexbox, grid, layout, shadow, space } from 'styled-system'
3 | import { containers } from '../../styles'
4 |
5 | const Box = styled.div`
6 | ${grid}
7 | ${space}
8 | ${color}
9 | ${layout}
10 | ${flexbox}
11 | ${border}
12 | ${containers}
13 | ${borderStyle}
14 | ${shadow}
15 | `
16 |
17 | Box.defaultProps = {
18 | border: 'none',
19 | height: 'auto',
20 | display: 'flex',
21 | }
22 |
23 | export default Box
24 |
--------------------------------------------------------------------------------
/ui/src/components/button/index.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | import { buttonStyle, shadow, layout, typography, border, space, color } from 'styled-system'
4 |
5 | const Button = styled.button`
6 | font-family: 'Lato';
7 | font-weight: bold;
8 | letter-spacing: 0.04em;
9 | border: none;
10 |
11 | :disabled {
12 | opacity: 0.4;
13 | background: #ddd;
14 | border-color: #ddd;
15 | color: #666;
16 | box-shadow: none;
17 | cursor: not-allowed;
18 | }
19 |
20 | &:hover {
21 | filter: brightness(95%);
22 | transition: all 0.7s ease;
23 | }
24 |
25 | border-radius: 1px;
26 |
27 | ${buttonStyle}
28 | ${typography}
29 | ${shadow}
30 | ${layout}
31 | ${space}
32 | ${border}
33 | ${color}
34 | `
35 |
36 | Button.defaultProps = {
37 | variant: 'primary',
38 | width: 'max-content',
39 | px: '24px',
40 | height: '50px',
41 | type: 'button',
42 | }
43 |
44 | export default Button
45 |
--------------------------------------------------------------------------------
/ui/src/components/collapse/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled from 'styled-components'
4 | import { space } from 'styled-system'
5 | import { icons } from '_assets'
6 |
7 | const Header = styled.div.attrs({
8 | role: 'button',
9 | })`
10 | cursor: pointer;
11 | display: flex;
12 | align-items: center;
13 | justify-content: space-between;
14 | ${space}
15 | `
16 |
17 | Header.defaultProps = {
18 | paddingTop: 2,
19 | paddingBottom: 2,
20 | }
21 |
22 | const Content = styled.div`
23 | ${space}
24 | `
25 |
26 | const Collapse = ({ title, children, ...props }) => {
27 | const [isCollapseOpen, setCollapseOpen] = useState(props.isOpen)
28 |
29 | const handleHeaderClick = () => {
30 | setCollapseOpen(!isCollapseOpen)
31 | }
32 |
33 | return (
34 | <>
35 |
36 | {title}
37 | {isCollapseOpen ? : }
38 |
39 | {isCollapseOpen && {children}}
40 | >
41 | )
42 | }
43 |
44 | Collapse.propTypes = {
45 | header: PropTypes.node,
46 | title: PropTypes.node.isRequired,
47 | isOpen: PropTypes.bool,
48 | children: PropTypes.node,
49 | }
50 |
51 | Collapse.defaultProps = {
52 | header: null,
53 | children: undefined,
54 | isOpen: false,
55 | }
56 |
57 | export default Collapse
58 |
--------------------------------------------------------------------------------
/ui/src/components/empty-state/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import styled from 'styled-components'
4 | import { illustrations } from '_assets/'
5 | import Box from '_components/box'
6 | import Text from '_components/text'
7 |
8 | const EmptyStateContainer = styled(Box).attrs({
9 | border: 'thin',
10 | })`
11 | > svg {
12 | height: 160px;
13 | width: auto;
14 | }
15 | padding: 48px;
16 | flex-direction: column;
17 | align-items: center;
18 | justify-content: center;
19 | background-color: ${(props) => props.theme.colors.white};
20 | border-color: ${(props) => props.theme.colors.neutralLighter};
21 | `
22 |
23 | const EmptyState = ({ image, title, description, extra, ...props }) => (
24 |
25 | {image}
26 |
27 | {title}
28 |
29 |
30 | {description}
31 |
32 | {extra}
33 |
34 | )
35 |
36 | EmptyState.propTypes = {
37 | image: PropTypes.node,
38 | title: PropTypes.string,
39 | description: PropTypes.string,
40 | extra: PropTypes.node,
41 | }
42 |
43 | EmptyState.defaultProps = {
44 | image: ,
45 | title: 'No results found',
46 | description: `Oops! It seems that this query has not returned any resources.`,
47 | extra: null,
48 | }
49 |
50 | export default EmptyState
51 |
--------------------------------------------------------------------------------
/ui/src/components/error-state/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import styled from 'styled-components'
4 | import { illustrations } from '_assets/'
5 | import Box from '_components/box'
6 | import Text from '_components/text'
7 |
8 | const ErrorStateContainer = styled(Box)`
9 | svg {
10 | height: 160px;
11 | width: auto;
12 | }
13 | padding: 20px;
14 | flex-direction: column;
15 | align-items: center;
16 | justify-content: center;
17 | background-color: ${(props) => props.theme.colors.background1};
18 | `
19 |
20 | const ErrorState = ({ title, description, extra }) => (
21 |
22 |
23 |
24 | {title}
25 |
26 |
27 | {description}
28 |
29 | {extra}
30 |
31 | )
32 |
33 | ErrorState.propTypes = {
34 | title: PropTypes.string,
35 | description: PropTypes.string,
36 | extra: PropTypes.node,
37 | }
38 |
39 | ErrorState.defaultProps = {
40 | title: 'Something is not right',
41 | description: 'It seems that an error has occurred. Check your network and try again.',
42 | extra: null,
43 | }
44 |
45 | export default ErrorState
46 |
--------------------------------------------------------------------------------
/ui/src/components/flex/index.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { flex, layout, grid } from 'styled-system'
3 |
4 | const Flex = styled.div`
5 | display: flex;
6 | ${grid}
7 | ${flex}
8 | ${layout}
9 | `
10 |
11 | export default Flex
12 |
--------------------------------------------------------------------------------
/ui/src/components/headers/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | import Avatar from '_components/avatar'
5 | import Text from '_components/text'
6 | import Box from '_components/box'
7 |
8 | const StyledBox = styled(Box).attrs({
9 | height: '120px',
10 | display: 'flex',
11 | alignItems: 'center',
12 | padding: 0,
13 | mb: 3,
14 | })`
15 | > :first-child {
16 | margin-right: 16px;
17 | }
18 | `
19 |
20 | const ProjectHeader = props => (
21 |
22 |
23 |
24 | some-project
25 | This projects does something really well
26 |
27 |
28 | )
29 |
30 | const NodeHeader = props => (
31 |
32 | xyZ761kgVK
33 | This projects does something really well
34 |
35 | )
36 |
37 | export { ProjectHeader, NodeHeader }
38 |
--------------------------------------------------------------------------------
/ui/src/components/icon/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled, { css } from 'styled-components'
4 | import { color, border } from 'styled-system'
5 |
6 | import Box from '_components/box'
7 |
8 | export const Container = styled(Box)`
9 | display: flex;
10 | align-items: center;
11 | justify-content: center;
12 |
13 | border: none;
14 | box-shadow: none;
15 | background: none;
16 | box-sizing: border-box;
17 | outline: none;
18 |
19 | flex-shrink: 0;
20 | width: ${(props) => props.size};
21 | height: ${(props) => props.size};
22 |
23 | svg {
24 | z-index: 1;
25 | width: 100%;
26 | height: auto;
27 | ${(props) =>
28 | props.color &&
29 | css`
30 | path {
31 | fill: ${props.theme.colors[props.color]};
32 | }
33 | `}
34 | }
35 | ${border}
36 | ${color}
37 | `
38 |
39 | const Icon = ({ icon, ...props }) => {icon}
40 |
41 | Icon.propTypes = {
42 | icon: PropTypes.node,
43 | size: PropTypes.string,
44 | color: PropTypes.string,
45 | }
46 |
47 | Icon.defaultProps = {
48 | icon: undefined,
49 | color: undefined,
50 | size: '24px',
51 | }
52 |
53 | export default Icon
54 |
--------------------------------------------------------------------------------
/ui/src/components/inputs/number-input/index.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { layout, space } from 'styled-system'
3 |
4 | const NumberInput = styled.input.attrs({
5 | type: 'number',
6 | })`
7 | box-sizing: border-box;
8 |
9 | border: none;
10 | border-bottom: 1px solid ${(props) => props.theme.colors.neutralLighter};
11 |
12 | padding-bottom: 4px;
13 |
14 | font-family: Lato;
15 | font-size: 16px;
16 |
17 | width: 100%;
18 |
19 | :focus {
20 | border-bottom: 1px solid ${(props) => props.theme.colors.primary};
21 | }
22 | :disabled {
23 | background: inherit;
24 | opacity: 0.5;
25 | border: none;
26 | }
27 | :invalid {
28 | border-bottom: 1px solid ${(props) => props.theme.colors.danger};
29 | }
30 | ::placeholder {
31 | color: ${(props) => props.theme.colors.neutralLight};
32 | }
33 |
34 | ::-webkit-outer-spin-button,
35 | ::-webkit-inner-spin-button {
36 | -webkit-appearance: none;
37 | margin: 0;
38 | }
39 |
40 | input[type='number'] {
41 | -moz-appearance: textfield; /* Firefox */
42 | }
43 |
44 | ${space}
45 | ${layout}
46 | `
47 |
48 | export default NumberInput
49 |
--------------------------------------------------------------------------------
/ui/src/components/inputs/text-input/index.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { border, layout, space } from 'styled-system'
3 |
4 | const TextInput = styled.input.attrs({
5 | type: 'text',
6 | autocomplete: 'off',
7 | })`
8 | box-sizing: border-box;
9 |
10 | border: none;
11 | border-bottom: 1px solid ${(props) => props.theme.colors.neutralLighter};
12 |
13 | padding-bottom: 4px;
14 |
15 | background-color: ${(props) => props.theme.colors.white};
16 | color: ${(props) => props.theme.colors.neutralDarker};
17 |
18 | font-family: Lato;
19 | font-size: 16px;
20 |
21 | width: 100%;
22 | ::placeholder {
23 | color: ${(props) => props.theme.colors.neutralLight};
24 | }
25 | :focus {
26 | border-bottom: 1px solid ${(props) => props.theme.colors.primary};
27 | }
28 | :disabled {
29 | opacity: 0.5;
30 | cursor: not-allowed;
31 | border: none;
32 | }
33 | :invalid {
34 | border-bottom: 1px solid ${(props) => props.theme.colors.danger};
35 | }
36 |
37 | ${border}
38 | ${space}
39 | ${layout}
40 | `
41 |
42 | export default TextInput
43 |
--------------------------------------------------------------------------------
/ui/src/components/link/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import styled from 'styled-components'
3 | import { color, typography, border, space, layout } from 'styled-system'
4 |
5 | import { Link } from '@reach/router'
6 |
7 | const StyledLink = styled(Link)`
8 | display: inline;
9 |
10 | font-family: Lato !important;
11 |
12 | :link {
13 | text-decoration: none;
14 | cursor: pointer;
15 | }
16 |
17 | :visited {
18 | text-decoration: inherit;
19 | cursor: auto;
20 | }
21 |
22 | color: inherit;
23 |
24 | :hover {
25 | ${(props) => props.hoverStyle}
26 | }
27 |
28 | &[aria-current] {
29 | ${(props) => props.activeStyle}
30 | }
31 |
32 | ${color}
33 | ${space}
34 | ${layout}
35 | ${border}
36 | ${typography}
37 | `
38 |
39 | StyledLink.propTypes = {
40 | href: PropTypes.string,
41 | to: PropTypes.string.isRequired,
42 | }
43 |
44 | export default StyledLink
45 |
--------------------------------------------------------------------------------
/ui/src/components/list/index.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { layout, space, flexbox } from 'styled-system'
3 |
4 | const List = styled.ul`
5 | overflow: hidden;
6 | overflow-y: auto;
7 |
8 | ::-webkit-scrollbar {
9 | background: transparent;
10 | width: 4px;
11 | height: 4px;
12 | }
13 |
14 | ::-webkit-scrollbar-button {
15 | display: none;
16 | }
17 |
18 | ::-webkit-scrollbar-track {
19 | background-color: transparent;
20 | }
21 |
22 | ::-webkit-scrollbar-track-piece {
23 | background-color: transparent;
24 | }
25 |
26 | ::-webkit-scrollbar-thumb {
27 | background-color: #ececf0;
28 | border-radius: 2px;
29 | }
30 |
31 | ::-webkit-scrollbar-corner {
32 | display: none;
33 | }
34 |
35 | ::-webkit-resizer {
36 | display: none;
37 | }
38 |
39 | ${flexbox}
40 | ${layout}
41 | ${space}
42 | `
43 |
44 | export default List
45 |
--------------------------------------------------------------------------------
/ui/src/components/nav/index.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | import Box from '_components/box'
4 | import Link from '_components/link'
5 |
6 | export const Nav = styled(Box).attrs({
7 | width: '100%',
8 | borderBottom: 'medium',
9 | borderColor: 'neutralLighter',
10 | })``
11 |
12 | export const HorizontalNavLink = styled(Link).attrs(({ theme }) => ({
13 | py: 3,
14 | px: 2,
15 | color: 'neutral',
16 | fontSize: '14px',
17 | fontWeight: '500',
18 | marginBottom: '-2px',
19 | activeStyle: {
20 | color: theme.colors.primaryDark,
21 | 'border-bottom': `2px solid ${theme.colors.primary}`,
22 | },
23 | hoverStyle: { 'border-bottom': `2px solid ${theme.colors.neutralDark}` },
24 | }))`
25 | :first-child {
26 | margin-left: 0;
27 | }
28 | `
29 |
30 | export const VerticalNavLink = styled(Link).attrs((props) => ({
31 | py: 2,
32 | color: 'neutral',
33 | activeStyle: { color: props.theme.colors.primary },
34 | }))``
35 |
--------------------------------------------------------------------------------
/ui/src/components/separator/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import styled, { css } from 'styled-components'
3 | import { color, layout, space } from 'styled-system'
4 |
5 | const Separator = styled.span`
6 | ${(props) =>
7 | props.vertical
8 | ? css`
9 | width: 1px;
10 | height: 100%;
11 | `
12 | : css`
13 | height: 1px;
14 | width: 100%;
15 | `};
16 | display: block;
17 | ${layout}
18 | ${space}
19 | ${color}
20 | `
21 |
22 | Separator.propTypes = {
23 | vertical: PropTypes.bool,
24 | }
25 |
26 | Separator.defaultProps = {
27 | vertical: false,
28 | bg: 'neutralLighter',
29 | }
30 |
31 | export default Separator
32 |
--------------------------------------------------------------------------------
/ui/src/components/spinner/index.js:
--------------------------------------------------------------------------------
1 | import Jellyfish from './jellyfish'
2 | import Dragon from './dragon'
3 |
4 | export { Dragon, Jellyfish }
5 |
--------------------------------------------------------------------------------
/ui/src/components/steps/index.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { space, layout, grid, flex } from 'styled-system'
3 |
4 | export const StepsContainer = styled.div`
5 | display: flex;
6 | counter-reset: step-counter;
7 |
8 | > * {
9 | opacity: 0.3;
10 | :nth-child(-n+${props => props.currentStep}){
11 | opacity: 1;
12 | }
13 | }
14 |
15 | ${space}
16 | ${layout}
17 | ${grid}
18 | ${flex}
19 | `
20 |
21 | export const Step = styled.p`
22 | color: ${props => props.theme.colors.primary};
23 | display: flex;
24 | align-items: center;
25 | :before {
26 | content: counter(step-counter);
27 | display: flex;
28 | align-items: center;
29 | justify-content: center;
30 | margin-right: 8px;
31 | border-radius: 50%;
32 | counter-increment: step-counter;
33 | height: 28px;
34 | width: 28px;
35 | border: 1px solid ${props => props.theme.colors.primary};
36 | }
37 | z-index: -1;
38 | `
39 |
--------------------------------------------------------------------------------
/ui/src/components/text/index.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { color, fontSize, fontWeight, layout, space, textAlign, textStyle } from 'styled-system'
3 |
4 | const Text = styled.div`
5 | strong {
6 | font-weight: bold;
7 | }
8 | ${textStyle}
9 | ${textAlign}
10 | ${space}
11 | ${layout}
12 | ${fontSize}
13 | ${fontWeight}
14 | ${color}
15 | `
16 |
17 | export default Text
18 |
--------------------------------------------------------------------------------
/ui/src/components/tooltip/index.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/components/tooltip/index.js
--------------------------------------------------------------------------------
/ui/src/components/unauthorized-state/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled from 'styled-components'
4 |
5 | import Box from '_components/box'
6 | import Text from '_components/text'
7 |
8 | import { illustrations } from '_assets/'
9 |
10 | const UnaurhorizedStateContainer = styled(Box).attrs({
11 | border: 'none',
12 | height: '300px',
13 | })`
14 | svg {
15 | height: 300px;
16 | width: auto;
17 | }
18 | padding: 20px;
19 | flex-direction: column;
20 | align-items: center;
21 | justify-content: center;
22 | `
23 |
24 | const UnaurhorizedState = ({ title, description }) => (
25 |
26 |
27 |
28 | {title}
29 |
30 |
31 | {description}
32 |
33 |
34 | )
35 |
36 | UnaurhorizedState.propTypes = {
37 | title: PropTypes.string,
38 | description: PropTypes.string,
39 | }
40 |
41 | UnaurhorizedState.defaultProps = {
42 | title: 'Forbidden',
43 | description: `Oops! It seems that you don't have enough permissions to access this resource`,
44 | }
45 |
46 | export default UnaurhorizedState
47 |
--------------------------------------------------------------------------------
/ui/src/containers/footer/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import { grid, space, color } from 'styled-system'
4 |
5 | const Container = styled.div`
6 | background: white;
7 | border-top: 1px solid #f1f1f1;
8 |
9 | display: flex;
10 | justify-content: center;
11 |
12 | align-items: center;
13 | * + * {
14 | margin-left: 12px;
15 | }
16 | ${space}
17 | ${grid}
18 |
19 | z-index: 99;
20 | `
21 |
22 | const StyledLink = styled.a`
23 | font-size: 14px;
24 | text-decoration: none;
25 | &:hover {
26 | color: ${(props) => props.theme.colors.primary};
27 | }
28 | ${color}
29 | `
30 |
31 | StyledLink.defaultProps = {
32 | color: 'neutralDarker',
33 | }
34 |
35 | const Footer = (props) => (
36 |
37 | Support
38 | Docs
39 |
40 | )
41 |
42 | Footer.defaultProps = {
43 | padding: 0,
44 | }
45 |
46 | export default Footer
47 |
--------------------------------------------------------------------------------
/ui/src/containers/header/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import { grid, space, color, shadow, border } from 'styled-system'
4 |
5 | import Brand from '_containers/side-nav/brand'
6 | import Flex from '_components/flex'
7 | import Link from '_components/link'
8 |
9 | export const Container = styled.div`
10 | display: flex;
11 |
12 | align-items: center;
13 | justify-content: space-between;
14 |
15 | border-bottom: 1px solid ${(props) => props.theme.colors.neutralLighter};
16 | position: fixed;
17 |
18 | top: 0;
19 | right: 0;
20 | left: 0;
21 |
22 | z-index: 199;
23 |
24 | ${border}
25 | ${shadow}
26 | ${color}
27 | ${space}
28 | ${grid}
29 | `
30 |
31 | Container.defaultProps = {
32 | backgroundColor: 'white',
33 | boxShadow: 'light',
34 | border: 'dark',
35 | }
36 |
37 | const StyledLink = styled(Link)`
38 | margin: auto;
39 | padding-right: 18px;
40 | :hover {
41 | color: ${({ theme }) => theme.colors.neutralDarker};
42 | }
43 | `
44 |
45 | const Header = (props) => (
46 |
47 |
48 |
49 |
50 | ACL Tokens
51 |
52 |
53 |
54 | )
55 |
56 | Header.defaultProps = {
57 | padding: 3,
58 | }
59 |
60 | export default Header
61 |
--------------------------------------------------------------------------------
/ui/src/containers/side-nav/brand.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import { colorStyle } from 'styled-system'
4 | import { Link } from '@reach/router'
5 | import { ReactComponent as Logo } from '_assets/icons/logo.svg'
6 |
7 | const StyledLink = styled(Link)`
8 | position: relative;
9 |
10 | height: auto;
11 |
12 | display: flex;
13 | align-items: center;
14 |
15 | svg {
16 | fill: ${(props) => props.theme.colors.primary};
17 | opacity: 0.3;
18 | }
19 | &:hover {
20 | svg {
21 | opacity: 1;
22 | transition: all 0.4s linear;
23 | }
24 | }
25 | ${colorStyle}
26 | `
27 |
28 | const Brand = (props) => (
29 |
30 |
31 |
32 | )
33 |
34 | export default Brand
35 |
--------------------------------------------------------------------------------
/ui/src/containers/side-nav/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { CollapsibleSection, Container, NavLink } from './styled'
3 |
4 | const SideNav = (props) => (
5 |
6 |
7 | Networks
8 | Clients
9 |
10 |
11 | )
12 |
13 | SideNav.defaultProps = {
14 | colors: 'darkHighContrast',
15 | }
16 |
17 | export default SideNav
18 |
--------------------------------------------------------------------------------
/ui/src/environment.js:
--------------------------------------------------------------------------------
1 | export const DEBUG = process.env.REACT_APP_DEBUG || false
2 |
3 | export const REST_API_URL = process.env.REACT_APP_REST_API_URL || 'http://localhost:8080'
4 |
--------------------------------------------------------------------------------
/ui/src/graphql/local-state.js:
--------------------------------------------------------------------------------
1 | const defaults = {
2 | data: {
3 | toggleState: true,
4 | },
5 | }
6 |
7 | const resolvers = {
8 | Query: {},
9 | Mutation: {},
10 | }
11 |
12 | export { defaults, resolvers }
13 |
--------------------------------------------------------------------------------
/ui/src/index.js:
--------------------------------------------------------------------------------
1 | import { Router } from '@reach/router'
2 | import React from 'react'
3 | import ReactDOM from 'react-dom'
4 | import styled, { ThemeProvider } from 'styled-components'
5 | import { BaseModalBackground, ModalProvider } from 'styled-react-modal'
6 | import ConfirmationDialogProvider from '_components/confirmation-dialog'
7 | import ApolloProvider from '_graphql/apollo-provider'
8 | import ToastProvider from '_utils/toast-provider'
9 | import App from '_views/app'
10 | import NotFound from '_views/not-found'
11 | import * as serviceWorker from './serviceWorker'
12 | import { GlobalStyles, themes } from './styles'
13 | import './assets/fonts/index.css'
14 |
15 | const theme = 'light'
16 |
17 | const ModalBackground = styled(BaseModalBackground)`
18 | z-index: 999;
19 | `
20 |
21 | ReactDOM.render(
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | ,
37 | document.getElementById('root')
38 | )
39 |
40 | serviceWorker.unregister()
41 |
--------------------------------------------------------------------------------
/ui/src/mock/commands.js:
--------------------------------------------------------------------------------
1 | import { handler } from './util'
2 |
3 | export default {
4 | createNetwork: handler(() => null),
5 | deleteNetwork: handler(() => null),
6 | }
7 |
--------------------------------------------------------------------------------
/ui/src/mock/factories.js:
--------------------------------------------------------------------------------
1 | import faker from 'faker'
2 | import { Factory, trait } from 'miragejs'
3 |
4 | export default {
5 | token: Factory.extend({
6 | withTime: trait({
7 | createdAt: () => faker.date.recent(),
8 | updatedAt: () => faker.date.recent(),
9 | }),
10 | }),
11 | network: Factory.extend({
12 | withTime: trait({
13 | createdAt: () => faker.date.recent(),
14 | updatedAt: () => faker.date.recent(),
15 | }),
16 | }),
17 | node: Factory.extend({
18 | withTime: trait({
19 | createdAt: () => faker.date.recent(),
20 | updatedAt: () => faker.date.recent(),
21 | }),
22 | }),
23 | interface: Factory.extend({
24 | withTime: trait({
25 | createdAt: () => faker.date.recent(),
26 | updatedAt: () => faker.date.recent(),
27 | }),
28 | }),
29 | connection: Factory.extend({
30 | withTime: trait({
31 | createdAt: () => faker.date.recent(),
32 | updatedAt: () => faker.date.recent(),
33 | }),
34 | }),
35 | }
36 |
--------------------------------------------------------------------------------
/ui/src/mock/identity.js:
--------------------------------------------------------------------------------
1 | import { v4 as uuidv4 } from 'uuid'
2 |
3 | export default class {
4 | constructor() {
5 | this.ids = new Set()
6 | }
7 |
8 | // Returns a new unused unique identifier.
9 | fetch() {
10 | let uuid = uuidv4()
11 | while (this.ids.has(uuid)) {
12 | uuid = uuidv4()
13 | }
14 |
15 | this.ids.add(uuid)
16 |
17 | return uuid
18 | }
19 |
20 | // Registers an identifier as used. Must throw if identifier is already used.
21 | set(id) {
22 | if (this.ids.has(id)) {
23 | throw new Error(`ID ${id} has already been used.`)
24 | }
25 |
26 | this.ids.add(id)
27 | }
28 |
29 | // Resets all used identifiers to unused.
30 | reset() {
31 | this.ids.clear()
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/ui/src/mock/index.js:
--------------------------------------------------------------------------------
1 | import { createServer, RestSerializer } from 'miragejs'
2 |
3 | import seeds from './seeds'
4 | import models from './models'
5 | import routes from './routes'
6 | import factories from './factories'
7 |
8 | import IdentityManager from './identity'
9 |
10 | export default () => {
11 | createServer({
12 | identityManagers: {
13 | application: IdentityManager,
14 | },
15 | serializers: {
16 | application: RestSerializer,
17 | },
18 | models,
19 | seeds,
20 | routes() {
21 | routes(this)
22 | },
23 | factories,
24 | })
25 | }
26 |
--------------------------------------------------------------------------------
/ui/src/mock/models.js:
--------------------------------------------------------------------------------
1 | import { Model, belongsTo, hasMany } from 'miragejs'
2 |
3 | export default {
4 | networks: Model.extend({}),
5 |
6 | nodes: Model.extend({
7 | interfaces: hasMany('interface'),
8 | }),
9 |
10 | interfaces: Model.extend({
11 | node: belongsTo('node'),
12 | network: belongsTo('network'),
13 | connections: hasMany('connection'),
14 | }),
15 |
16 | connections: Model.extend({
17 | from: belongsTo('interface'),
18 | to: belongsTo('interface'),
19 | }),
20 | }
21 |
--------------------------------------------------------------------------------
/ui/src/mock/queries.js:
--------------------------------------------------------------------------------
1 | import { handler } from './util'
2 |
3 | export default {
4 | getSelf: handler((schema, request, self) => self.user.attrs),
5 | }
6 |
--------------------------------------------------------------------------------
/ui/src/mock/routes.js:
--------------------------------------------------------------------------------
1 | import { REST_API_URL } from '../environment'
2 | import C from './commands'
3 | import Q from './queries'
4 |
5 | const routes = (server) => {
6 | const get = (path, handler) => server.get(`${REST_API_URL}${path}`, handler, { timing: 1000 })
7 | const post = (path, handler) => server.post(`${REST_API_URL}${path}`, handler, { timing: 2000 })
8 |
9 | // Setup query routes
10 | get('/api/self/token', Q.getSelf)
11 |
12 | // Setup command routes
13 | post('/api/networks/', C.createNetwork)
14 |
15 | // Setup passthrough routes (to which requests will not be intercepted by Mirage)
16 | // server.passthrough()
17 | }
18 |
19 | export default routes
20 |
--------------------------------------------------------------------------------
/ui/src/mock/seeds.js:
--------------------------------------------------------------------------------
1 | export default (server) => {
2 | const create = (...args) => server.create(...args, 'withTime')
3 | const networks = {
4 | n1: create('network', { name: 'net1' }),
5 | n2: create('network', { name: 'net2' }),
6 | }
7 | const nodes = {
8 | n1: create('node', { name: 'node1' }),
9 | n2: create('node', { name: 'node2' }),
10 | }
11 | const interfaces = {
12 | i1: create('interface', { name: 'wg0', node: nodes.n1, network: networks.n1 }),
13 | i2: create('interface', { name: 'wg0', node: nodes.n2, network: networks.n2 }),
14 | }
15 | // eslint-disable-next-line no-unused-vars
16 | const connections = {
17 | c1: create('interface', { from: interfaces.i1, to: interfaces.i2 }),
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ui/src/mock/util.js:
--------------------------------------------------------------------------------
1 | export const handler = (f) => (schema, request) => {
2 | const context = {}
3 | const self = {}
4 | request.body = JSON.parse(request.requestBody)
5 | request.headers = request.requestHeaders
6 | return f(schema, request, self, context)
7 | }
8 |
9 | export default {}
10 |
--------------------------------------------------------------------------------
/ui/src/styles/index.js:
--------------------------------------------------------------------------------
1 | import reset from 'styled-reset'
2 | import { variant } from 'styled-system'
3 | import { createGlobalStyle } from 'styled-components'
4 |
5 | import lightTheme from './themes/light'
6 |
7 | const GlobalStyles = createGlobalStyle`
8 | ${reset}
9 |
10 | :root {
11 | height: 100%;
12 | font-family: Lato;
13 | text-rendering: optimizeLegibility;
14 | outline: none;
15 | }
16 |
17 | body {
18 | height: 100%;
19 | }
20 |
21 | body::-webkit-scrollbar {
22 | width: 8px;
23 | height: 8px;
24 | background-color: ${(props) => props.theme.colors.background3};
25 | }
26 |
27 | body::-webkit-scrollbar-track {
28 | border-radius: 4px;
29 | background-color: ${(props) => props.theme.colors.background3};
30 | }
31 |
32 | body::-webkit-scrollbar-thumb {
33 | border-radius: 4px;
34 | background-color: ${(props) => props.theme.colors.border1};
35 | }
36 |
37 | * {
38 | outline: none;
39 | -webkit-tap-highlight-color: transparent;
40 | }
41 |
42 | button {
43 | cursor: pointer;
44 | }
45 | `
46 |
47 | const containers = variant({
48 | scale: 'containers',
49 | prop: 'type',
50 | })
51 |
52 | const themes = { light: lightTheme }
53 |
54 | export { GlobalStyles, containers, themes }
55 |
--------------------------------------------------------------------------------
/ui/src/utils/formik-utils.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | import { Portal } from 'react-portal'
4 | import Box from '_components/box'
5 | import { DEBUG } from '../environment'
6 |
7 | const Container = styled(Box).attrs({
8 | border: 'discrete',
9 | })`
10 | padding: 12px;
11 | position: absolute;
12 | bottom: 40px;
13 | left: 20px;
14 | height: auto;
15 | `
16 |
17 | const FormikState = (props) =>
18 | DEBUG ? (
19 |
20 |
21 |
22 |
32 | {JSON.stringify(props, null, 4)}
33 |
34 |
35 |
36 |
37 | ) : null
38 |
39 | export default FormikState
40 |
--------------------------------------------------------------------------------
/ui/src/utils/hocs/index.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line import/prefer-default-export
2 | export { default as withValidityIndicator } from './with-validity-indicator'
3 |
--------------------------------------------------------------------------------
/ui/src/utils/hocs/with-validity-indicator.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import styled from 'styled-components'
4 | import { icons } from '_assets/'
5 | import Box from '_components/box'
6 | import Popover from '_components/popover'
7 | import Text from '_components/text'
8 |
9 | const ErrorIconContainer = styled.div`
10 | position: absolute;
11 | right: -28px;
12 | top: 50%;
13 | transform: translateY(-50%);
14 | `
15 |
16 | const ErrorTooltip = styled.div`
17 | padding: 8px;
18 | max-width: 180px;
19 | background: #fff;
20 | border-radius: 4px;
21 | `
22 |
23 | const ErrorIndicator = ({ error }) => {
24 | if (error === undefined) return null
25 | return (
26 |
27 |
31 |
32 | {error}
33 |
34 |
35 | }
36 | >
37 |
38 |
39 |
40 | )
41 | }
42 |
43 | ErrorIndicator.propTypes = {
44 | error: PropTypes.string,
45 | }
46 |
47 | ErrorIndicator.defaultProps = {
48 | error: undefined,
49 | }
50 |
51 | const withValidityIndicator = (input, error) => (
52 |
53 | {input}
54 |
55 |
56 | )
57 |
58 | export default withValidityIndicator
59 |
--------------------------------------------------------------------------------
/ui/src/utils/toast-provider.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React, { useContext } from 'react'
3 | import { toast } from 'react-toastify'
4 |
5 | import { icons } from '_assets/'
6 |
7 | import {
8 | ToastContainer,
9 | ToastBody,
10 | IconContainer,
11 | DismissButton,
12 | ToastContent,
13 | } from '_components/toast'
14 |
15 | const displayToast = (content, icon, color, options = {}) => {
16 | toast(
17 | ({ closeToast }) => (
18 |
19 | {icon}
20 | {content}
21 | } color={color} onClick={() => closeToast()} />
22 |
23 | ),
24 | { ...options, autoClose: 3000, closeButton: false }
25 | )
26 | }
27 |
28 | const ToastContext = React.createContext()
29 |
30 | export const useToast = () => useContext(ToastContext)
31 |
32 | const ToastProvider = ({ children }) => (
33 | <>
34 | displayToast(text, icon, color, options),
37 | error: (text, options) => displayToast(text, , 'danger', options),
38 | warning: (text, options) => displayToast(text, , 'warning', options),
39 | success: (text, options) => displayToast(text, , 'success', options),
40 | }}
41 | >
42 | {children}
43 |
44 |
45 | >
46 | )
47 |
48 | ToastProvider.propTypes = {
49 | children: PropTypes.node.isRequired,
50 | }
51 |
52 | export default ToastProvider
53 |
--------------------------------------------------------------------------------
/ui/src/views/app/index.js:
--------------------------------------------------------------------------------
1 | import { Router } from '@reach/router'
2 | import React from 'react'
3 | import styled from 'styled-components'
4 | import Footer from '_containers/footer'
5 | import Header from '_containers/header'
6 | import SideNav from '_containers/side-nav'
7 | import ClientsRouter from '_views/clients'
8 | import HomeView from '_views/home'
9 | import NetworksRouter from '_views/networks'
10 | import NotFound from '_views/not-found'
11 | import SettingsRouter from '_views/settings'
12 |
13 | const Dashboard = styled.div`
14 | position: relative;
15 | display: grid;
16 | height: 100vh;
17 | grid-template: 72px auto 40px / auto;
18 | grid-template-areas:
19 | 'header'
20 | 'body'
21 | 'footer';
22 | `
23 |
24 | const Content = styled(Router).attrs({ primary: false })`
25 | padding-top: 84px;
26 | padding-bottom: 32px;
27 |
28 | grid-area: body;
29 |
30 | width: 90%;
31 | max-width: 800px;
32 | justify-self: center;
33 |
34 | // Laptops and above
35 | @media (min-width: 1280px) {
36 | padding-left: 200px;
37 | }
38 | `
39 |
40 | const App = () => (
41 |
42 |
43 |
44 |
45 |
46 |
47 | {/* */}
48 | {/* */}
49 |
50 |
51 |
52 |
53 |
54 |
55 | )
56 | export default App
57 |
--------------------------------------------------------------------------------
/ui/src/views/clients/index.js:
--------------------------------------------------------------------------------
1 | import { Router } from '@reach/router'
2 | import React from 'react'
3 | import Details from './details'
4 | import List from './list'
5 |
6 | const ClientsRouter = () => (
7 |
8 |
9 |
10 |
11 | )
12 |
13 | export default ClientsRouter
14 |
--------------------------------------------------------------------------------
/ui/src/views/home/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { Redirect } from '@reach/router'
4 |
5 | const HomeView = () =>
6 |
7 | export default HomeView
8 |
--------------------------------------------------------------------------------
/ui/src/views/networks/details/topology.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seashell/drago/9bc3763434c73a7f95a73ec69ad4bae2ed10ff08/ui/src/views/networks/details/topology.js
--------------------------------------------------------------------------------
/ui/src/views/networks/index.js:
--------------------------------------------------------------------------------
1 | import { Router } from '@reach/router'
2 | import React from 'react'
3 | import Details from './details'
4 | import List from './list'
5 | import New from './new'
6 |
7 | const NetworksRouter = (props) => (
8 |
9 |
10 |
11 |
12 |
13 | )
14 |
15 | export default NetworksRouter
16 |
--------------------------------------------------------------------------------
/ui/src/views/not-found/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | import Box from '_components/box'
5 | import Text from '_components/text'
6 | import { illustrations } from '_assets/'
7 | import Flex from '_components/flex'
8 |
9 | const StyledBox = styled(Box).attrs({
10 | border: '',
11 | height: 'auto',
12 | })`
13 | svg {
14 | height: 300px;
15 | width: auto;
16 | }
17 | padding: 20px;
18 | flex-direction: column;
19 | align-items: center;
20 | justify-content: center;
21 | `
22 |
23 | const Container = styled(Flex)`
24 | flex-direction: column;
25 | `
26 | const NotFound = () => (
27 |
28 |
29 |
30 |
31 | Page not found
32 |
33 |
34 | Oops! It seems that the page you are trying to access does not exist or has been moved.
35 |
36 |
37 |
38 | )
39 |
40 | export default NotFound
41 |
--------------------------------------------------------------------------------
/ui/src/views/settings/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Router } from '@reach/router'
3 |
4 | import Tokens from './tokens'
5 |
6 | const SettingsRouter = (props) => (
7 |
8 |
9 |
10 | )
11 |
12 | export default SettingsRouter
13 |
--------------------------------------------------------------------------------