diff --git a/generator/api-back.mdx b/generator/api-back.mdx new file mode 100644 index 00000000..6e2d24d2 --- /dev/null +++ b/generator/api-back.mdx @@ -0,0 +1,5333 @@ +--- +id: usage +slug: /usage +title: Usage +toc_max_heading_level: 3 +--- + + + + +API to manipulate groups, rules, policies and retrieve information about peers and users + + + + + + +## Users [#users] + +Interact with and view information about users. + + + + + +### Retrieve Users [#retrieve-users] + +``` +GET https://api.netbird.io/api/users +``` + + + + + + + + + + + + + + +#### Responses + + + + + +A JSON array of Users + + + + +```json +{ + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Create a User [#create-a-user] + +``` +POST https://api.netbird.io/api/users +``` + + + + + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "$ref": "#/components/schemas/UserCreateRequest" + } +} +``` + + + + +#### Responses + + + + + +A User object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/User" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Update a User [#update-a-user] + +``` +PUT https://api.netbird.io/api/users/{userId} +``` + + + + +#### Path Parameters + + + + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "$ref": "#/components/schemas/UserRequest" + } +} +``` + + + + +#### Responses + + + + + +A User object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/User" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Delete a User [#delete-a-user] + +``` +DELETE https://api.netbird.io/api/users/{userId} +``` + + + + +#### Path Parameters + + + + + + + + + + + + + +#### Responses + + + + + +Delete status code + + + + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### List all Tokens [#list-all-tokens] + +``` +GET https://api.netbird.io/api/users/{userId}/tokens +``` + + + + +#### Path Parameters + + + + + + + + + + + + + +#### Responses + + + + + +A JSON Array of PersonalAccessTokens + + + + +```json +{ + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PersonalAccessToken" + } + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Create a Token [#create-a-token] + +``` +POST https://api.netbird.io/api/users/{userId}/tokens +``` + + + + +#### Path Parameters + + + + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "$ref": "#/components/schemas/PersonalAccessTokenRequest" + } +} +``` + + + + +#### Responses + + + + + +The token in plain text + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/PersonalAccessTokenGenerated" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Retrieve a Token [#retrieve-a-token] + +``` +GET https://api.netbird.io/api/users/{userId}/tokens/{tokenId} +``` + + + + +#### Path Parameters + + + + + + + + + + + + + +#### Responses + + + + + +A PersonalAccessTokens Object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/PersonalAccessToken" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Delete a Token [#delete-a-token] + +``` +DELETE https://api.netbird.io/api/users/{userId}/tokens/{tokenId} +``` + + + + +#### Path Parameters + + + + + + + + + + + + + +#### Responses + + + + + +Delete status code + + + + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + +## Peers [#peers] + +Interact with and view information about peers. + + + + + +### List all Peers [#list-all-peers] + +``` +GET https://api.netbird.io/api/peers +``` + + + + + + + + + + + + + + +#### Responses + + + + + +A JSON Array of Peers + + + + +```json +{ + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Peer" + } + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Retrieve a Peer [#retrieve-a-peer] + +``` +GET https://api.netbird.io/api/peers/{peerId} +``` + + + + +#### Path Parameters + + + + + + + + + + + + + +#### Responses + + + + + +A Peer object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/Peer" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Update a Peer [#update-a-peer] + +``` +PUT https://api.netbird.io/api/peers/{peerId} +``` + + + + +#### Path Parameters + + + + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "$ref": "#/components/schemas/peers_peerId_body" + } +} +``` + + + + +#### Responses + + + + + +A Peer object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/Peer" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Delete a Peer [#delete-a-peer] + +``` +DELETE https://api.netbird.io/api/peers/{peerId} +``` + + + + +#### Path Parameters + + + + + + + + + + + + + +#### Responses + + + + + +Delete status code + + + + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + +## Setup Keys [#setup-keys] + +Interact with and view information about setup keys. + + + + + +### List all Setup Keys [#list-all-setup-keys] + +``` +GET https://api.netbird.io/api/setup-keys +``` + + + + + + + + + + + + + + +#### Responses + + + + + +A JSON Array of Setup keys + + + + +```json +{ + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SetupKey" + } + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Create a Setup Key [#create-a-setup-key] + +``` +POST https://api.netbird.io/api/setup-keys +``` + + + + + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "$ref": "#/components/schemas/SetupKeyRequest" + } +} +``` + + + + +#### Responses + + + + + +A Setup Keys Object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/SetupKey" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Retrieve a Setup Key [#retrieve-a-setup-key] + +``` +GET https://api.netbird.io/api/setup-keys/{keyId} +``` + + + + +#### Path Parameters + + + + + + + + + + + + + +#### Responses + + + + + +A Setup Key object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/SetupKey" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Update a Setup Key [#update-a-setup-key] + +``` +PUT https://api.netbird.io/api/setup-keys/{keyId} +``` + + + + +#### Path Parameters + + + + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "$ref": "#/components/schemas/SetupKeyRequest" + } +} +``` + + + + +#### Responses + + + + + +A Setup Key object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/SetupKey" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + +## Groups [#groups] + +Interact with and view information about groups. + + + + + +### List all Groups [#list-all-groups] + +``` +GET https://api.netbird.io/api/groups +``` + + + + + + + + + + + + + + +#### Responses + + + + + +A JSON Array of Groups + + + + +```json +{ + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Group" + } + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Create a Group [#create-a-group] + +``` +POST https://api.netbird.io/api/groups +``` + + + + + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "$ref": "#/components/schemas/api_groups_body" + } +} +``` + + + + +#### Responses + + + + + +A Group Object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/Group" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Retrieve a Group [#retrieve-a-group] + +``` +GET https://api.netbird.io/api/groups/{groupId} +``` + + + + +#### Path Parameters + + + + + + + + + + + + + +#### Responses + + + + + +A Group object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/Group" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Update a Group [#update-a-group] + +``` +PUT https://api.netbird.io/api/groups/{groupId} +``` + + + + +#### Path Parameters + + + + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "$ref": "#/components/schemas/groups_groupId_body" + } +} +``` + + + + +#### Responses + + + + + +A Group object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/Group" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Delete a Group [#delete-a-group] + +``` +DELETE https://api.netbird.io/api/groups/{groupId} +``` + + + + +#### Path Parameters + + + + + + + + + + + + + +#### Responses + + + + + +Delete status code + + + + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + +## Rules [#rules] + +Interact with and view information about rules. + + + + + +### List all Rules [#list-all-rules] + +``` +GET https://api.netbird.io/api/rules +``` + + + + + + + + + + + + + + +#### Responses + + + + + +A JSON Array of Rules + + + + +```json +{ + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Rule" + } + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Create a Rule [#create-a-rule] + +``` +POST https://api.netbird.io/api/rules +``` + + + + + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "$ref": "#/components/schemas/api_rules_body" + } +} +``` + + + + +#### Responses + + + + + +A Rule Object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/Rule" + } +} +``` + + + + + + + + +
+ + + + +### Retrieve a Rule [#retrieve-a-rule] + +``` +GET https://api.netbird.io/api/rules/{ruleId} +``` + + + + +#### Path Parameters + + + + + + + + + + + + + +#### Responses + + + + + +A Rule object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/Rule" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Update a Rule [#update-a-rule] + +``` +PUT https://api.netbird.io/api/rules/{ruleId} +``` + + + + +#### Path Parameters + + + + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "$ref": "#/components/schemas/rules_ruleId_body" + } +} +``` + + + + +#### Responses + + + + + +A Rule object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/Rule" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Delete a Rule [#delete-a-rule] + +``` +DELETE https://api.netbird.io/api/rules/{ruleId} +``` + + + + +#### Path Parameters + + + + + + + + + + + + + +#### Responses + + + + + +Delete status code + + + + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + +## Policies [#policies] + +Interact with and view information about policies. + + + + + +### List all Policies [#list-all-policies] + +``` +GET https://api.netbird.io/api/policies +``` + + + + + + + + + + + + + + +#### Responses + + + + + +A JSON Array of Policies + + + + +```json +{ + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Policy" + } + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Create a Policy [#create-a-policy] + +``` +POST https://api.netbird.io/api/policies +``` + + + + + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "$ref": "#/components/schemas/api_policies_body" + } +} +``` + + + + +#### Responses + + + + + +A Policy Object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/Policy" + } +} +``` + + + + + + + + +
+ + + + +### Retrieve a Policy [#retrieve-a-policy] + +``` +GET https://api.netbird.io/api/policies/{policyId} +``` + + + + +#### Path Parameters + + + + + + + + + + + + + +#### Responses + + + + + +A Policy object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/Policy" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Update a Policy [#update-a-policy] + +``` +PUT https://api.netbird.io/api/policies/{policyId} +``` + + + + +#### Path Parameters + + + + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "$ref": "#/components/schemas/policies_policyId_body" + } +} +``` + + + + +#### Responses + + + + + +A Policy object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/Policy" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Delete a Policy [#delete-a-policy] + +``` +DELETE https://api.netbird.io/api/policies/{policyId} +``` + + + + +#### Path Parameters + + + + + + + + + + + + + +#### Responses + + + + + +Delete status code + + + + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + +## Routes [#routes] + +Interact with and view information about routes. + + + + + +### List all Routes [#list-all-routes] + +``` +GET https://api.netbird.io/api/routes +``` + + + + + + + + + + + + + + +#### Responses + + + + + +A JSON Array of Routes + + + + +```json +{ + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Route" + } + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Create a Route [#create-a-route] + +``` +POST https://api.netbird.io/api/routes +``` + + + + + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "$ref": "#/components/schemas/RouteRequest" + } +} +``` + + + + +#### Responses + + + + + +A Route Object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/Route" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Retrieve a Route [#retrieve-a-route] + +``` +GET https://api.netbird.io/api/routes/{routeId} +``` + + + + +#### Path Parameters + + + + + + + + + + + + + +#### Responses + + + + + +A Route object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/Route" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Update a Route [#update-a-route] + +``` +PUT https://api.netbird.io/api/routes/{routeId} +``` + + + + +#### Path Parameters + + + + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "$ref": "#/components/schemas/RouteRequest" + } +} +``` + + + + +#### Responses + + + + + +A Route object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/Route" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Delete a Route [#delete-a-route] + +``` +DELETE https://api.netbird.io/api/routes/{routeId} +``` + + + + +#### Path Parameters + + + + + + + + + + + + + +#### Responses + + + + + +Delete status code + + + + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + +## DNS [#dns] + +Interact with and view information about DNS configuration. + + + + + +### List all Nameserver Groups [#list-all-nameserver-groups] + +``` +GET https://api.netbird.io/api/dns/nameservers +``` + + + + + + + + + + + + + + +#### Responses + + + + + +A JSON Array of Nameserver Groups + + + + +```json +{ + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NameserverGroup" + } + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Create a Nameserver Group [#create-a-nameserver-group] + +``` +POST https://api.netbird.io/api/dns/nameservers +``` + + + + + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "$ref": "#/components/schemas/NameserverGroupRequest" + } +} +``` + + + + +#### Responses + + + + + +A Nameserver Groups Object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/NameserverGroup" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Retrieve a Nameserver Group [#retrieve-a-nameserver-group] + +``` +GET https://api.netbird.io/api/dns/nameservers/{nsgroupId} +``` + + + + +#### Path Parameters + + + + + + + + + + + + + +#### Responses + + + + + +A Nameserver Group object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/NameserverGroup" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Update a Nameserver Group [#update-a-nameserver-group] + +``` +PUT https://api.netbird.io/api/dns/nameservers/{nsgroupId} +``` + + + + +#### Path Parameters + + + + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "$ref": "#/components/schemas/NameserverGroupRequest" + } +} +``` + + + + +#### Responses + + + + + +A Nameserver Group object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/NameserverGroup" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Delete a Nameserver Group [#delete-a-nameserver-group] + +``` +DELETE https://api.netbird.io/api/dns/nameservers/{nsgroupId} +``` + + + + +#### Path Parameters + + + + + + + + + + + + + +#### Responses + + + + + +Delete status code + + + + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Retrieve DNS Settings [#retrieve-dns-settings] + +``` +GET https://api.netbird.io/api/dns/settings +``` + + + + + + + + + + + + + + +#### Responses + + + + + +A JSON Object of DNS Setting + + + + +```json +{ + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DNSSettings" + } + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Update DNS Settings [#update-dns-settings] + +``` +PUT https://api.netbird.io/api/dns/settings +``` + + + + + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "$ref": "#/components/schemas/DNSSettings" + } +} +``` + + + + +#### Responses + + + + + +A JSON Object of DNS Setting + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/DNSSettings" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + +## Events [#events] + +View information about the account and network events. + + + + + +### List all Events [#list-all-events] + +``` +GET https://api.netbird.io/api/events +``` + + + + + + + + + + + + + + +#### Responses + + + + + +A JSON Array of Events + + + + +```json +{ + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Event" + } + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + +## Accounts [#accounts] + +View information about the accounts. + + + + + +### List all Accounts [#list-all-accounts] + +``` +GET https://api.netbird.io/api/accounts +``` + + + + + + + + + + + + + + +#### Responses + + + + + +A JSON array of accounts + + + + +```json +{ + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Account" + } + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ + + + +### Update an Account [#update-an-account] + +``` +PUT https://api.netbird.io/api/accounts/{accountId} +``` + + + + +#### Path Parameters + + + + + + + + + + +#### Body Parameters + +```json +{ + "schema": { + "$ref": "#/components/schemas/accounts_accountId_body" + } +} +``` + + + + +#### Responses + + + + + +An Account object + + + + +```json +{ + "schema": { + "$ref": "#/components/schemas/Account" + } +} +``` + + + + + + + + +Bad Request + + + + + + + + + + + +Requires authentication + + + + + + + + + + + +Forbidden + + + + + + + + + + + +Internal Server Error + + + + + + + + + + + +
+ diff --git a/generator/api.json/.swagger-codegen-ignore b/generator/api.json/.swagger-codegen-ignore new file mode 100644 index 00000000..c5fa491b --- /dev/null +++ b/generator/api.json/.swagger-codegen-ignore @@ -0,0 +1,23 @@ +# Swagger Codegen Ignore +# Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell Swagger Codgen to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/generator/api.json/.swagger-codegen/VERSION b/generator/api.json/.swagger-codegen/VERSION new file mode 100644 index 00000000..34ec317a --- /dev/null +++ b/generator/api.json/.swagger-codegen/VERSION @@ -0,0 +1 @@ +3.0.42 \ No newline at end of file diff --git a/generator/api.json/README.md b/generator/api.json/README.md new file mode 100644 index 00000000..e69de29b diff --git a/generator/api.mdx b/generator/api.mdx new file mode 100644 index 00000000..f6b0e208 --- /dev/null +++ b/generator/api.mdx @@ -0,0 +1,4425 @@ + + + +# Users + +Interact with and view information about users. + +--- + + + +## Retrieve Users {{ tag: 'GET' , label: '/api/users' }} + + + + Returns a list of all users + + + + +```bash {{ title: 'cURL' }} +curl -X GET https://api.netbird.io/api/users \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \ +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +[ + { + "id": "string", + "email": "string", + "name": "string", + "role": "string", + "status": "string", + "auto_groups": [ + "string" + ], + "is_current": "boolean", + "is_service_user": "boolean" + } +] +``` + + + + + + + + + +--- + + +## Create a User {{ tag: 'POST' , label: '/api/users' }} + + + + Creates a new service user or sends an invite to a regular user + + #### Request-Body Parameters + + + + User's Email to send invite to + + + + User's full name + + + + User's NetBird account role + + + + Groups to auto-assign to peers registered by this user + + + + Is true if this user is a service user + + + + + + +```bash {{ title: 'cURL' }} +curl -X POST https://api.netbird.io/api/users \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \-H 'Content-Type: application/json' \ +--data-raw '{ + "email": "string", + "name": "string", + "role": "string", + "auto_groups": [ + "string" + ], + "is_service_user": "boolean" +}' +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "id": "string", + "email": "string", + "name": "string", + "role": "string", + "status": "string", + "auto_groups": [ + "string" + ], + "is_current": "boolean", + "is_service_user": "boolean" +} +``` + + + + + + + + + +--- + + +## Update a User {{ tag: 'PUT' , label: '/api/users/{userId}' }} + + + + Update information about a User + + #### Path Parameters + + + + The unique identifier of a user + + + + #### Request-Body Parameters + + + + User's NetBird account role + + + + Groups to auto-assign to peers registered by this user + + + + + + +```bash {{ title: 'cURL' }} +curl -X PUT https://api.netbird.io/api/users/{userId} \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \-H 'Content-Type: application/json' \ +--data-raw '{ + "role": "string", + "auto_groups": [ + "string" + ] +}' +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "id": "string", + "email": "string", + "name": "string", + "role": "string", + "status": "string", + "auto_groups": [ + "string" + ], + "is_current": "boolean", + "is_service_user": "boolean" +} +``` + + + + + + + + + +--- + + +## Delete a User {{ tag: 'DELETE' , label: '/api/users/{userId}' }} + + + + Delete a User + + #### Path Parameters + + + + The unique identifier of a user + + + + + + +```bash {{ title: 'cURL' }} +curl -X DELETE https://api.netbird.io/api/users/{userId} \ +-H "Authorization: Bearer {token}" \ + +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + + + + + +--- + + +## List all Tokens {{ tag: 'GET' , label: '/api/users/{userId}/tokens' }} + + + + Returns a list of all tokens for a user + + #### Path Parameters + + + + The unique identifier of a user + + + + + + +```bash {{ title: 'cURL' }} +curl -X GET https://api.netbird.io/api/users/{userId}/tokens \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \ +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +[ + { + "id": "string", + "name": "string", + "expiration_date": "string", + "created_by": "string", + "created_at": "string", + "last_used": "string" + } +] +``` + + + + + + + + + +--- + + +## Create a Token {{ tag: 'POST' , label: '/api/users/{userId}/tokens' }} + + + + Create a new token for a user + + #### Path Parameters + + + + The unique identifier of a user + + + + #### Request-Body Parameters + + + + Name of the token + + + + Expiration in days + + + + + + +```bash {{ title: 'cURL' }} +curl -X POST https://api.netbird.io/api/users/{userId}/tokens \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \-H 'Content-Type: application/json' \ +--data-raw '{ + "name": "string", + "expires_in": "integer" +}' +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "plain_token": "string", + "personal_access_token": { + "id": "string", + "name": "string", + "expiration_date": "string", + "created_by": "string", + "created_at": "string", + "last_used": "string" + } +} +``` + + + + + + + + + +--- + + +## Retrieve a Token {{ tag: 'GET' , label: '/api/users/{userId}/tokens/{tokenId}' }} + + + + Returns a specific token for a user + + #### Path Parameters + + + + The unique identifier of a user + + + + The unique identifier of a token + + + + + + +```bash {{ title: 'cURL' }} +curl -X GET https://api.netbird.io/api/users/{userId}/tokens/{tokenId} \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \ +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "id": "string", + "name": "string", + "expiration_date": "string", + "created_by": "string", + "created_at": "string", + "last_used": "string" +} +``` + + + + + + + + + +--- + + +## Delete a Token {{ tag: 'DELETE' , label: '/api/users/{userId}/tokens/{tokenId}' }} + + + + Delete a token for a user + + #### Path Parameters + + + + The unique identifier of a user + + + + The unique identifier of a token + + + + + + +```bash {{ title: 'cURL' }} +curl -X DELETE https://api.netbird.io/api/users/{userId}/tokens/{tokenId} \ +-H "Authorization: Bearer {token}" \ + +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + + + + + +--- + + + +# Peers + +Interact with and view information about peers. + +--- + + + +## List all Peers {{ tag: 'GET' , label: '/api/peers' }} + + + + Returns a list of all peers + + + + +```bash {{ title: 'cURL' }} +curl -X GET https://api.netbird.io/api/peers \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \ +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +[ + { + "id": "string", + "name": "string", + "ip": "string", + "connected": "boolean", + "last_seen": "string", + "os": "string", + "version": "string", + "groups": [ + { + "id": "string", + "name": "string", + "peers_count": "integer" + } + ], + "ssh_enabled": "boolean", + "user_id": "string", + "hostname": "string", + "ui_version": "string", + "dns_label": "string", + "login_expiration_enabled": "boolean", + "login_expired": "boolean", + "last_login": "string" + } +] +``` + + + + + + + + + +--- + + +## Retrieve a Peer {{ tag: 'GET' , label: '/api/peers/{peerId}' }} + + + + Get information about a peer + + #### Path Parameters + + + + The unique identifier of a peer + + + + + + +```bash {{ title: 'cURL' }} +curl -X GET https://api.netbird.io/api/peers/{peerId} \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \ +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "id": "string", + "name": "string", + "ip": "string", + "connected": "boolean", + "last_seen": "string", + "os": "string", + "version": "string", + "groups": [ + { + "id": "string", + "name": "string", + "peers_count": "integer" + } + ], + "ssh_enabled": "boolean", + "user_id": "string", + "hostname": "string", + "ui_version": "string", + "dns_label": "string", + "login_expiration_enabled": "boolean", + "login_expired": "boolean", + "last_login": "string" +} +``` + + + + + + + + + +--- + + +## Update a Peer {{ tag: 'PUT' , label: '/api/peers/{peerId}' }} + + + + Update information about a peer + + #### Path Parameters + + + + The unique identifier of a peer + + + + #### Request-Body Parameters + + + + + + + + + + + + + + + + + + +```bash {{ title: 'cURL' }} +curl -X PUT https://api.netbird.io/api/peers/{peerId} \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \-H 'Content-Type: application/json' \ +--data-raw '{ + "name": "string", + "ssh_enabled": "boolean", + "login_expiration_enabled": "boolean" +}' +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "id": "string", + "name": "string", + "ip": "string", + "connected": "boolean", + "last_seen": "string", + "os": "string", + "version": "string", + "groups": [ + { + "id": "string", + "name": "string", + "peers_count": "integer" + } + ], + "ssh_enabled": "boolean", + "user_id": "string", + "hostname": "string", + "ui_version": "string", + "dns_label": "string", + "login_expiration_enabled": "boolean", + "login_expired": "boolean", + "last_login": "string" +} +``` + + + + + + + + + +--- + + +## Delete a Peer {{ tag: 'DELETE' , label: '/api/peers/{peerId}' }} + + + + Delete a peer + + #### Path Parameters + + + + The unique identifier of a peer + + + + + + +```bash {{ title: 'cURL' }} +curl -X DELETE https://api.netbird.io/api/peers/{peerId} \ +-H "Authorization: Bearer {token}" \ + +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + + + + + +--- + + + +# Setup Keys + +Interact with and view information about setup keys. + +--- + + + +## List all Setup Keys {{ tag: 'GET' , label: '/api/setup-keys' }} + + + + Returns a list of all Setup Keys + + + + +```bash {{ title: 'cURL' }} +curl -X GET https://api.netbird.io/api/setup-keys \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \ +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +[ + { + "id": "string", + "key": "string", + "name": "string", + "expires": "string", + "type": "string", + "valid": "boolean", + "revoked": "boolean", + "used_times": "integer", + "last_used": "string", + "state": "string", + "auto_groups": [ + "string" + ], + "updated_at": "string", + "usage_limit": "integer" + } +] +``` + + + + + + + + + +--- + + +## Create a Setup Key {{ tag: 'POST' , label: '/api/setup-keys' }} + + + + Creates a Setup Key + + #### Request-Body Parameters + + + + Setup Key name + + + + Setup key type, one-off for single time usage and reusable + + + + Expiration time in seconds + + + + Setup key revocation status + + + + Setup key groups to auto-assign to peers registered with this key + + + + A number of times this key can be used. The value of 0 indicates the unlimited usage. + + + + + + +```bash {{ title: 'cURL' }} +curl -X POST https://api.netbird.io/api/setup-keys \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \-H 'Content-Type: application/json' \ +--data-raw '{ + "name": "string", + "type": "string", + "expires_in": "integer", + "revoked": "boolean", + "auto_groups": [ + "string" + ], + "usage_limit": "integer" +}' +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "id": "string", + "key": "string", + "name": "string", + "expires": "string", + "type": "string", + "valid": "boolean", + "revoked": "boolean", + "used_times": "integer", + "last_used": "string", + "state": "string", + "auto_groups": [ + "string" + ], + "updated_at": "string", + "usage_limit": "integer" +} +``` + + + + + + + + + +--- + + +## Retrieve a Setup Key {{ tag: 'GET' , label: '/api/setup-keys/{keyId}' }} + + + + Get information about a Setup Key + + #### Path Parameters + + + + The unique identifier of a setup key + + + + + + +```bash {{ title: 'cURL' }} +curl -X GET https://api.netbird.io/api/setup-keys/{keyId} \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \ +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "id": "string", + "key": "string", + "name": "string", + "expires": "string", + "type": "string", + "valid": "boolean", + "revoked": "boolean", + "used_times": "integer", + "last_used": "string", + "state": "string", + "auto_groups": [ + "string" + ], + "updated_at": "string", + "usage_limit": "integer" +} +``` + + + + + + + + + +--- + + +## Update a Setup Key {{ tag: 'PUT' , label: '/api/setup-keys/{keyId}' }} + + + + Update information about a Setup Key + + #### Path Parameters + + + + The unique identifier of a setup key + + + + #### Request-Body Parameters + + + + Setup Key name + + + + Setup key type, one-off for single time usage and reusable + + + + Expiration time in seconds + + + + Setup key revocation status + + + + Setup key groups to auto-assign to peers registered with this key + + + + A number of times this key can be used. The value of 0 indicates the unlimited usage. + + + + + + +```bash {{ title: 'cURL' }} +curl -X PUT https://api.netbird.io/api/setup-keys/{keyId} \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \-H 'Content-Type: application/json' \ +--data-raw '{ + "name": "string", + "type": "string", + "expires_in": "integer", + "revoked": "boolean", + "auto_groups": [ + "string" + ], + "usage_limit": "integer" +}' +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "id": "string", + "key": "string", + "name": "string", + "expires": "string", + "type": "string", + "valid": "boolean", + "revoked": "boolean", + "used_times": "integer", + "last_used": "string", + "state": "string", + "auto_groups": [ + "string" + ], + "updated_at": "string", + "usage_limit": "integer" +} +``` + + + + + + + + + +--- + + + +# Groups + +Interact with and view information about groups. + +--- + + + +## List all Groups {{ tag: 'GET' , label: '/api/groups' }} + + + + Returns a list of all Groups + + + + +```bash {{ title: 'cURL' }} +curl -X GET https://api.netbird.io/api/groups \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \ +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +[ + { + "id": "string", + "name": "string", + "peers_count": "integer", + "peers": [ + { + "id": "string", + "name": "string" + } + ] + } +] +``` + + + + + + + + + +--- + + +## Create a Group {{ tag: 'POST' , label: '/api/groups' }} + + + + Creates a Group + + #### Request-Body Parameters + + + + + + + + + + + + + + +```bash {{ title: 'cURL' }} +curl -X POST https://api.netbird.io/api/groups \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \-H 'Content-Type: application/json' \ +--data-raw '{ + "name": "string", + "peers": [ + "string" + ] +}' +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "id": "string", + "name": "string", + "peers_count": "integer", + "peers": [ + { + "id": "string", + "name": "string" + } + ] +} +``` + + + + + + + + + +--- + + +## Retrieve a Group {{ tag: 'GET' , label: '/api/groups/{groupId}' }} + + + + Get information about a Group + + #### Path Parameters + + + + The unique identifier of a group + + + + + + +```bash {{ title: 'cURL' }} +curl -X GET https://api.netbird.io/api/groups/{groupId} \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \ +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "id": "string", + "name": "string", + "peers_count": "integer", + "peers": [ + { + "id": "string", + "name": "string" + } + ] +} +``` + + + + + + + + + +--- + + +## Update a Group {{ tag: 'PUT' , label: '/api/groups/{groupId}' }} + + + + Update/Replace a Group + + #### Path Parameters + + + + The unique identifier of a group + + + + #### Request-Body Parameters + + + + + + + + + + + + + + +```bash {{ title: 'cURL' }} +curl -X PUT https://api.netbird.io/api/groups/{groupId} \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \-H 'Content-Type: application/json' \ +--data-raw '{ + "Name": "string", + "Peers": [ + "string" + ] +}' +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "id": "string", + "name": "string", + "peers_count": "integer", + "peers": [ + { + "id": "string", + "name": "string" + } + ] +} +``` + + + + + + + + + +--- + + +## Delete a Group {{ tag: 'DELETE' , label: '/api/groups/{groupId}' }} + + + + Delete a Group + + #### Path Parameters + + + + The unique identifier of a group + + + + + + +```bash {{ title: 'cURL' }} +curl -X DELETE https://api.netbird.io/api/groups/{groupId} \ +-H "Authorization: Bearer {token}" \ + +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + + + + + +--- + + + +# Rules + +Interact with and view information about rules. + +--- + + + +## List all Rules {{ tag: 'GET' , label: '/api/rules' }} + + + + Returns a list of all Rules + + + + +```bash {{ title: 'cURL' }} +curl -X GET https://api.netbird.io/api/rules \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \ +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +[ + { + "id": "string", + "name": "string", + "description": "string", + "disabled": "boolean", + "flow": "string", + "sources": [ + { + "id": "string", + "name": "string", + "peers_count": "integer" + } + ], + "destinations": [ + { + "id": "string", + "name": "string", + "peers_count": "integer" + } + ] + } +] +``` + + + + + + + + + +--- + + +## Create a Rule {{ tag: 'POST' , label: '/api/rules' }} + + + + Creates a Rule + + #### Request-Body Parameters + + + + + + + + + + + + + + +```bash {{ title: 'cURL' }} +curl -X POST https://api.netbird.io/api/rules \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \-H 'Content-Type: application/json' \ +--data-raw '{ + "name": "string", + "description": "string", + "disabled": "boolean", + "flow": "string", + "sources": [ + "string" + ], + "destinations": [ + "string" + ] +}' +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "id": "string", + "name": "string", + "description": "string", + "disabled": "boolean", + "flow": "string", + "sources": [ + { + "id": "string", + "name": "string", + "peers_count": "integer" + } + ], + "destinations": [ + { + "id": "string", + "name": "string", + "peers_count": "integer" + } + ] +} +``` + + + + + +--- + + +## Retrieve a Rule {{ tag: 'GET' , label: '/api/rules/{ruleId}' }} + + + + Get information about a Rules + + #### Path Parameters + + + + The unique identifier of a rule + + + + + + +```bash {{ title: 'cURL' }} +curl -X GET https://api.netbird.io/api/rules/{ruleId} \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \ +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "id": "string", + "name": "string", + "description": "string", + "disabled": "boolean", + "flow": "string", + "sources": [ + { + "id": "string", + "name": "string", + "peers_count": "integer" + } + ], + "destinations": [ + { + "id": "string", + "name": "string", + "peers_count": "integer" + } + ] +} +``` + + + + + + + + + +--- + + +## Update a Rule {{ tag: 'PUT' , label: '/api/rules/{ruleId}' }} + + + + Update/Replace a Rule + + #### Path Parameters + + + + The unique identifier of a rule + + + + #### Request-Body Parameters + + + + + + + + + + + + + + +```bash {{ title: 'cURL' }} +curl -X PUT https://api.netbird.io/api/rules/{ruleId} \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \-H 'Content-Type: application/json' \ +--data-raw '{ + "name": "string", + "description": "string", + "disabled": "boolean", + "flow": "string", + "sources": [ + "string" + ], + "destinations": [ + "string" + ] +}' +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "id": "string", + "name": "string", + "description": "string", + "disabled": "boolean", + "flow": "string", + "sources": [ + { + "id": "string", + "name": "string", + "peers_count": "integer" + } + ], + "destinations": [ + { + "id": "string", + "name": "string", + "peers_count": "integer" + } + ] +} +``` + + + + + + + + + +--- + + +## Delete a Rule {{ tag: 'DELETE' , label: '/api/rules/{ruleId}' }} + + + + Delete a Rule + + #### Path Parameters + + + + The unique identifier of a rule + + + + + + +```bash {{ title: 'cURL' }} +curl -X DELETE https://api.netbird.io/api/rules/{ruleId} \ +-H "Authorization: Bearer {token}" \ + +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + + + + + +--- + + + +# Policies + +Interact with and view information about policies. + +--- + + + +## List all Policies {{ tag: 'GET' , label: '/api/policies' }} + + + + Returns a list of all Policies + + + + +```bash {{ title: 'cURL' }} +curl -X GET https://api.netbird.io/api/policies \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \ +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +[ + { + "name": "string", + "description": "string", + "enabled": "boolean", + "query": "string", + "rules": [ + { + "id": "string", + "name": "string", + "description": "string", + "enabled": "boolean", + "sources": [ + { + "id": "string", + "name": "string", + "peers_count": "integer" + } + ], + "destinations": [ + { + "id": "string", + "name": "string", + "peers_count": "integer" + } + ], + "action": "string" + } + ], + "id": "string" + } +] +``` + + + + + + + + + +--- + + +## Create a Policy {{ tag: 'POST' , label: '/api/policies' }} + + + + Creates a Policy + + #### Request-Body Parameters + + + + + + +```bash {{ title: 'cURL' }} +curl -X POST https://api.netbird.io/api/policies \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \-H 'Content-Type: application/json' \ +--data-raw '{ + "name": "string", + "description": "string", + "enabled": "boolean", + "query": "string", + "rules": [ + { + "id": "string", + "name": "string", + "description": "string", + "enabled": "boolean", + "sources": [ + { + "id": "string", + "name": "string", + "peers_count": "integer" + } + ], + "destinations": [ + { + "id": "string", + "name": "string", + "peers_count": "integer" + } + ], + "action": "string" + } + ] +}' +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "name": "string", + "description": "string", + "enabled": "boolean", + "query": "string", + "rules": [ + { + "id": "string", + "name": "string", + "description": "string", + "enabled": "boolean", + "sources": [ + { + "id": "string", + "name": "string", + "peers_count": "integer" + } + ], + "destinations": [ + { + "id": "string", + "name": "string", + "peers_count": "integer" + } + ], + "action": "string" + } + ], + "id": "string" +} +``` + + + + + +--- + + +## Retrieve a Policy {{ tag: 'GET' , label: '/api/policies/{policyId}' }} + + + + Get information about a Policies + + #### Path Parameters + + + + The unique identifier of a policy + + + + + + +```bash {{ title: 'cURL' }} +curl -X GET https://api.netbird.io/api/policies/{policyId} \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \ +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "name": "string", + "description": "string", + "enabled": "boolean", + "query": "string", + "rules": [ + { + "id": "string", + "name": "string", + "description": "string", + "enabled": "boolean", + "sources": [ + { + "id": "string", + "name": "string", + "peers_count": "integer" + } + ], + "destinations": [ + { + "id": "string", + "name": "string", + "peers_count": "integer" + } + ], + "action": "string" + } + ], + "id": "string" +} +``` + + + + + + + + + +--- + + +## Update a Policy {{ tag: 'PUT' , label: '/api/policies/{policyId}' }} + + + + Update/Replace a Policy + + #### Path Parameters + + + + The unique identifier of a policy + + + + #### Request-Body Parameters + + + + + + +```bash {{ title: 'cURL' }} +curl -X PUT https://api.netbird.io/api/policies/{policyId} \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \-H 'Content-Type: application/json' \ +--data-raw '{ + "name": "string", + "description": "string", + "enabled": "boolean", + "query": "string", + "rules": [ + { + "id": "string", + "name": "string", + "description": "string", + "enabled": "boolean", + "sources": [ + { + "id": "string", + "name": "string", + "peers_count": "integer" + } + ], + "destinations": [ + { + "id": "string", + "name": "string", + "peers_count": "integer" + } + ], + "action": "string" + } + ] +}' +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "name": "string", + "description": "string", + "enabled": "boolean", + "query": "string", + "rules": [ + { + "id": "string", + "name": "string", + "description": "string", + "enabled": "boolean", + "sources": [ + { + "id": "string", + "name": "string", + "peers_count": "integer" + } + ], + "destinations": [ + { + "id": "string", + "name": "string", + "peers_count": "integer" + } + ], + "action": "string" + } + ], + "id": "string" +} +``` + + + + + + + + + +--- + + +## Delete a Policy {{ tag: 'DELETE' , label: '/api/policies/{policyId}' }} + + + + Delete a Policy + + #### Path Parameters + + + + The unique identifier of a policy + + + + + + +```bash {{ title: 'cURL' }} +curl -X DELETE https://api.netbird.io/api/policies/{policyId} \ +-H "Authorization: Bearer {token}" \ + +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + + + + + +--- + + + +# Routes + +Interact with and view information about routes. + +--- + + + +## List all Routes {{ tag: 'GET' , label: '/api/routes' }} + + + + Returns a list of all routes + + + + +```bash {{ title: 'cURL' }} +curl -X GET https://api.netbird.io/api/routes \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \ +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +[ + { + "id": "string", + "network_type": "string", + "description": "string", + "network_id": "string", + "enabled": "boolean", + "peer": "string", + "network": "string", + "metric": "integer", + "masquerade": "boolean", + "groups": [ + "string" + ] + } +] +``` + + + + + + + + + +--- + + +## Create a Route {{ tag: 'POST' , label: '/api/routes' }} + + + + Creates a Route + + #### Request-Body Parameters + + + + Route description + + + + Route network identifier, to group HA routes + + + + Route status + + + + Peer Identifier associated with route + + + + Network range in CIDR format + + + + Route metric number. Lowest number has higher priority + + + + Indicate if peer should masquerade traffic to this route's prefix + + + + Route group tag groups + + + + + + +```bash {{ title: 'cURL' }} +curl -X POST https://api.netbird.io/api/routes \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \-H 'Content-Type: application/json' \ +--data-raw '{ + "description": "string", + "network_id": "string", + "enabled": "boolean", + "peer": "string", + "network": "string", + "metric": "integer", + "masquerade": "boolean", + "groups": [ + "string" + ] +}' +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "id": "string", + "network_type": "string", + "description": "string", + "network_id": "string", + "enabled": "boolean", + "peer": "string", + "network": "string", + "metric": "integer", + "masquerade": "boolean", + "groups": [ + "string" + ] +} +``` + + + + + + + + + +--- + + +## Retrieve a Route {{ tag: 'GET' , label: '/api/routes/{routeId}' }} + + + + Get information about a Routes + + #### Path Parameters + + + + The unique identifier of a route + + + + + + +```bash {{ title: 'cURL' }} +curl -X GET https://api.netbird.io/api/routes/{routeId} \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \ +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "id": "string", + "network_type": "string", + "description": "string", + "network_id": "string", + "enabled": "boolean", + "peer": "string", + "network": "string", + "metric": "integer", + "masquerade": "boolean", + "groups": [ + "string" + ] +} +``` + + + + + + + + + +--- + + +## Update a Route {{ tag: 'PUT' , label: '/api/routes/{routeId}' }} + + + + Update/Replace a Route + + #### Path Parameters + + + + The unique identifier of a route + + + + #### Request-Body Parameters + + + + Route description + + + + Route network identifier, to group HA routes + + + + Route status + + + + Peer Identifier associated with route + + + + Network range in CIDR format + + + + Route metric number. Lowest number has higher priority + + + + Indicate if peer should masquerade traffic to this route's prefix + + + + Route group tag groups + + + + + + +```bash {{ title: 'cURL' }} +curl -X PUT https://api.netbird.io/api/routes/{routeId} \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \-H 'Content-Type: application/json' \ +--data-raw '{ + "description": "string", + "network_id": "string", + "enabled": "boolean", + "peer": "string", + "network": "string", + "metric": "integer", + "masquerade": "boolean", + "groups": [ + "string" + ] +}' +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "id": "string", + "network_type": "string", + "description": "string", + "network_id": "string", + "enabled": "boolean", + "peer": "string", + "network": "string", + "metric": "integer", + "masquerade": "boolean", + "groups": [ + "string" + ] +} +``` + + + + + + + + + +--- + + +## Delete a Route {{ tag: 'DELETE' , label: '/api/routes/{routeId}' }} + + + + Delete a Route + + #### Path Parameters + + + + The unique identifier of a route + + + + + + +```bash {{ title: 'cURL' }} +curl -X DELETE https://api.netbird.io/api/routes/{routeId} \ +-H "Authorization: Bearer {token}" \ + +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + + + + + +--- + + + +# DNS + +Interact with and view information about DNS configuration. + +--- + + + +## List all Nameserver Groups {{ tag: 'GET' , label: '/api/dns/nameservers' }} + + + + Returns a list of all Nameserver Groups + + + + +```bash {{ title: 'cURL' }} +curl -X GET https://api.netbird.io/api/dns/nameservers \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \ +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +[ + { + "id": "string", + "name": "string", + "description": "string", + "nameservers": [ + { + "ip": "string", + "ns_type": "string", + "port": "integer" + } + ], + "enabled": "boolean", + "groups": [ + "string" + ], + "primary": "boolean", + "domains": [ + "string" + ] + } +] +``` + + + + + + + + + +--- + + +## Create a Nameserver Group {{ tag: 'POST' , label: '/api/dns/nameservers' }} + + + + Creates a Nameserver Group + + #### Request-Body Parameters + + + + Nameserver group name + + + + Nameserver group description + + + + Nameserver group + + + + Nameserver group status + + + + Nameserver group tag groups + + + + Nameserver group primary status + + + + Nameserver group domain list + + + + + + +```bash {{ title: 'cURL' }} +curl -X POST https://api.netbird.io/api/dns/nameservers \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \-H 'Content-Type: application/json' \ +--data-raw '{ + "name": "string", + "description": "string", + "nameservers": [ + { + "ip": "string", + "ns_type": "string", + "port": "integer" + } + ], + "enabled": "boolean", + "groups": [ + "string" + ], + "primary": "boolean", + "domains": [ + "string" + ] +}' +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "id": "string", + "name": "string", + "description": "string", + "nameservers": [ + { + "ip": "string", + "ns_type": "string", + "port": "integer" + } + ], + "enabled": "boolean", + "groups": [ + "string" + ], + "primary": "boolean", + "domains": [ + "string" + ] +} +``` + + + + + + + + + +--- + + +## Retrieve a Nameserver Group {{ tag: 'GET' , label: '/api/dns/nameservers/{nsgroupId}' }} + + + + Get information about a Nameserver Groups + + #### Path Parameters + + + + The unique identifier of a Nameserver Group + + + + + + +```bash {{ title: 'cURL' }} +curl -X GET https://api.netbird.io/api/dns/nameservers/{nsgroupId} \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \ +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "id": "string", + "name": "string", + "description": "string", + "nameservers": [ + { + "ip": "string", + "ns_type": "string", + "port": "integer" + } + ], + "enabled": "boolean", + "groups": [ + "string" + ], + "primary": "boolean", + "domains": [ + "string" + ] +} +``` + + + + + + + + + +--- + + +## Update a Nameserver Group {{ tag: 'PUT' , label: '/api/dns/nameservers/{nsgroupId}' }} + + + + Update/Replace a Nameserver Group + + #### Path Parameters + + + + The unique identifier of a Nameserver Group + + + + #### Request-Body Parameters + + + + Nameserver group name + + + + Nameserver group description + + + + Nameserver group + + + + Nameserver group status + + + + Nameserver group tag groups + + + + Nameserver group primary status + + + + Nameserver group domain list + + + + + + +```bash {{ title: 'cURL' }} +curl -X PUT https://api.netbird.io/api/dns/nameservers/{nsgroupId} \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \-H 'Content-Type: application/json' \ +--data-raw '{ + "name": "string", + "description": "string", + "nameservers": [ + { + "ip": "string", + "ns_type": "string", + "port": "integer" + } + ], + "enabled": "boolean", + "groups": [ + "string" + ], + "primary": "boolean", + "domains": [ + "string" + ] +}' +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "id": "string", + "name": "string", + "description": "string", + "nameservers": [ + { + "ip": "string", + "ns_type": "string", + "port": "integer" + } + ], + "enabled": "boolean", + "groups": [ + "string" + ], + "primary": "boolean", + "domains": [ + "string" + ] +} +``` + + + + + + + + + +--- + + +## Delete a Nameserver Group {{ tag: 'DELETE' , label: '/api/dns/nameservers/{nsgroupId}' }} + + + + Delete a Nameserver Group + + #### Path Parameters + + + + The unique identifier of a Nameserver Group + + + + + + +```bash {{ title: 'cURL' }} +curl -X DELETE https://api.netbird.io/api/dns/nameservers/{nsgroupId} \ +-H "Authorization: Bearer {token}" \ + +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + + + + + +--- + + +## Retrieve DNS Settings {{ tag: 'GET' , label: '/api/dns/settings' }} + + + + Returns a DNS settings object + + + + +```bash {{ title: 'cURL' }} +curl -X GET https://api.netbird.io/api/dns/settings \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \ +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +[ + { + "disabled_management_groups": [ + "string" + ] + } +] +``` + + + + + + + + + +--- + + +## Update DNS Settings {{ tag: 'PUT' , label: '/api/dns/settings' }} + + + + Updates a DNS settings object + + #### Request-Body Parameters + + + + Groups whose DNS management is disabled + + + + + + +```bash {{ title: 'cURL' }} +curl -X PUT https://api.netbird.io/api/dns/settings \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \-H 'Content-Type: application/json' \ +--data-raw '{ + "disabled_management_groups": [ + "string" + ] +}' +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "disabled_management_groups": [ + "string" + ] +} +``` + + + + + + + + + +--- + + + +# Events + +View information about the account and network events. + +--- + + + +## List all Events {{ tag: 'GET' , label: '/api/events' }} + + + + Returns a list of all events + + + + +```bash {{ title: 'cURL' }} +curl -X GET https://api.netbird.io/api/events \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \ +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +[ + { + "id": "string", + "timestamp": "string", + "activity": "string", + "activity_code": "string", + "initiator_id": "string", + "target_id": "string", + "meta": "object" + } +] +``` + + + + + + + + + +--- + + + +# Accounts + +View information about the accounts. + +--- + + + +## List all Accounts {{ tag: 'GET' , label: '/api/accounts' }} + + + + Returns a list of accounts of a user. Always returns a list of one account. + + + + +```bash {{ title: 'cURL' }} +curl -X GET https://api.netbird.io/api/accounts \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \ +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +[ + { + "id": "string", + "settings": { + "peer_login_expiration_enabled": "boolean", + "peer_login_expiration": "integer" + } + } +] +``` + + + + + + + + + +--- + + +## Update an Account {{ tag: 'PUT' , label: '/api/accounts/{accountId}' }} + + + + Update information about an account + + #### Path Parameters + + + + The unique identifier of an account + + + + #### Request-Body Parameters + + + + + + + + + + +```bash {{ title: 'cURL' }} +curl -X PUT https://api.netbird.io/api/accounts/{accountId} \ +-H "Authorization: Bearer {token}" \ +-H 'Accept: application/json' \-H 'Content-Type: application/json' \ +--data-raw '{ + "settings": { + "peer_login_expiration_enabled": "boolean", + "peer_login_expiration": "integer" + } +}' +``` + + ```js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + ``` + + ```python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + ``` + + ```php + $client = new \Protocol\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + ``` + + + + + + +```json {{ title: '200' }} +{ + "id": "string", + "settings": { + "peer_login_expiration_enabled": "boolean", + "peer_login_expiration": "integer" + } +} +``` + + + + + + + + + +--- diff --git a/generator/api.ts b/generator/api.ts new file mode 100644 index 00000000..8e275b14 --- /dev/null +++ b/generator/api.ts @@ -0,0 +1,200 @@ +import template from './templates/ApiTemplate' +import { slugify, toArrayWithKey, toTitle, writeToDisk } from './helpers' +import { OpenAPIV3, OpenAPIV2 } from 'openapi-types' +import * as yaml from 'yaml'; +import * as fs from 'fs' +import * as ejs from 'ejs' + +export default async function gen(inputFileName: string, outputDir: string, apiUrl: string) { + const specRaw = fs.readFileSync(inputFileName, 'utf8') + const spec = yaml.parse(specRaw) as any + // console.log('spec', spec) + + switch (spec.openapi || spec.swagger) { + case '3.0.0': + case '3.0.1': + case '3.0.3': + await gen_v3(spec, outputDir, { apiUrl }) + break + + default: + console.log('Unrecognized specification version:', spec.openapi) + break + } +} + +/** + * Versioned Generator + */ + +// OPENAPI-SPEC-VERSION: 3.0.0 +type v3OperationWithPath = OpenAPIV3.OperationObject & { + path: string +} +export type enrichedOperation = OpenAPIV3.OperationObject & { + path: string + fullPath: string + operationId: string +} + + +export type schemaParameter = { + name: string + type: string + description: string + required: boolean + minimum?: number + maximum?: number + minLength?: number + maxLength?: number + enum?: string[] +} + +export type schemaObject = { + examples: Object + parameters: schemaParameter[] +} + +async function gen_v3(spec: OpenAPIV3.Document, dest: string, { apiUrl }: { apiUrl: string }) { + const specLayout = spec.tags || [] + // const operations: enrichedOperation[] = [] + const tagGroups = new Map() + Object.entries(spec.paths).forEach(([key, val]) => { + const fullPath = `${apiUrl}${key}` + + toArrayWithKey(val!, 'operation').forEach((o) => { + const operation = o as v3OperationWithPath + const enriched = { + ...operation, + path: key, + fullPath, + operationId: slugify(operation.summary!), + + responseList: toArrayWithKey(operation.responses!, 'responseCode') || [], + } + let tag = operation.tags.pop() + let tagOperations = tagGroups.get(tag) ?? [] + tagGroups.set(tag, tagOperations.concat(enriched)) + }) + }) + + let schemas = new Map(); + Object.entries(spec.components?.schemas).forEach(([key, val]) => { + const schema = val as OpenAPIV3.SchemaObject + let examples = new Map(); + let parameters : schemaParameter[] = [] + if(schema.allOf){ + schema.allOf.forEach((item) => { + if((item as OpenAPIV3.ReferenceObject).$ref){ + let schemaObject = schemas.get((item as OpenAPIV3.ReferenceObject).$ref.split('/').pop()) + let examplesMap = new Map(Object.entries(schemaObject.examples)) + examplesMap.forEach((value, key) => { + examples.set(key, value) + }) + parameters = parameters.concat(schemaObject.parameters) + } + if((item as OpenAPIV3.SchemaObject).properties){ + Object.entries((item as OpenAPIV3.SchemaObject).properties).forEach(([key, val]) => { + let property = val as OpenAPIV3.SchemaObject + let type + if (property.type === "array") { + type = new Array(resolveType(property.items, spec.components?.schemas)) + } else { + type = resolveType(property, spec.components?.schemas) + } + examples.set(key, type) + let parameter: schemaParameter = { + name: key, + type: property.type === "array" ? ((property.items as OpenAPIV3.SchemaObject).type || (property.items as OpenAPIV3.ReferenceObject).$ref.split('/').pop()) + "[]" : property.type, + description: property.description, + required: schema.required?.includes(key) || false, + minimum: property.minimum, + maximum: property.maximum, + minLength: property.minLength, + maxLength: property.maxLength, + enum: property.enum + } + parameters.push(parameter) + }) + } + }) + } else { + Object.entries(schema.properties).forEach(([key, val]) => { + let property = val as OpenAPIV3.SchemaObject + let type + if(property.type === "array"){ + type = new Array(resolveType(property.items, spec.components?.schemas)) + } else { + type = resolveType(property, spec.components?.schemas) + } + examples.set(key, type) + let parameter : schemaParameter = { + name: key, + type: property.type === "array" ? ((property.items as OpenAPIV3.SchemaObject).type || (property.items as OpenAPIV3.ReferenceObject).$ref.split('/').pop()) + "[]" : property.type, + description: property.description, + required: schema.required?.includes(key) || false, + minimum: property.minimum, + maximum: property.maximum, + minLength: property.minLength, + maxLength: property.maxLength, + enum: property.enum + } + parameters.push(parameter) + }) + } + + + let output : schemaObject = { + examples: Object.fromEntries(examples), + parameters: parameters + } + schemas.set(key, output) + }) + + + tagGroups.forEach((value: enrichedOperation[], key: string) => { + + const operations = value + + const sections = specLayout.map((section) => { + return { + ...section, + title: toTitle(section.name), + id: slugify(section.name), + operations: operations, + } + }) + + const content = ejs.render(template, { + info: spec.info, + sections, + operations, + schemas, + }) + + // Write to disk + let outputFile = dest + "/" + key.toLowerCase().replace(" ", "-") + ".mdx" + writeToDisk(outputFile, content) + // console.log('Saved: ', outputFile) + }) +} + +function resolveType(items: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject | OpenAPIV3.NonArraySchemaObjectType, schemas) : any { + if((items as OpenAPIV3.ReferenceObject).$ref){ + let ref = (items as OpenAPIV3.ReferenceObject).$ref + let map = new Map() + Object.entries(schemas[ref.split('/').pop()].properties).forEach(([key, val]) => { + let property = val as OpenAPIV3.SchemaObject + let type + if(property.type === "array"){ + type = new Array(resolveType(property.items, schemas)) + } else { + type = resolveType(property, schemas) + } + map.set(key, type) + }) + return Object.fromEntries(map) + } + return (items as OpenAPIV3.ArraySchemaObject).type +} + diff --git a/generator/helpers.ts b/generator/helpers.ts new file mode 100644 index 00000000..e7796c91 --- /dev/null +++ b/generator/helpers.ts @@ -0,0 +1,42 @@ +import * as _ from 'lodash' +import * as fs from 'fs' + +export const slugify = (text: string) => { + if (!text) return '' + return text + .toString() + .toLowerCase() + .replace(/[. )(]/g, '-') // Replace spaces and brackets - + .replace(/[^\w\-]+/g, '') // Remove all non-word chars + .replace(/\-\-+/g, '-') // Replace multiple - with single - + .replace(/^-+/, '') // Trim - from start of text + .replace(/-+$/, '') // Trim - from end of text +} + +// Uppercase the first letter of a string +export const toTitle = (text: string) => { + return text.charAt(0).toUpperCase() + text.slice(1) +} + +/** + * writeToDisk() + */ +export const writeToDisk = (fileName: string, content: any) => { + return new Promise((resolve, reject) => { + fs.writeFile(fileName, content, (err: any) => { + if (err) return reject(err) + else return resolve(true) + }) + }) +} + +/** + * Convert Object to Array of values + */ +export const toArrayWithKey = (obj: object, keyAs: string) => + _.values( + _.mapValues(obj, (value: any, key: string) => { + value[keyAs] = key + return value + }) + ) diff --git a/generator/index.ts b/generator/index.ts new file mode 100644 index 00000000..53ead88d --- /dev/null +++ b/generator/index.ts @@ -0,0 +1,45 @@ +import ApiGenerator from './api' + + +const main = (command: string[], options: any) => { + handleInput(command[0], options) +} + +// Run everything +const argv = require('minimist')(process.argv.slice(2)) +main(argv['_'], argv) + +function handleInput(command: string, options: any) { + switch (command) { + case 'gen': + DocGenerator(options) + break + + default: + console.log('Unrecognized command:', command) + break + } +} + +export default async function DocGenerator({ + input, + output, + type, + url, +}: { + input: string + output: string + type: 'api' + url?: string +}) { + switch (type) { + case 'api': + await ApiGenerator(input, output, url || '') + break + + default: + await console.log('Unrecognized type: ', type) + break + } + return 'Done' +} diff --git a/generator/openapi.json b/generator/openapi.json new file mode 100644 index 00000000..df1bf59f --- /dev/null +++ b/generator/openapi.json @@ -0,0 +1,3153 @@ +{ + "openapi" : "3.0.1", + "info" : { + "title" : "NetBird REST API", + "description" : "API to manipulate groups, rules, policies and retrieve information about peers and users", + "version" : "0.0.1" + }, + "servers" : [ { + "url" : "https://netbird.io", + "description" : "Default server" + } ], + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ], + "tags" : [ { + "name" : "Users", + "description" : "Interact with and view information about users." + }, { + "name" : "Tokens", + "description" : "Interact with and view information about tokens." + }, { + "name" : "Peers", + "description" : "Interact with and view information about peers." + }, { + "name" : "Setup Keys", + "description" : "Interact with and view information about setup keys." + }, { + "name" : "Groups", + "description" : "Interact with and view information about groups." + }, { + "name" : "Rules", + "description" : "Interact with and view information about rules." + }, { + "name" : "Policies", + "description" : "Interact with and view information about policies." + }, { + "name" : "Routes", + "description" : "Interact with and view information about routes." + }, { + "name" : "DNS", + "description" : "Interact with and view information about DNS configuration." + }, { + "name" : "Events", + "description" : "View information about the account and network events." + }, { + "name" : "Accounts", + "description" : "View information about the accounts." + } ], + "paths" : { + "/api/accounts" : { + "get" : { + "tags" : [ "Accounts" ], + "summary" : "List all Accounts", + "description" : "Returns a list of accounts of a user. Always returns a list of one account.", + "operationId" : "getAccounts", + "responses" : { + "200" : { + "description" : "A JSON array of accounts", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/Account" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + } + }, + "/api/accounts/{accountId}" : { + "put" : { + "tags" : [ "Accounts" ], + "summary" : "Update an Account", + "description" : "Update information about an account", + "operationId" : "updateAccount", + "parameters" : [ { + "name" : "accountId", + "in" : "path", + "description" : "The unique identifier of an account", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "description" : "update an account", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/AccountSettings" + } + } + } + }, + "responses" : { + "200" : { + "description" : "An Account object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Account" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + } + }, + "/api/users" : { + "get" : { + "tags" : [ "Users" ], + "summary" : "Retrieve Users", + "description" : "Returns a list of all users", + "operationId" : "getUsers", + "parameters" : [ { + "name" : "service_user", + "in" : "query", + "description" : "Filters users and returns either regular users or service users", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "boolean" + } + } ], + "responses" : { + "200" : { + "description" : "A JSON array of Users", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/User" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "post" : { + "tags" : [ "Users" ], + "summary" : "Create a User", + "description" : "Creates a new service user or sends an invite to a regular user", + "operationId" : "createUser", + "requestBody" : { + "description" : "User invite information", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserCreateRequest" + } + } + } + }, + "responses" : { + "200" : { + "description" : "A User object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/User" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + } + }, + "/api/users/{userId}" : { + "put" : { + "tags" : [ "Users" ], + "summary" : "Update a User", + "description" : "Update information about a User", + "operationId" : "updateUser", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "The unique identifier of a user", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "description" : "User update", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/UserRequest" + } + } + } + }, + "responses" : { + "200" : { + "description" : "A User object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/User" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "delete" : { + "tags" : [ "Users" ], + "summary" : "Delete a User", + "description" : "Delete a User", + "operationId" : "deleteUser", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "The unique identifier of a user", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "Delete status code", + "content" : { } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + } + }, + "/api/users/{userId}/tokens" : { + "get" : { + "tags" : [ "Tokens" ], + "summary" : "List all Tokens", + "description" : "Returns a list of all tokens for a user", + "operationId" : "getTokens", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "The unique identifier of a user", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "A JSON Array of PersonalAccessTokens", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/PersonalAccessToken" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "post" : { + "tags" : [ "Tokens" ], + "summary" : "Create a Token", + "description" : "Create a new token for a user", + "operationId" : "createToken", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "The unique identifier of a user", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "description" : "PersonalAccessToken create parameters", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PersonalAccessTokenRequest" + } + } + } + }, + "responses" : { + "200" : { + "description" : "The token in plain text", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PersonalAccessTokenGenerated" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + } + }, + "/api/users/{userId}/tokens/{tokenId}" : { + "get" : { + "tags" : [ "Tokens" ], + "summary" : "Retrieve a Token", + "description" : "Returns a specific token for a user", + "operationId" : "getToken", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "The unique identifier of a user", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "tokenId", + "in" : "path", + "description" : "The unique identifier of a token", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "A PersonalAccessTokens Object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PersonalAccessToken" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "delete" : { + "tags" : [ "Tokens" ], + "summary" : "Delete a Token", + "description" : "Delete a token for a user", + "operationId" : "deleteToken", + "parameters" : [ { + "name" : "userId", + "in" : "path", + "description" : "The unique identifier of a user", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "tokenId", + "in" : "path", + "description" : "The unique identifier of a token", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "Delete status code", + "content" : { } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + } + }, + "/api/peers" : { + "get" : { + "tags" : [ "Peers" ], + "summary" : "List all Peers", + "description" : "Returns a list of all peers", + "operationId" : "getPeers", + "responses" : { + "200" : { + "description" : "A JSON Array of Peers", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/Peer" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + } + }, + "/api/peers/{peerId}" : { + "get" : { + "tags" : [ "Peers" ], + "summary" : "Retrieve a Peer", + "description" : "Get information about a peer", + "operationId" : "getPeer", + "parameters" : [ { + "name" : "peerId", + "in" : "path", + "description" : "The unique identifier of a peer", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "A Peer object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Peer" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "put" : { + "tags" : [ "Peers" ], + "summary" : "Update a Peer", + "description" : "Update information about a peer", + "parameters" : [ { + "name" : "peerId", + "in" : "path", + "description" : "The unique identifier of a peer", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "description" : "update a peer", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PeerRequest" + } + } + } + }, + "responses" : { + "200" : { + "description" : "A Peer object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Peer" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "delete" : { + "tags" : [ "Peers" ], + "summary" : "Delete a Peer", + "description" : "Delete a peer", + "parameters" : [ { + "name" : "peerId", + "in" : "path", + "description" : "The unique identifier of a peer", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "Delete status code", + "content" : { } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + } + }, + "/api/setup-keys" : { + "get" : { + "tags" : [ "Setup Keys" ], + "summary" : "List all Setup Keys", + "description" : "Returns a list of all Setup Keys", + "responses" : { + "200" : { + "description" : "A JSON Array of Setup keys", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/SetupKey" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "post" : { + "tags" : [ "Setup Keys" ], + "summary" : "Create a Setup Key", + "description" : "Creates a Setup Key", + "requestBody" : { + "description" : "New Setup Key request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SetupKeyRequest" + } + } + } + }, + "responses" : { + "200" : { + "description" : "A Setup Keys Object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SetupKey" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + } + }, + "/api/setup-keys/{keyId}" : { + "get" : { + "tags" : [ "Setup Keys" ], + "summary" : "Retrieve a Setup Key", + "description" : "Get information about a Setup Key", + "parameters" : [ { + "name" : "keyId", + "in" : "path", + "description" : "The unique identifier of a setup key", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "A Setup Key object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SetupKey" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "put" : { + "tags" : [ "Setup Keys" ], + "summary" : "Update a Setup Key", + "description" : "Update information about a Setup Key", + "parameters" : [ { + "name" : "keyId", + "in" : "path", + "description" : "The unique identifier of a setup key", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "description" : "update to Setup Key", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SetupKeyRequest" + } + } + } + }, + "responses" : { + "200" : { + "description" : "A Setup Key object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SetupKey" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + } + }, + "/api/groups" : { + "get" : { + "tags" : [ "Groups" ], + "summary" : "List all Groups", + "description" : "Returns a list of all Groups", + "responses" : { + "200" : { + "description" : "A JSON Array of Groups", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/Group" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "post" : { + "tags" : [ "Groups" ], + "summary" : "Create a Group", + "description" : "Creates a Group", + "requestBody" : { + "description" : "New Group request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/GroupRequest" + } + } + } + }, + "responses" : { + "200" : { + "description" : "A Group Object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Group" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + } + }, + "/api/groups/{groupId}" : { + "get" : { + "tags" : [ "Groups" ], + "summary" : "Retrieve a Group", + "description" : "Get information about a Group", + "parameters" : [ { + "name" : "groupId", + "in" : "path", + "description" : "The unique identifier of a group", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "A Group object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Group" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "put" : { + "tags" : [ "Groups" ], + "summary" : "Update a Group", + "description" : "Update/Replace a Group", + "parameters" : [ { + "name" : "groupId", + "in" : "path", + "description" : "The unique identifier of a group", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "description" : "Update Group request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/GroupRequest" + } + } + } + }, + "responses" : { + "200" : { + "description" : "A Group object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Group" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "delete" : { + "tags" : [ "Groups" ], + "summary" : "Delete a Group", + "description" : "Delete a Group", + "parameters" : [ { + "name" : "groupId", + "in" : "path", + "description" : "The unique identifier of a group", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "Delete status code", + "content" : { } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + } + }, + "/api/rules" : { + "get" : { + "tags" : [ "Rules" ], + "summary" : "List all Rules", + "description" : "Returns a list of all Rules", + "responses" : { + "200" : { + "description" : "A JSON Array of Rules", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/Rule" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "post" : { + "tags" : [ "Rules" ], + "summary" : "Create a Rule", + "description" : "Creates a Rule", + "requestBody" : { + "description" : "New Rule request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RuleRequest" + } + } + } + }, + "responses" : { + "200" : { + "description" : "A Rule Object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Rule" + } + } + } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + } + }, + "/api/rules/{ruleId}" : { + "get" : { + "tags" : [ "Rules" ], + "summary" : "Retrieve a Rule", + "description" : "Get information about a Rules", + "parameters" : [ { + "name" : "ruleId", + "in" : "path", + "description" : "The unique identifier of a rule", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "A Rule object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Rule" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "put" : { + "tags" : [ "Rules" ], + "summary" : "Update a Rule", + "description" : "Update/Replace a Rule", + "parameters" : [ { + "name" : "ruleId", + "in" : "path", + "description" : "The unique identifier of a rule", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "description" : "Update Rule request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RuleRequest" + } + } + } + }, + "responses" : { + "200" : { + "description" : "A Rule object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Rule" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "delete" : { + "tags" : [ "Rules" ], + "summary" : "Delete a Rule", + "description" : "Delete a Rule", + "parameters" : [ { + "name" : "ruleId", + "in" : "path", + "description" : "The unique identifier of a rule", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "Delete status code", + "content" : { } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + } + }, + "/api/policies" : { + "get" : { + "tags" : [ "Policies" ], + "summary" : "List all Policies", + "description" : "Returns a list of all Policies", + "responses" : { + "200" : { + "description" : "A JSON Array of Policies", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/Policy" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "post" : { + "tags" : [ "Policies" ], + "summary" : "Create a Policy", + "description" : "Creates a Policy", + "requestBody" : { + "description" : "New Policy request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PolicyMinimum" + } + } + } + }, + "responses" : { + "200" : { + "description" : "A Policy Object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Policy" + } + } + } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + } + }, + "/api/policies/{policyId}" : { + "get" : { + "tags" : [ "Policies" ], + "summary" : "Retrieve a Policy", + "description" : "Get information about a Policies", + "parameters" : [ { + "name" : "policyId", + "in" : "path", + "description" : "The unique identifier of a policy", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "A Policy object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Policy" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "put" : { + "tags" : [ "Policies" ], + "summary" : "Update a Policy", + "description" : "Update/Replace a Policy", + "parameters" : [ { + "name" : "policyId", + "in" : "path", + "description" : "The unique identifier of a policy", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "description" : "Update Policy request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PolicyMinimum" + } + } + } + }, + "responses" : { + "200" : { + "description" : "A Policy object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Policy" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "delete" : { + "tags" : [ "Policies" ], + "summary" : "Delete a Policy", + "description" : "Delete a Policy", + "parameters" : [ { + "name" : "policyId", + "in" : "path", + "description" : "The unique identifier of a policy", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "Delete status code", + "content" : { } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + } + }, + "/api/routes" : { + "get" : { + "tags" : [ "Routes" ], + "summary" : "List all Routes", + "description" : "Returns a list of all routes", + "responses" : { + "200" : { + "description" : "A JSON Array of Routes", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/Route" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "post" : { + "tags" : [ "Routes" ], + "summary" : "Create a Route", + "description" : "Creates a Route", + "requestBody" : { + "description" : "New Routes request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RouteRequest" + } + } + } + }, + "responses" : { + "200" : { + "description" : "A Route Object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Route" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + } + }, + "/api/routes/{routeId}" : { + "get" : { + "tags" : [ "Routes" ], + "summary" : "Retrieve a Route", + "description" : "Get information about a Routes", + "parameters" : [ { + "name" : "routeId", + "in" : "path", + "description" : "The unique identifier of a route", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "A Route object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Route" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "put" : { + "tags" : [ "Routes" ], + "summary" : "Update a Route", + "description" : "Update/Replace a Route", + "parameters" : [ { + "name" : "routeId", + "in" : "path", + "description" : "The unique identifier of a route", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "description" : "Update Route request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/RouteRequest" + } + } + } + }, + "responses" : { + "200" : { + "description" : "A Route object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Route" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "delete" : { + "tags" : [ "Routes" ], + "summary" : "Delete a Route", + "description" : "Delete a Route", + "parameters" : [ { + "name" : "routeId", + "in" : "path", + "description" : "The unique identifier of a route", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "Delete status code", + "content" : { } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + } + }, + "/api/dns/nameservers" : { + "get" : { + "tags" : [ "DNS" ], + "summary" : "List all Nameserver Groups", + "description" : "Returns a list of all Nameserver Groups", + "responses" : { + "200" : { + "description" : "A JSON Array of Nameserver Groups", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/NameserverGroup" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "post" : { + "tags" : [ "DNS" ], + "summary" : "Create a Nameserver Group", + "description" : "Creates a Nameserver Group", + "requestBody" : { + "description" : "New Nameserver Groups request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/NameserverGroupRequest" + } + } + } + }, + "responses" : { + "200" : { + "description" : "A Nameserver Groups Object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/NameserverGroup" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + } + }, + "/api/dns/nameservers/{nsgroupId}" : { + "get" : { + "tags" : [ "DNS" ], + "summary" : "Retrieve a Nameserver Group", + "description" : "Get information about a Nameserver Groups", + "parameters" : [ { + "name" : "nsgroupId", + "in" : "path", + "description" : "The unique identifier of a Nameserver Group", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "A Nameserver Group object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/NameserverGroup" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "put" : { + "tags" : [ "DNS" ], + "summary" : "Update a Nameserver Group", + "description" : "Update/Replace a Nameserver Group", + "parameters" : [ { + "name" : "nsgroupId", + "in" : "path", + "description" : "The unique identifier of a Nameserver Group", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "description" : "Update Nameserver Group request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/NameserverGroupRequest" + } + } + } + }, + "responses" : { + "200" : { + "description" : "A Nameserver Group object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/NameserverGroup" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "delete" : { + "tags" : [ "DNS" ], + "summary" : "Delete a Nameserver Group", + "description" : "Delete a Nameserver Group", + "parameters" : [ { + "name" : "nsgroupId", + "in" : "path", + "description" : "The unique identifier of a Nameserver Group", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "Delete status code", + "content" : { } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + } + }, + "/api/dns/settings" : { + "get" : { + "tags" : [ "DNS" ], + "summary" : "Retrieve DNS Settings", + "description" : "Returns a DNS settings object", + "responses" : { + "200" : { + "description" : "A JSON Object of DNS Setting", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/DNSSettings" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + }, + "put" : { + "tags" : [ "DNS" ], + "summary" : "Update DNS Settings", + "description" : "Updates a DNS settings object", + "requestBody" : { + "description" : "A DNS settings object", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/DNSSettings" + } + } + } + }, + "responses" : { + "200" : { + "description" : "A JSON Object of DNS Setting", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/DNSSettings" + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + } + }, + "/api/events" : { + "get" : { + "tags" : [ "Events" ], + "summary" : "List all Events", + "description" : "Returns a list of all events", + "responses" : { + "200" : { + "description" : "A JSON Array of Events", + "content" : { + "application/json" : { + "schema" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/Event" + } + } + } + } + }, + "400" : { + "description" : "Bad Request", + "content" : { } + }, + "401" : { + "description" : "Requires authentication", + "content" : { } + }, + "403" : { + "description" : "Forbidden", + "content" : { } + }, + "500" : { + "description" : "Internal Server Error", + "content" : { } + } + }, + "security" : [ { + "BearerAuth" : [ ] + }, { + "TokenAuth" : [ ] + } ] + } + } + }, + "components" : { + "schemas" : { + "Account" : { + "required" : [ "id", "settings" ], + "properties" : { + "id" : { + "type" : "string", + "description" : "Account ID" + }, + "settings" : { + "$ref" : "#/components/schemas/AccountSettings" + } + } + }, + "AccountSettings" : { + "required" : [ "peer_login_expiration", "peer_login_expiration_enabled" ], + "properties" : { + "peer_login_expiration_enabled" : { + "type" : "boolean", + "description" : "Enables or disables peer login expiration globally. After peer's login has expired the user has to log in (authenticate). Applies only to peers that were added by a user (interactive SSO login)." + }, + "peer_login_expiration" : { + "type" : "integer", + "description" : "Period of time after which peer login expires (seconds)." + } + } + }, + "User" : { + "required" : [ "auto_groups", "email", "id", "name", "role", "status" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string", + "description" : "User ID" + }, + "email" : { + "type" : "string", + "description" : "User's email address" + }, + "name" : { + "type" : "string", + "description" : "User's name from idp provider" + }, + "role" : { + "type" : "string", + "description" : "User's NetBird account role" + }, + "status" : { + "type" : "string", + "description" : "User's status", + "enum" : [ "active", "invited", "disabled" ] + }, + "auto_groups" : { + "type" : "array", + "description" : "Groups to auto-assign to peers registered by this user", + "items" : { + "type" : "string" + } + }, + "is_current" : { + "type" : "boolean", + "description" : "Is true if authenticated user is the same as this user", + "readOnly" : true + }, + "is_service_user" : { + "type" : "boolean", + "description" : "Is true if this user is a service user", + "readOnly" : true + } + } + }, + "UserRequest" : { + "required" : [ "auto_groups", "role" ], + "type" : "object", + "properties" : { + "role" : { + "type" : "string", + "description" : "User's NetBird account role" + }, + "auto_groups" : { + "type" : "array", + "description" : "Groups to auto-assign to peers registered by this user", + "items" : { + "type" : "string" + } + } + } + }, + "UserCreateRequest" : { + "required" : [ "auto_groups", "is_service_user", "role" ], + "type" : "object", + "properties" : { + "email" : { + "type" : "string", + "description" : "User's Email to send invite to" + }, + "name" : { + "type" : "string", + "description" : "User's full name" + }, + "role" : { + "type" : "string", + "description" : "User's NetBird account role" + }, + "auto_groups" : { + "type" : "array", + "description" : "Groups to auto-assign to peers registered by this user", + "items" : { + "type" : "string" + } + }, + "is_service_user" : { + "type" : "boolean", + "description" : "Is true if this user is a service user" + } + } + }, + "PeerMinimum" : { + "required" : [ "id", "name" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string", + "description" : "Peer ID" + }, + "name" : { + "type" : "string", + "description" : "Peer's hostname" + } + } + }, + "PeerRequest" : { + "required" : [ "login_expiration_enabled", "name", "ssh_enabled" ], + "type" : "object", + "properties" : { + "name" : { + "type" : "string" + }, + "ssh_enabled" : { + "type" : "boolean" + }, + "login_expiration_enabled" : { + "type" : "boolean" + } + } + }, + "Peer" : { + "allOf" : [ { + "$ref" : "#/components/schemas/PeerMinimum" + }, { + "required" : [ "connected", "dns_label", "groups", "hostname", "ip", "last_login", "last_seen", "login_expiration_enabled", "login_expired", "os", "ssh_enabled", "version" ], + "type" : "object", + "properties" : { + "ip" : { + "type" : "string", + "description" : "Peer's IP address" + }, + "connected" : { + "type" : "boolean", + "description" : "Peer to Management connection status" + }, + "last_seen" : { + "type" : "string", + "description" : "Last time peer connected to Netbird's management service", + "format" : "date-time" + }, + "os" : { + "type" : "string", + "description" : "Peer's operating system and version" + }, + "version" : { + "type" : "string", + "description" : "Peer's daemon or cli version" + }, + "groups" : { + "type" : "array", + "description" : "Groups that the peer belongs to", + "items" : { + "$ref" : "#/components/schemas/GroupMinimum" + } + }, + "ssh_enabled" : { + "type" : "boolean", + "description" : "Indicates whether SSH server is enabled on this peer" + }, + "user_id" : { + "type" : "string", + "description" : "User ID of the user that enrolled this peer" + }, + "hostname" : { + "type" : "string", + "description" : "Hostname of the machine" + }, + "ui_version" : { + "type" : "string", + "description" : "Peer's desktop UI version" + }, + "dns_label" : { + "type" : "string", + "description" : "Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud" + }, + "login_expiration_enabled" : { + "type" : "boolean", + "description" : "Indicates whether peer login expiration has been enabled or not" + }, + "login_expired" : { + "type" : "boolean", + "description" : "Indicates whether peer's login expired or not" + }, + "last_login" : { + "type" : "string", + "description" : "Last time this peer performed log in (authentication). E.g., user authenticated.", + "format" : "date-time" + } + } + } ] + }, + "SetupKey" : { + "required" : [ "auto_groups", "expires", "id", "key", "last_used", "name", "revoked", "state", "type", "updated_at", "usage_limit", "used_times", "valid" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string", + "description" : "Setup Key ID" + }, + "key" : { + "type" : "string", + "description" : "Setup Key value" + }, + "name" : { + "type" : "string", + "description" : "Setup key name identifier" + }, + "expires" : { + "type" : "string", + "description" : "Setup Key expiration date", + "format" : "date-time" + }, + "type" : { + "type" : "string", + "description" : "Setup key type, one-off for single time usage and reusable" + }, + "valid" : { + "type" : "boolean", + "description" : "Setup key validity status" + }, + "revoked" : { + "type" : "boolean", + "description" : "Setup key revocation status" + }, + "used_times" : { + "type" : "integer", + "description" : "Usage count of setup key" + }, + "last_used" : { + "type" : "string", + "description" : "Setup key last usage date", + "format" : "date-time" + }, + "state" : { + "type" : "string", + "description" : "Setup key status, \"valid\", \"overused\",\"expired\" or \"revoked\"" + }, + "auto_groups" : { + "type" : "array", + "description" : "Setup key groups to auto-assign to peers registered with this key", + "items" : { + "type" : "string" + } + }, + "updated_at" : { + "type" : "string", + "description" : "Setup key last update date", + "format" : "date-time" + }, + "usage_limit" : { + "type" : "integer", + "description" : "A number of times this key can be used. The value of 0 indicates the unlimited usage." + } + } + }, + "SetupKeyRequest" : { + "required" : [ "auto_groups", "expires_in", "name", "revoked", "type", "usage_limit" ], + "type" : "object", + "properties" : { + "name" : { + "type" : "string", + "description" : "Setup Key name" + }, + "type" : { + "type" : "string", + "description" : "Setup key type, one-off for single time usage and reusable" + }, + "expires_in" : { + "type" : "integer", + "description" : "Expiration time in seconds" + }, + "revoked" : { + "type" : "boolean", + "description" : "Setup key revocation status" + }, + "auto_groups" : { + "type" : "array", + "description" : "Setup key groups to auto-assign to peers registered with this key", + "items" : { + "type" : "string" + } + }, + "usage_limit" : { + "type" : "integer", + "description" : "A number of times this key can be used. The value of 0 indicates the unlimited usage." + } + } + }, + "PersonalAccessToken" : { + "required" : [ "created_at", "created_by", "expiration_date", "id", "name" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string", + "description" : "ID of a token" + }, + "name" : { + "type" : "string", + "description" : "Name of the token" + }, + "expiration_date" : { + "type" : "string", + "description" : "Date the token expires", + "format" : "date-time" + }, + "created_by" : { + "type" : "string", + "description" : "User ID of the user who created the token" + }, + "created_at" : { + "type" : "string", + "description" : "Date the token was created", + "format" : "date-time" + }, + "last_used" : { + "type" : "string", + "description" : "Date the token was last used", + "format" : "date-time" + } + } + }, + "PersonalAccessTokenGenerated" : { + "required" : [ "personal_access_token", "plain_token" ], + "type" : "object", + "properties" : { + "plain_token" : { + "type" : "string", + "description" : "Plain text representation of the generated token" + }, + "personal_access_token" : { + "$ref" : "#/components/schemas/PersonalAccessToken" + } + } + }, + "PersonalAccessTokenRequest" : { + "required" : [ "expires_in", "name" ], + "type" : "object", + "properties" : { + "name" : { + "type" : "string", + "description" : "Name of the token" + }, + "expires_in" : { + "maximum" : 365, + "minimum" : 1, + "type" : "integer", + "description" : "Expiration in days" + } + } + }, + "GroupMinimum" : { + "required" : [ "id", "name", "peers_count" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string", + "description" : "Group ID" + }, + "name" : { + "type" : "string", + "description" : "Group Name identifier" + }, + "peers_count" : { + "type" : "integer", + "description" : "Count of peers associated to the group" + } + } + }, + "GroupRequest" : { + "required" : [ "name" ], + "type" : "object", + "properties" : { + "name" : { + "type" : "string", + "description" : "Group name identifier" + }, + "peers" : { + "type" : "array", + "description" : "List of peers ids", + "items" : { + "type" : "string" + } + } + } + }, + "Group" : { + "allOf" : [ { + "$ref" : "#/components/schemas/GroupMinimum" + }, { + "required" : [ "peers" ], + "type" : "object", + "properties" : { + "peers" : { + "type" : "array", + "description" : "List of peers object", + "items" : { + "$ref" : "#/components/schemas/PeerMinimum" + } + } + } + } ] + }, + "RuleMinimum" : { + "required" : [ "description", "disabled", "flow", "name" ], + "type" : "object", + "properties" : { + "name" : { + "type" : "string", + "description" : "Rule name identifier" + }, + "description" : { + "type" : "string", + "description" : "Rule friendly description" + }, + "disabled" : { + "type" : "boolean", + "description" : "Rules status" + }, + "flow" : { + "type" : "string", + "description" : "Rule flow, currently, only \"bidirect\" for bi-directional traffic is accepted" + } + } + }, + "RuleRequest" : { + "allOf" : [ { + "$ref" : "#/components/schemas/RuleMinimum" + }, { + "type" : "object", + "properties" : { + "sources" : { + "type" : "array", + "description" : "List of source groups", + "items" : { + "type" : "string" + } + }, + "destinations" : { + "type" : "array", + "description" : "List of destination groups", + "items" : { + "type" : "string" + } + } + } + } ] + }, + "Rule" : { + "allOf" : [ { + "required" : [ "id" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string", + "description" : "Rule ID" + } + } + }, { + "$ref" : "#/components/schemas/RuleMinimum" + }, { + "required" : [ "destinations", "sources" ], + "type" : "object", + "properties" : { + "sources" : { + "type" : "array", + "description" : "Rule source groups", + "items" : { + "$ref" : "#/components/schemas/GroupMinimum" + } + }, + "destinations" : { + "type" : "array", + "description" : "Rule destination groups", + "items" : { + "$ref" : "#/components/schemas/GroupMinimum" + } + } + } + } ] + }, + "PolicyRule" : { + "required" : [ "action", "destinations", "enabled", "name", "sources" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string", + "description" : "Rule ID" + }, + "name" : { + "type" : "string", + "description" : "Rule name identifier" + }, + "description" : { + "type" : "string", + "description" : "Rule friendly description" + }, + "enabled" : { + "type" : "boolean", + "description" : "Rules status" + }, + "sources" : { + "type" : "array", + "description" : "policy source groups", + "items" : { + "$ref" : "#/components/schemas/GroupMinimum" + } + }, + "destinations" : { + "type" : "array", + "description" : "policy destination groups", + "items" : { + "$ref" : "#/components/schemas/GroupMinimum" + } + }, + "action" : { + "type" : "string", + "description" : "policy accept or drops packets", + "enum" : [ "accept", "drop" ] + } + } + }, + "PolicyMinimum" : { + "required" : [ "description", "enabled", "name", "query", "rules" ], + "type" : "object", + "properties" : { + "name" : { + "type" : "string", + "description" : "Policy name identifier" + }, + "description" : { + "type" : "string", + "description" : "Policy friendly description" + }, + "enabled" : { + "type" : "boolean", + "description" : "Policy status" + }, + "query" : { + "type" : "string", + "description" : "Policy Rego query" + }, + "rules" : { + "type" : "array", + "description" : "Policy rule object for policy UI editor", + "items" : { + "$ref" : "#/components/schemas/PolicyRule" + } + } + } + }, + "Policy" : { + "allOf" : [ { + "$ref" : "#/components/schemas/PolicyMinimum" + }, { + "required" : [ "id" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string", + "description" : "Policy ID" + } + } + } ] + }, + "RouteRequest" : { + "required" : [ "description", "enabled", "groups", "id", "masquerade", "metric", "network", "network_id", "peer" ], + "type" : "object", + "properties" : { + "description" : { + "type" : "string", + "description" : "Route description" + }, + "network_id" : { + "maxLength" : 40, + "minLength" : 1, + "type" : "string", + "description" : "Route network identifier, to group HA routes" + }, + "enabled" : { + "type" : "boolean", + "description" : "Route status" + }, + "peer" : { + "type" : "string", + "description" : "Peer Identifier associated with route" + }, + "network" : { + "type" : "string", + "description" : "Network range in CIDR format" + }, + "metric" : { + "maximum" : 9999, + "minimum" : 1, + "type" : "integer", + "description" : "Route metric number. Lowest number has higher priority" + }, + "masquerade" : { + "type" : "boolean", + "description" : "Indicate if peer should masquerade traffic to this route's prefix" + }, + "groups" : { + "type" : "array", + "description" : "Route group tag groups", + "items" : { + "type" : "string" + } + } + } + }, + "Route" : { + "allOf" : [ { + "required" : [ "id", "network_type" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string", + "description" : "Route Id" + }, + "network_type" : { + "type" : "string", + "description" : "Network type indicating if it is IPv4 or IPv6" + } + } + }, { + "$ref" : "#/components/schemas/RouteRequest" + } ] + }, + "Nameserver" : { + "required" : [ "ip", "ns_type", "port" ], + "type" : "object", + "properties" : { + "ip" : { + "type" : "string", + "description" : "Nameserver IP" + }, + "ns_type" : { + "type" : "string", + "description" : "Nameserver Type", + "enum" : [ "udp" ] + }, + "port" : { + "type" : "integer", + "description" : "Nameserver Port" + } + } + }, + "NameserverGroupRequest" : { + "required" : [ "description", "domains", "enabled", "groups", "name", "nameservers", "primary" ], + "type" : "object", + "properties" : { + "name" : { + "maxLength" : 40, + "minLength" : 1, + "type" : "string", + "description" : "Nameserver group name" + }, + "description" : { + "type" : "string", + "description" : "Nameserver group description" + }, + "nameservers" : { + "maxLength" : 2, + "minLength" : 1, + "type" : "array", + "description" : "Nameserver group", + "items" : { + "$ref" : "#/components/schemas/Nameserver" + } + }, + "enabled" : { + "type" : "boolean", + "description" : "Nameserver group status" + }, + "groups" : { + "type" : "array", + "description" : "Nameserver group tag groups", + "items" : { + "type" : "string" + } + }, + "primary" : { + "type" : "boolean", + "description" : "Nameserver group primary status" + }, + "domains" : { + "type" : "array", + "description" : "Nameserver group domain list", + "items" : { + "maxLength" : 255, + "minLength" : 1, + "type" : "string" + } + } + } + }, + "NameserverGroup" : { + "allOf" : [ { + "required" : [ "id" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string", + "description" : "Nameserver group ID" + } + } + }, { + "$ref" : "#/components/schemas/NameserverGroupRequest" + } ] + }, + "DNSSettings" : { + "required" : [ "disabled_management_groups" ], + "type" : "object", + "properties" : { + "disabled_management_groups" : { + "type" : "array", + "description" : "Groups whose DNS management is disabled", + "items" : { + "type" : "string" + } + } + } + }, + "Event" : { + "required" : [ "activity", "activity_code", "id", "initiator_id", "meta", "target_id", "timestamp" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string", + "description" : "Event unique identifier" + }, + "timestamp" : { + "type" : "string", + "description" : "The date and time when the event occurred", + "format" : "date-time" + }, + "activity" : { + "type" : "string", + "description" : "The activity that occurred during the event" + }, + "activity_code" : { + "type" : "string", + "description" : "The string code of the activity that occurred during the event", + "enum" : [ "user.peer.delete", "user.join", "user.invite", "user.peer.add", "user.group.add", "user.group.delete", "user.role.update", "setupkey.peer.add", "setupkey.add", "setupkey.update", "setupkey.revoke", "setupkey.overuse", "setupkey.group.delete", "setupkey.group.add", "rule.add", "rule.delete", "rule.update", "policy.add", "policy.delete", "policy.update", "group.add", "group.update", "dns.setting.disabled.management.group.add", "dns.setting.disabled.management.group.delete", "account.create", "account.setting.peer.login.expiration.update", "account.setting.peer.login.expiration.disable", "account.setting.peer.login.expiration.enable", "route.add", "route.delete", "route.update", "nameserver.group.add", "nameserver.group.delete", "nameserver.group.update", "peer.ssh.disable", "peer.ssh.enable", "peer.rename", "peer.login.expiration.disable", "peer.login.expiration.enable", "service.user.create", "personal.access.token.create", "service.user.delete", "personal.access.token.delete" ] + }, + "initiator_id" : { + "type" : "string", + "description" : "The ID of the initiator of the event. E.g., an ID of a user that triggered the event." + }, + "target_id" : { + "type" : "string", + "description" : "The ID of the target of the event. E.g., an ID of the peer that a user removed." + }, + "meta" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + }, + "description" : "The metadata of the event" + } + } + } + }, + "responses" : { + "not_found" : { + "description" : "Resource not found", + "content" : { } + }, + "validation_failed_simple" : { + "description" : "Validation failed", + "content" : { } + }, + "bad_request" : { + "description" : "Bad Request", + "content" : { } + }, + "internal_error" : { + "description" : "Internal Server Error", + "content" : { } + }, + "validation_failed" : { + "description" : "Validation failed", + "content" : { } + }, + "forbidden" : { + "description" : "Forbidden", + "content" : { } + }, + "requires_authentication" : { + "description" : "Requires authentication", + "content" : { } + } + }, + "securitySchemes" : { + "BearerAuth" : { + "type" : "http", + "scheme" : "bearer", + "bearerFormat" : "JWT" + }, + "TokenAuth" : { + "type" : "apiKey", + "description" : "Enter the token with the `Token` prefix, e.g. \"Token nbp_F3f0d.....\".", + "name" : "Authorization", + "in" : "header" + } + } + } +} \ No newline at end of file diff --git a/generator/openapi.yml b/generator/openapi.yml new file mode 100644 index 00000000..9e74b3a5 --- /dev/null +++ b/generator/openapi.yml @@ -0,0 +1,2105 @@ +openapi: 3.0.1 +servers: + - url: https://netbird.io + description: Default server +info: + title: NetBird REST API + description: API to manipulate groups, rules, policies and retrieve information about peers and users + version: 0.0.1 +tags: + - name: Users + description: Interact with and view information about users. + - name: Tokens + description: Interact with and view information about tokens. + - name: Peers + description: Interact with and view information about peers. + - name: Setup Keys + description: Interact with and view information about setup keys. + - name: Groups + description: Interact with and view information about groups. + - name: Rules + description: Interact with and view information about rules. + - name: Policies + description: Interact with and view information about policies. + - name: Routes + description: Interact with and view information about routes. + - name: DNS + description: Interact with and view information about DNS configuration. + - name: Events + description: View information about the account and network events. + - name: Accounts + description: View information about the accounts. +components: + schemas: + Account: + properties: + id: + description: Account ID + type: string + settings: + $ref: '#/components/schemas/AccountSettings' + required: + - id + - settings + AccountSettings: + properties: + peer_login_expiration_enabled: + description: Enables or disables peer login expiration globally. After peer's login has expired the user has to log in (authenticate). Applies only to peers that were added by a user (interactive SSO login). + type: boolean + peer_login_expiration: + description: Period of time after which peer login expires (seconds). + type: integer + required: + - peer_login_expiration_enabled + - peer_login_expiration + User: + type: object + properties: + id: + description: User ID + type: string + email: + description: User's email address + type: string + name: + description: User's name from idp provider + type: string + role: + description: User's NetBird account role + type: string + status: + description: User's status + type: string + enum: [ "active","invited","disabled" ] + auto_groups: + description: Groups to auto-assign to peers registered by this user + type: array + items: + type: string + is_current: + description: Is true if authenticated user is the same as this user + type: boolean + readOnly: true + is_service_user: + description: Is true if this user is a service user + type: boolean + readOnly: true + required: + - id + - email + - name + - role + - auto_groups + - status + UserRequest: + type: object + properties: + role: + description: User's NetBird account role + type: string + auto_groups: + description: Groups to auto-assign to peers registered by this user + type: array + items: + type: string + required: + - role + - auto_groups + UserCreateRequest: + type: object + properties: + email: + description: User's Email to send invite to + type: string + name: + description: User's full name + type: string + role: + description: User's NetBird account role + type: string + auto_groups: + description: Groups to auto-assign to peers registered by this user + type: array + items: + type: string + is_service_user: + description: Is true if this user is a service user + type: boolean + required: + - role + - auto_groups + - is_service_user + PeerMinimum: + type: object + properties: + id: + description: Peer ID + type: string + name: + description: Peer's hostname + type: string + required: + - id + - name + PeerRequest: + type: object + properties: + name: + type: string + ssh_enabled: + type: boolean + login_expiration_enabled: + type: boolean + required: + - name + - ssh_enabled + - login_expiration_enabled + Peer: + allOf: + - $ref: '#/components/schemas/PeerMinimum' + - type: object + properties: + ip: + description: Peer's IP address + type: string + connected: + description: Peer to Management connection status + type: boolean + last_seen: + description: Last time peer connected to Netbird's management service + type: string + format: date-time + os: + description: Peer's operating system and version + type: string + version: + description: Peer's daemon or cli version + type: string + groups: + description: Groups that the peer belongs to + type: array + items: + $ref: '#/components/schemas/GroupMinimum' + ssh_enabled: + description: Indicates whether SSH server is enabled on this peer + type: boolean + user_id: + description: User ID of the user that enrolled this peer + type: string + hostname: + description: Hostname of the machine + type: string + ui_version: + description: Peer's desktop UI version + type: string + dns_label: + description: Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud + type: string + login_expiration_enabled: + description: Indicates whether peer login expiration has been enabled or not + type: boolean + login_expired: + description: Indicates whether peer's login expired or not + type: boolean + last_login: + description: Last time this peer performed log in (authentication). E.g., user authenticated. + type: string + format: date-time + required: + - ip + - connected + - last_seen + - os + - version + - groups + - ssh_enabled + - hostname + - dns_label + - login_expiration_enabled + - login_expired + - last_login + SetupKey: + type: object + properties: + id: + description: Setup Key ID + type: string + key: + description: Setup Key value + type: string + name: + description: Setup key name identifier + type: string + expires: + description: Setup Key expiration date + type: string + format: date-time + type: + description: Setup key type, one-off for single time usage and reusable + type: string + valid: + description: Setup key validity status + type: boolean + revoked: + description: Setup key revocation status + type: boolean + used_times: + description: Usage count of setup key + type: integer + last_used: + description: Setup key last usage date + type: string + format: date-time + state: + description: Setup key status, "valid", "overused","expired" or "revoked" + type: string + auto_groups: + description: Setup key groups to auto-assign to peers registered with this key + type: array + items: + type: string + updated_at: + description: Setup key last update date + type: string + format: date-time + usage_limit: + description: A number of times this key can be used. The value of 0 indicates the unlimited usage. + type: integer + required: + - id + - key + - name + - expires + - type + - valid + - revoked + - used_times + - last_used + - state + - auto_groups + - updated_at + - usage_limit + SetupKeyRequest: + type: object + properties: + name: + description: Setup Key name + type: string + type: + description: Setup key type, one-off for single time usage and reusable + type: string + expires_in: + description: Expiration time in seconds + type: integer + revoked: + description: Setup key revocation status + type: boolean + auto_groups: + description: Setup key groups to auto-assign to peers registered with this key + type: array + items: + type: string + usage_limit: + description: A number of times this key can be used. The value of 0 indicates the unlimited usage. + type: integer + required: + - name + - type + - expires_in + - revoked + - auto_groups + - usage_limit + PersonalAccessToken: + type: object + properties: + id: + description: ID of a token + type: string + name: + description: Name of the token + type: string + expiration_date: + description: Date the token expires + type: string + format: date-time + created_by: + description: User ID of the user who created the token + type: string + created_at: + description: Date the token was created + type: string + format: date-time + last_used: + description: Date the token was last used + type: string + format: date-time + required: + - id + - name + - expiration_date + - created_by + - created_at + PersonalAccessTokenGenerated: + type: object + properties: + plain_token: + description: Plain text representation of the generated token + type: string + personal_access_token: + $ref: '#/components/schemas/PersonalAccessToken' + required: + - plain_token + - personal_access_token + PersonalAccessTokenRequest: + type: object + properties: + name: + description: Name of the token + type: string + expires_in: + description: Expiration in days + type: integer + minimum: 1 + maximum: 365 + required: + - name + - expires_in + GroupMinimum: + type: object + properties: + id: + description: Group ID + type: string + name: + description: Group Name identifier + type: string + peers_count: + description: Count of peers associated to the group + type: integer + required: + - id + - name + - peers_count + GroupRequest: + type: object + properties: + name: + type: string + description: Group name identifier + peers: + type: array + description: List of peers ids + items: + type: string + required: + - name + Group: + allOf: + - $ref: '#/components/schemas/GroupMinimum' + - type: object + properties: + peers: + description: List of peers object + type: array + items: + $ref: '#/components/schemas/PeerMinimum' + required: + - peers + RuleMinimum: + type: object + properties: + name: + description: Rule name identifier + type: string + description: + description: Rule friendly description + type: string + disabled: + description: Rules status + type: boolean + flow: + description: Rule flow, currently, only "bidirect" for bi-directional traffic is accepted + type: string + required: + - name + - description + - disabled + - flow + RuleRequest: + allOf: + - $ref: '#/components/schemas/RuleMinimum' + - type: object + properties: + sources: + type: array + description: List of source groups + items: + type: string + destinations: + type: array + description: List of destination groups + items: + type: string + Rule: + allOf: + - type: object + properties: + id: + description: Rule ID + type: string + required: + - id + - $ref: '#/components/schemas/RuleMinimum' + - type: object + properties: + sources: + description: Rule source groups + type: array + items: + $ref: '#/components/schemas/GroupMinimum' + destinations: + description: Rule destination groups + type: array + items: + $ref: '#/components/schemas/GroupMinimum' + required: + - sources + - destinations + PolicyRule: + type: object + properties: + id: + description: Rule ID + type: string + name: + description: Rule name identifier + type: string + description: + description: Rule friendly description + type: string + enabled: + description: Rules status + type: boolean + sources: + description: policy source groups + type: array + items: + $ref: '#/components/schemas/GroupMinimum' + destinations: + description: policy destination groups + type: array + items: + $ref: '#/components/schemas/GroupMinimum' + action: + description: policy accept or drops packets + type: string + enum: ["accept","drop"] + required: + - name + - sources + - destinations + - action + - enabled + PolicyMinimum: + type: object + properties: + name: + description: Policy name identifier + type: string + description: + description: Policy friendly description + type: string + enabled: + description: Policy status + type: boolean + query: + description: Policy Rego query + type: string + rules: + description: Policy rule object for policy UI editor + type: array + items: + $ref: '#/components/schemas/PolicyRule' + required: + - name + - description + - enabled + - query + - rules + Policy: + allOf: + - $ref: '#/components/schemas/PolicyMinimum' + - type: object + properties: + id: + description: Policy ID + type: string + required: + - id + RouteRequest: + type: object + properties: + description: + description: Route description + type: string + network_id: + description: Route network identifier, to group HA routes + type: string + maxLength: 40 + minLength: 1 + enabled: + description: Route status + type: boolean + peer: + description: Peer Identifier associated with route + type: string + network: + description: Network range in CIDR format + type: string + metric: + description: Route metric number. Lowest number has higher priority + type: integer + maximum: 9999 + minimum: 1 + masquerade: + description: Indicate if peer should masquerade traffic to this route's prefix + type: boolean + groups: + description: Route group tag groups + type: array + items: + type: string + required: + - id + - description + - network_id + - enabled + - peer + - network + - metric + - masquerade + - groups + Route: + allOf: + - type: object + properties: + id: + description: Route Id + type: string + network_type: + description: Network type indicating if it is IPv4 or IPv6 + type: string + required: + - id + - network_type + - $ref: '#/components/schemas/RouteRequest' + Nameserver: + type: object + properties: + ip: + description: Nameserver IP + type: string + ns_type: + description: Nameserver Type + type: string + enum: [ "udp" ] + port: + description: Nameserver Port + type: integer + required: + - ip + - ns_type + - port + NameserverGroupRequest: + type: object + properties: + name: + description: Nameserver group name + type: string + maxLength: 40 + minLength: 1 + description: + description: Nameserver group description + type: string + nameservers: + description: Nameserver group + minLength: 1 + maxLength: 2 + type: array + items: + $ref: '#/components/schemas/Nameserver' + enabled: + description: Nameserver group status + type: boolean + groups: + description: Nameserver group tag groups + type: array + items: + type: string + primary: + description: Nameserver group primary status + type: boolean + domains: + description: Nameserver group domain list + type: array + items: + type: string + minLength: 1 + maxLength: 255 + required: + - name + - description + - nameservers + - enabled + - groups + - primary + - domains + NameserverGroup: + allOf: + - type: object + properties: + id: + description: Nameserver group ID + type: string + required: + - id + - $ref: '#/components/schemas/NameserverGroupRequest' + DNSSettings: + type: object + properties: + disabled_management_groups: + description: Groups whose DNS management is disabled + type: array + items: + type: string + required: + - disabled_management_groups + Event: + type: object + properties: + id: + description: Event unique identifier + type: string + timestamp: + description: The date and time when the event occurred + type: string + format: date-time + activity: + description: The activity that occurred during the event + type: string + activity_code: + description: The string code of the activity that occurred during the event + type: string + enum: [ "user.peer.delete", "user.join", "user.invite", "user.peer.add", "user.group.add", "user.group.delete", + "user.role.update", + "setupkey.peer.add", "setupkey.add", "setupkey.update", "setupkey.revoke", "setupkey.overuse", + "setupkey.group.delete", "setupkey.group.add", + "rule.add", "rule.delete", "rule.update", + "policy.add", "policy.delete", "policy.update", + "group.add", "group.update", "dns.setting.disabled.management.group.add", "dns.setting.disabled.management.group.delete", + "account.create", "account.setting.peer.login.expiration.update", "account.setting.peer.login.expiration.disable", "account.setting.peer.login.expiration.enable", + "route.add", "route.delete", "route.update", + "nameserver.group.add", "nameserver.group.delete", "nameserver.group.update", + "peer.ssh.disable", "peer.ssh.enable", "peer.rename", "peer.login.expiration.disable", "peer.login.expiration.enable", + "service.user.create", "personal.access.token.create", "service.user.delete", "personal.access.token.delete" ] + initiator_id: + description: The ID of the initiator of the event. E.g., an ID of a user that triggered the event. + type: string + target_id: + description: The ID of the target of the event. E.g., an ID of the peer that a user removed. + type: string + meta: + description: The metadata of the event + type: object + additionalProperties: + type: string + required: + - id + - timestamp + - activity + - activity_code + - initiator_id + - target_id + - meta + responses: + not_found: + description: Resource not found + content: { } + validation_failed_simple: + description: Validation failed + content: { } + bad_request: + description: Bad Request + content: { } + internal_error: + description: Internal Server Error + content: { } + validation_failed: + description: Validation failed + content: { } + forbidden: + description: Forbidden + content: { } + requires_authentication: + description: Requires authentication + content: { } + securitySchemes: + BearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + TokenAuth: + type: apiKey + in: header + name: Authorization + description: >- + Enter the token with the `Token` prefix, e.g. "Token nbp_F3f0d.....". +security: + - BearerAuth: [ ] + - TokenAuth: [ ] +paths: + /api/accounts: + get: + summary: List all Accounts + description: Returns a list of accounts of a user. Always returns a list of one account. + tags: [ Accounts ] + operationId: getAccounts + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + responses: + '200': + description: A JSON array of accounts + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Account' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/accounts/{accountId}: + put: + summary: Update an Account + description: Update information about an account + tags: [ Accounts ] + operationId: updateAccount + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: accountId + required: true + schema: + type: string + description: The unique identifier of an account + requestBody: + description: update an account + content: + 'application/json': + schema: + $ref: '#/components/schemas/AccountSettings' + responses: + '200': + description: An Account object + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/users: + get: + summary: List all Users + description: Returns a list of all users + tags: [ Users ] + operationId: getUsers + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: query + name: service_user + schema: + type: boolean + description: Filters users and returns either regular users or service users + responses: + '200': + description: A JSON array of Users + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + post: + summary: Create a User + description: Creates a new service user or sends an invite to a regular user + tags: [ Users ] + operationId: createUser + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + requestBody: + description: User invite information + content: + 'application/json': + schema: + $ref: '#/components/schemas/UserCreateRequest' + responses: + '200': + description: A User object + content: + application/json: + schema: + $ref: '#/components/schemas/User' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/users/{userId}: + put: + summary: Update a User + description: Update information about a User + tags: [ Users ] + operationId: updateUser + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: userId + required: true + schema: + type: string + description: The unique identifier of a user + requestBody: + description: User update + content: + 'application/json': + schema: + $ref: '#/components/schemas/UserRequest' + responses: + '200': + description: A User object + content: + application/json: + schema: + $ref: '#/components/schemas/User' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + delete: + summary: Delete a User + description: Delete a User + tags: [ Users ] + operationId: deleteUser + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: userId + required: true + schema: + type: string + description: The unique identifier of a user + responses: + '200': + description: Delete status code + content: { } + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/users/{userId}/tokens: + get: + summary: List all Tokens + description: Returns a list of all tokens for a user + tags: [ Tokens ] + operationId: getTokens + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: userId + required: true + schema: + type: string + description: The unique identifier of a user + responses: + '200': + description: A JSON Array of PersonalAccessTokens + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/PersonalAccessToken' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + post: + summary: Create a Token + description: Create a new token for a user + tags: [ Tokens ] + operationId: createToken + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: userId + required: true + schema: + type: string + description: The unique identifier of a user + requestBody: + description: PersonalAccessToken create parameters + content: + application/json: + schema: + $ref: '#/components/schemas/PersonalAccessTokenRequest' + responses: + '200': + description: The token in plain text + content: + application/json: + schema: + $ref: '#/components/schemas/PersonalAccessTokenGenerated' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/users/{userId}/tokens/{tokenId}: + get: + summary: Retrieve a Token + description: Returns a specific token for a user + tags: [ Tokens ] + operationId: getToken + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: userId + required: true + schema: + type: string + description: The unique identifier of a user + - in: path + name: tokenId + required: true + schema: + type: string + description: The unique identifier of a token + responses: + '200': + description: A PersonalAccessTokens Object + content: + application/json: + schema: + $ref: '#/components/schemas/PersonalAccessToken' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + delete: + summary: Delete a Token + description: Delete a token for a user + tags: [ Tokens ] + operationId: deleteToken + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: userId + required: true + schema: + type: string + description: The unique identifier of a user + - in: path + name: tokenId + required: true + schema: + type: string + description: The unique identifier of a token + responses: + '200': + description: Delete status code + content: { } + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/peers: + get: + summary: List all Peers + description: Returns a list of all peers + tags: [ Peers ] + operationId: getPeers + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + responses: + '200': + description: A JSON Array of Peers + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Peer' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/peers/{peerId}: + get: + summary: Retrieve a Peer + description: Get information about a peer + tags: [ Peers ] + operationId: getPeer + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: peerId + required: true + schema: + type: string + description: The unique identifier of a peer + responses: + '200': + description: A Peer object + content: + application/json: + schema: + $ref: '#/components/schemas/Peer' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + put: + summary: Update a Peer + description: Update information about a peer + tags: [ Peers ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: peerId + required: true + schema: + type: string + description: The unique identifier of a peer + requestBody: + description: update a peer + content: + 'application/json': + schema: + $ref: '#/components/schemas/PeerRequest' + responses: + '200': + description: A Peer object + content: + application/json: + schema: + $ref: '#/components/schemas/Peer' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + delete: + summary: Delete a Peer + description: Delete a peer + tags: [ Peers ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: peerId + required: true + schema: + type: string + description: The unique identifier of a peer + responses: + '200': + description: Delete status code + content: { } + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/setup-keys: + get: + summary: List all Setup Keys + description: Returns a list of all Setup Keys + tags: [ Setup Keys ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + responses: + '200': + description: A JSON Array of Setup keys + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/SetupKey' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + post: + summary: Create a Setup Key + description: Creates a Setup Key + tags: [ Setup Keys ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + requestBody: + description: New Setup Key request + content: + 'application/json': + schema: + $ref: '#/components/schemas/SetupKeyRequest' + responses: + '200': + description: A Setup Keys Object + content: + application/json: + schema: + $ref: '#/components/schemas/SetupKey' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/setup-keys/{keyId}: + get: + summary: Retrieve a Setup Key + description: Get information about a Setup Key + tags: [ Setup Keys ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: keyId + required: true + schema: + type: string + description: The unique identifier of a setup key + responses: + '200': + description: A Setup Key object + content: + application/json: + schema: + $ref: '#/components/schemas/SetupKey' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + put: + summary: Update a Setup Key + description: Update information about a Setup Key + tags: [ Setup Keys ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: keyId + required: true + schema: + type: string + description: The unique identifier of a setup key + requestBody: + description: update to Setup Key + content: + 'application/json': + schema: + $ref: '#/components/schemas/SetupKeyRequest' + responses: + '200': + description: A Setup Key object + content: + application/json: + schema: + $ref: '#/components/schemas/SetupKey' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/groups: + get: + summary: List all Groups + description: Returns a list of all Groups + tags: [ Groups ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + responses: + '200': + description: A JSON Array of Groups + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Group' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + post: + summary: Create a Group + description: Creates a Group + tags: [ Groups ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + requestBody: + description: New Group request + content: + 'application/json': + schema: + $ref: '#/components/schemas/GroupRequest' + responses: + '200': + description: A Group Object + content: + application/json: + schema: + $ref: '#/components/schemas/Group' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/groups/{groupId}: + get: + summary: Retrieve a Group + description: Get information about a Group + tags: [ Groups ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: groupId + required: true + schema: + type: string + description: The unique identifier of a group + responses: + '200': + description: A Group object + content: + application/json: + schema: + $ref: '#/components/schemas/Group' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + put: + summary: Update a Group + description: Update/Replace a Group + tags: [ Groups ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: groupId + required: true + schema: + type: string + description: The unique identifier of a group + requestBody: + description: Update Group request + content: + 'application/json': + schema: + $ref: '#/components/schemas/GroupRequest' + responses: + '200': + description: A Group object + content: + application/json: + schema: + $ref: '#/components/schemas/Group' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + delete: + summary: Delete a Group + description: Delete a Group + tags: [ Groups ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: groupId + required: true + schema: + type: string + description: The unique identifier of a group + responses: + '200': + description: Delete status code + content: { } + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/rules: + get: + summary: List all Rules + description: Returns a list of all Rules + tags: [ Rules ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + responses: + '200': + description: A JSON Array of Rules + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Rule' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + post: + summary: Create a Rule + description: Creates a Rule + tags: [ Rules ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + requestBody: + description: New Rule request + content: + 'application/json': + schema: + $ref: '#/components/schemas/RuleRequest' + responses: + '200': + description: A Rule Object + content: + application/json: + schema: + $ref: '#/components/schemas/Rule' + /api/rules/{ruleId}: + get: + summary: Retrieve a Rule + description: Get information about a Rules + tags: [ Rules ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: ruleId + required: true + schema: + type: string + description: The unique identifier of a rule + responses: + '200': + description: A Rule object + content: + application/json: + schema: + $ref: '#/components/schemas/Rule' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + put: + summary: Update a Rule + description: Update/Replace a Rule + tags: [ Rules ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: ruleId + required: true + schema: + type: string + description: The unique identifier of a rule + requestBody: + description: Update Rule request + content: + 'application/json': + schema: + $ref: '#/components/schemas/RuleRequest' + responses: + '200': + description: A Rule object + content: + application/json: + schema: + $ref: '#/components/schemas/Rule' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + delete: + summary: Delete a Rule + description: Delete a Rule + tags: [ Rules ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: ruleId + required: true + schema: + type: string + description: The unique identifier of a rule + responses: + '200': + description: Delete status code + content: { } + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/policies: + get: + summary: List all Policies + description: Returns a list of all Policies + tags: [ Policies ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + responses: + '200': + description: A JSON Array of Policies + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Policy' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + post: + summary: Create a Policy + description: Creates a Policy + tags: [ Policies ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + requestBody: + description: New Policy request + content: + 'application/json': + schema: + $ref: '#/components/schemas/PolicyMinimum' + responses: + '200': + description: A Policy Object + content: + application/json: + schema: + $ref: '#/components/schemas/Policy' + /api/policies/{policyId}: + get: + summary: Retrieve a Policy + description: Get information about a Policies + tags: [ Policies ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: policyId + required: true + schema: + type: string + description: The unique identifier of a policy + responses: + '200': + description: A Policy object + content: + application/json: + schema: + $ref: '#/components/schemas/Policy' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + put: + summary: Update a Policy + description: Update/Replace a Policy + tags: [ Policies ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: policyId + required: true + schema: + type: string + description: The unique identifier of a policy + requestBody: + description: Update Policy request + content: + 'application/json': + schema: + $ref: '#/components/schemas/PolicyMinimum' + responses: + '200': + description: A Policy object + content: + application/json: + schema: + $ref: '#/components/schemas/Policy' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + delete: + summary: Delete a Policy + description: Delete a Policy + tags: [ Policies ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: policyId + required: true + schema: + type: string + description: The unique identifier of a policy + responses: + '200': + description: Delete status code + content: { } + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/routes: + get: + summary: List all Routes + description: Returns a list of all routes + tags: [ Routes ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + responses: + '200': + description: A JSON Array of Routes + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Route' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + post: + summary: Create a Route + description: Creates a Route + tags: [ Routes ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + requestBody: + description: New Routes request + content: + 'application/json': + schema: + $ref: '#/components/schemas/RouteRequest' + responses: + '200': + description: A Route Object + content: + application/json: + schema: + $ref: '#/components/schemas/Route' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + + /api/routes/{routeId}: + get: + summary: Retrieve a Route + description: Get information about a Routes + tags: [ Routes ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: routeId + required: true + schema: + type: string + description: The unique identifier of a route + responses: + '200': + description: A Route object + content: + application/json: + schema: + $ref: '#/components/schemas/Route' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + put: + summary: Update a Route + description: Update/Replace a Route + tags: [ Routes ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: routeId + required: true + schema: + type: string + description: The unique identifier of a route + requestBody: + description: Update Route request + content: + application/json: + schema: + $ref: '#/components/schemas/RouteRequest' + responses: + '200': + description: A Route object + content: + application/json: + schema: + $ref: '#/components/schemas/Route' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + delete: + summary: Delete a Route + description: Delete a Route + tags: [ Routes ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: routeId + required: true + schema: + type: string + description: The unique identifier of a route + responses: + '200': + description: Delete status code + content: { } + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/dns/nameservers: + get: + summary: List all Nameserver Groups + description: Returns a list of all Nameserver Groups + tags: [ DNS ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + responses: + '200': + description: A JSON Array of Nameserver Groups + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/NameserverGroup' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + post: + summary: Create a Nameserver Group + description: Creates a Nameserver Group + tags: [ DNS ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + requestBody: + description: New Nameserver Groups request + content: + 'application/json': + schema: + $ref: '#/components/schemas/NameserverGroupRequest' + responses: + '200': + description: A Nameserver Groups Object + content: + application/json: + schema: + $ref: '#/components/schemas/NameserverGroup' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/dns/nameservers/{nsgroupId}: + get: + summary: Retrieve a Nameserver Group + description: Get information about a Nameserver Groups + tags: [ DNS ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: nsgroupId + required: true + schema: + type: string + description: The unique identifier of a Nameserver Group + responses: + '200': + description: A Nameserver Group object + content: + application/json: + schema: + $ref: '#/components/schemas/NameserverGroup' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + put: + summary: Update a Nameserver Group + description: Update/Replace a Nameserver Group + tags: [ DNS ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: nsgroupId + required: true + schema: + type: string + description: The unique identifier of a Nameserver Group + requestBody: + description: Update Nameserver Group request + content: + application/json: + schema: + $ref: '#/components/schemas/NameserverGroupRequest' + responses: + '200': + description: A Nameserver Group object + content: + application/json: + schema: + $ref: '#/components/schemas/NameserverGroup' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + delete: + summary: Delete a Nameserver Group + description: Delete a Nameserver Group + tags: [ DNS ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: nsgroupId + required: true + schema: + type: string + description: The unique identifier of a Nameserver Group + responses: + '200': + description: Delete status code + content: { } + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/dns/settings: + get: + summary: Retrieve DNS Settings + description: Returns a DNS settings object + tags: [ DNS ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + responses: + '200': + description: A JSON Object of DNS Setting + content: + application/json: + schema: + items: + $ref: '#/components/schemas/DNSSettings' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + put: + summary: Update DNS Settings + description: Updates a DNS settings object + tags: [ DNS ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + requestBody: + description: A DNS settings object + content: + 'application/json': + schema: + $ref: '#/components/schemas/DNSSettings' + responses: + '200': + description: A JSON Object of DNS Setting + content: + application/json: + schema: + $ref: '#/components/schemas/DNSSettings' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/events: + get: + summary: List all Events + description: Returns a list of all events + tags: [ Events ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + responses: + '200': + description: A JSON Array of Events + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Event' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" diff --git a/generator/templates/ApiTemplate.ts b/generator/templates/ApiTemplate.ts new file mode 100644 index 00000000..77db07fa --- /dev/null +++ b/generator/templates/ApiTemplate.ts @@ -0,0 +1,123 @@ +const template = ` + + +--- + +<% operations.forEach(function(operation){ %> + +## <%- operation.summary %> {{ tag: '<%- operation.operation.toUpperCase() %>' , label: '<%- operation.path %>' }} + + + + <%- operation.description %> + <% if(operation.parameters && operation.parameters.filter((parameter) => parameter.in === 'path').length > 0){ %> + #### Path Parameters + + <% operation.parameters.filter((parameter) => parameter.in === 'path').forEach(function(parameter){ %> + + <%- parameter.description %> + + <% }); -%> + + <% }; -%> + <% if(operation.parameters && operation.parameters.filter((parameter) => parameter.in === 'query').length > 0){ %> + #### Query Parameters + + <% operation.parameters.filter((parameter) => parameter.in === 'query').forEach(function(parameter){ %> + + <%- parameter.description %> + + <% }); -%> + + <% }; -%> + <% if(operation.requestBody?.content && operation.requestBody?.content['application/json']){ %> + #### Request-Body Parameters + + <% schemas.get(operation.requestBody?.content['application/json'].schema.$ref.split('/').pop())?.parameters.forEach(function(parameter){ %> + \} + <% if(parameter.enum){ %> + enumList="<%- parameter.enum %>" + <% }; -%> + <% if(parameter.minimum){ %> + min=\{<%- parameter.minimum %>\} + <% }; -%> + <% if(parameter.maximum){ %> + max=\{<%- parameter.maximum %>\} + <% }; -%> + <% if(parameter.minLength){ %> + minLen=\{<%- parameter.minLength %>\} + <% }; -%> + <% if(parameter.maxLength){ %> + maxLen=\{<%- parameter.maxLength %>\} + <% }; -%> > + <%- parameter.description %> + + <% }); -%> + + <% }; -%> + + + + +\`\`\`bash {{ title: 'cURL' }} +curl -X <%- operation.operation.toUpperCase() %> <%- operation.fullPath %> \\ +-H "Authorization: Bearer {token}" \\ +<% if(operation.responseList[0].content && operation.responseList[0].content['application/json']){ -%> +-H 'Accept: application/json' \\<% }; %> +<% if(operation.requestBody?.content && operation.requestBody?.content['application/json']){ -%> +-H 'Content-Type: application/json' \\ +--data-raw '<%- JSON.stringify(schemas.get(operation.requestBody?.content['application/json'].schema.$ref?.split('/').pop())?.examples, null, 2) %>'<% }; %> +\`\`\` + + \`\`\`js + import ApiClient from '@example/protocol-api' + + const client = new ApiClient(token) + + await client.contacts.update('WAz8eIbvDR60rouK', { + display_name: 'UncleFrank', + }) + \`\`\` + + \`\`\`python + from protocol_api import ApiClient + + client = ApiClient(token) + + client.contacts.update("WAz8eIbvDR60rouK", display_name="UncleFrank") + \`\`\` + + \`\`\`php + $client = new \\Protocol\\ApiClient($token); + + $client->contacts->update('WAz8eIbvDR60rouK', [ + 'display_name' => 'UncleFrank', + ]); + \`\`\` + + + <% operation.responseList.forEach(function(response){ %> + <% if(response?.content && response?.content['application/json']){ %> + <% if(response?.content['application/json'].schema.type === 'array'){ %> + +\`\`\`json {{ title: '200' }} +<%- JSON.stringify(new Array(schemas.get(response?.content['application/json'].schema.items.$ref?.split('/').pop())?.examples), null, 2) %> +\`\`\` + + <% } else { %> + +\`\`\`json {{ title: '200' }} +<%- JSON.stringify(schemas.get(response?.content['application/json'].schema.$ref?.split('/').pop())?.examples, null, 2) %> +\`\`\` + + <% }; -%> + <% }; -%> + <% }); -%> + + + +--- +<% }); -%> +`.trim() + +export default template diff --git a/generator/templates/ApiTemplateBack.ts b/generator/templates/ApiTemplateBack.ts new file mode 100644 index 00000000..d27a424d --- /dev/null +++ b/generator/templates/ApiTemplateBack.ts @@ -0,0 +1,144 @@ +const template = ` +--- +id: usage +slug: /usage +title: Usage +toc_max_heading_level: 3 +--- + + + + +<%- info.description %> + + + + +<% sections.forEach(function(section){ %> + +## <%- section.title %> [#<%= section.id %>] + +<%- section.description %> + +<% section.operations.forEach(function(operation){ %> + + + +### <%- operation.summary %> [#<%- operation.operationId %>] + +\`\`\` +<%- operation.operation.toUpperCase() %> <%- operation.fullPath %> +\`\`\` + + + +<% if(operation.parameters && operation.parameters.filter((parameter) => parameter.in === 'path').length > 0){ %> +#### Path Parameters + +
    +<% operation.parameters +.filter((parameter) => parameter.in === 'path').forEach(function(parameter){ %> +
  • +

    + + <%- parameter.name %> + + + <%- parameter.required ? 'required' : 'optional' %> + + + <%- parameter.type %> + +

    + <% if(parameter.example){ %> +

    + Example: + + <%- parameter.example %> + +

    + <% } %> +
    + <%- parameter.description %> +
    +
  • +<% }); %> +
+<% }; %> + +<% if(operation.parameters && operation.parameters.filter((parameter) => parameter.in === 'header').length > 0){ %> +#### Header Parameters +
    +<% operation.parameters +.filter((parameter) => parameter.in === 'header').forEach(function(parameter){ %> +
  • +

    + + <%- parameter.name %> + + + <%- parameter.required ? 'required' : 'optional' %> + + + <%- parameter.type %> + +

    + <% if(parameter.example){ %> +

    + Example: + + <%- parameter.example %> + +

    + <% } %> +
    + <%- parameter.description %> +
    +
  • +<% }); %> +
+<% }; %> + + + + +<% if(operation.requestBody?.content && operation.requestBody?.content['application/json']){ %> +#### Body Parameters + +\`\`\`json +<%- JSON.stringify(operation.requestBody?.content['application/json'], null, 2) %> +\`\`\` +<% }; %> + + + +#### Responses + + +<% operation.responseList.forEach(function(response){ %> + + +<%- response.description %> + + + +<% if(response?.content && response?.content['application/json']){ %> +\`\`\`json +<%- JSON.stringify(response.content['application/json'], null, 2) %> +\`\`\` +<% }; %> + + + + +<% }); %> + + +
+<% }); %> +<% }); %> + + +`.trim() + +export default template diff --git a/generator/tsconfig.json b/generator/tsconfig.json new file mode 100644 index 00000000..8615cb56 --- /dev/null +++ b/generator/tsconfig.json @@ -0,0 +1,13 @@ +// generator files need their own tsconfig.json because they don't like "module": "esnext" setting that nextjs requires in the main tsconfig +{ + "compilerOptions": { + "incremental": true, + "noImplicitAny": false, + "baseUrl": ".", + "paths": { + "~/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/generator/types/CliSpec.ts b/generator/types/CliSpec.ts new file mode 100644 index 00000000..8c3515e7 --- /dev/null +++ b/generator/types/CliSpec.ts @@ -0,0 +1,34 @@ +import { SpecLink } from './Spec' +import { Url } from 'url' + +export type CliInfo = { + id: string + version: string + title: string + language: string + source: Url + bugs: Url + spec: Url + description: string + options: string +} + +export type CliCommand = { + id: string + title: string + summary: string + description: string + tags: string[] + links: SpecLink[] + usage: string + subcommands: string[] + options: string +} + +export interface CliSpec { + clispec: '001' + + info: CliInfo + + commands: CliCommand[] +} diff --git a/generator/types/ConfigSpec.ts b/generator/types/ConfigSpec.ts new file mode 100644 index 00000000..6981b9b4 --- /dev/null +++ b/generator/types/ConfigSpec.ts @@ -0,0 +1,36 @@ +import { SpecLink } from './Spec' +import { Url } from 'url' + +export type Tag = { + id: string + title: string + description?: string +} + +export type ConfigInfo = { + id: string + version: string + title: string + source: Url + bugs: Url + spec: Url + description: string + tags: Tag[] +} + +export type ConfigParameter = { + id: string + title: string + tags: string[] + required: boolean + description: string + links: SpecLink[] +} + +export interface ConfigSpec { + configspec: '001' + + info: ConfigInfo + + parameters: ConfigParameter[] +} diff --git a/generator/types/SdkSpec.ts b/generator/types/SdkSpec.ts new file mode 100644 index 00000000..9664d2e2 --- /dev/null +++ b/generator/types/SdkSpec.ts @@ -0,0 +1,75 @@ +import { SpecLink } from './Spec' +import { Url } from 'url' + +export type SdkInfo = { + id: string + version: string + title: string + language: string + source: Url + bugs: Url + spec: Url + description: string + options: string +} + +export type SdkType = { + id: string + title: string + summary: string + source: Url + value: string + ref?: SdkType + links: SpecLink[] +} + +export type FunctionAttribute = { + id: string + title: string + required: boolean + description: string + type?: string[] + ref?: string // If a "type" is not supplied, a "ref" must be. This is a pointer to a type. + children: FunctionAttribute[] +} + +export type FunctionReturn = { + id: string + title: string + value: string + description: string + ref?: string // This is a pointer to a type. +} + +export type FunctionExample = { + id: string + title: string + description?: string + links: SpecLink[] + code: string + returns?: FunctionReturn +} + +export type Function = { + id: string + title: string + summary: string + source: Url + description?: string + usage: string + tags: string[] + links: SpecLink[] + attributes?: FunctionAttribute[] + returns?: FunctionReturn[] + examples?: FunctionExample[] +} + +export interface SdkSpec { + sdkspec: '001' + + info: SdkInfo + + functions: Function[] + + types: SdkType[] +} diff --git a/generator/types/Spec.ts b/generator/types/Spec.ts new file mode 100644 index 00000000..6f6c2c72 --- /dev/null +++ b/generator/types/Spec.ts @@ -0,0 +1,6 @@ +import { Url } from 'url' + +export type SpecLink = { + name: string + url: Url +} diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 00000000..2c8ee2bb --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } + } +} diff --git a/mdx/recma.mjs b/mdx/recma.mjs new file mode 100644 index 00000000..48901160 --- /dev/null +++ b/mdx/recma.mjs @@ -0,0 +1,19 @@ +import { mdxAnnotations } from 'mdx-annotations' +import recmaNextjsStaticProps from 'recma-nextjs-static-props' + +function recmaRemoveNamedExports() { + return (tree) => { + tree.body = tree.body.map((node) => { + if (node.type === 'ExportNamedDeclaration') { + return node.declaration + } + return node + }) + } +} + +export const recmaPlugins = [ + mdxAnnotations.recma, + recmaRemoveNamedExports, + recmaNextjsStaticProps, +] diff --git a/mdx/rehype.mjs b/mdx/rehype.mjs new file mode 100644 index 00000000..d13c65aa --- /dev/null +++ b/mdx/rehype.mjs @@ -0,0 +1,126 @@ +import { mdxAnnotations } from 'mdx-annotations' +import { visit } from 'unist-util-visit' +import rehypeMdxTitle from 'rehype-mdx-title' +import shiki from 'shiki' +import { toString } from 'mdast-util-to-string' +import * as acorn from 'acorn' +import { slugifyWithCounter } from '@sindresorhus/slugify' + +function rehypeParseCodeBlocks() { + return (tree) => { + visit(tree, 'element', (node, _nodeIndex, parentNode) => { + if (node.tagName === 'code' && node.properties.className) { + parentNode.properties.language = node.properties.className[0]?.replace( + /^language-/, + '' + ) + } + }) + } +} + +let highlighter + +function rehypeShiki() { + return async (tree) => { + highlighter = + highlighter ?? (await shiki.getHighlighter({ theme: 'css-variables' })) + + visit(tree, 'element', (node) => { + if (node.tagName === 'pre' && node.children[0]?.tagName === 'code') { + let codeNode = node.children[0] + let textNode = codeNode.children[0] + + node.properties.code = textNode.value + + if (node.properties.language) { + let tokens = highlighter.codeToThemedTokens( + textNode.value, + node.properties.language + ) + + textNode.value = shiki.renderToHtml(tokens, { + elements: { + pre: ({ children }) => children, + code: ({ children }) => children, + line: ({ children }) => `${children}`, + }, + }) + } + } + }) + } +} + +function rehypeSlugify() { + return (tree) => { + let slugify = slugifyWithCounter() + visit(tree, 'element', (node) => { + if (node.tagName === 'h2' && !node.properties.id) { + node.properties.id = slugify(toString(node)) + } + }) + } +} + +function rehypeAddMDXExports(getExports) { + return (tree) => { + let exports = Object.entries(getExports(tree)) + + for (let [name, value] of exports) { + for (let node of tree.children) { + if ( + node.type === 'mdxjsEsm' && + new RegExp(`export\\s+const\\s+${name}\\s*=`).test(node.value) + ) { + return + } + } + + let exportStr = `export const ${name} = ${value}` + + tree.children.push({ + type: 'mdxjsEsm', + value: exportStr, + data: { + estree: acorn.parse(exportStr, { + sourceType: 'module', + ecmaVersion: 'latest', + }), + }, + }) + } + } +} + +function getSections(node) { + let sections = [] + + for (let child of node.children ?? []) { + if (child.type === 'element' && child.tagName === 'h2') { + sections.push(`{ + title: ${JSON.stringify(toString(child))}, + id: ${JSON.stringify(child.properties.id)}, + ...${child.properties.annotation} + }`) + } else if (child.children) { + sections.push(...getSections(child)) + } + } + + return sections +} + +export const rehypePlugins = [ + mdxAnnotations.rehype, + rehypeParseCodeBlocks, + rehypeShiki, + rehypeSlugify, + rehypeMdxTitle, + [ + rehypeAddMDXExports, + (tree) => ({ + sections: `[${getSections(tree).join()}]`, + }), + ], +] diff --git a/mdx/remark.mjs b/mdx/remark.mjs new file mode 100644 index 00000000..e523464b --- /dev/null +++ b/mdx/remark.mjs @@ -0,0 +1,4 @@ +import { mdxAnnotations } from 'mdx-annotations' +import remarkGfm from 'remark-gfm' + +export const remarkPlugins = [mdxAnnotations.remark, remarkGfm] diff --git a/next.config.mjs b/next.config.mjs new file mode 100644 index 00000000..b5482b54 --- /dev/null +++ b/next.config.mjs @@ -0,0 +1,33 @@ +import nextMDX from '@next/mdx' +import { remarkPlugins } from './mdx/remark.mjs' +import { rehypePlugins } from './mdx/rehype.mjs' +import { recmaPlugins } from './mdx/recma.mjs' + +const withMDX = nextMDX({ + options: { + remarkPlugins, + rehypePlugins, + recmaPlugins, + providerImportSource: '@mdx-js/react', + }, +}) + +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, + pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'mdx'], + experimental: { + scrollRestoration: true, + }, + redirects: async () => { + return [ + { + source: '/', + destination: '/docs/introduction', + permanent: true, + }, + ] + } +} + +export default withMDX(nextConfig) diff --git a/package.json b/package.json new file mode 100644 index 00000000..8bba1dda --- /dev/null +++ b/package.json @@ -0,0 +1,50 @@ +{ + "name": "wiretrustee-docs", + "version": "1.0.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "browserslist": "defaults, not ie <= 11", + "dependencies": { + "@algolia/autocomplete-core": "^1.7.3", + "@algolia/autocomplete-preset-algolia": "^1.7.3", + "@headlessui/react": "^1.7.13", + "@mdx-js/loader": "^2.1.5", + "@mdx-js/react": "^2.1.5", + "@next/mdx": "^13.0.3", + "@sindresorhus/slugify": "^2.1.1", + "@tailwindcss/typography": "^0.5.8", + "acorn": "^8.8.1", + "algoliasearch": "^4.14.2", + "autoprefixer": "^10.4.7", + "clsx": "^1.2.0", + "ejs": "^3.1.9", + "focus-visible": "^5.2.0", + "framer-motion": "7.8.1", + "lodash": "^4.17.21", + "mdast-util-to-string": "^3.1.0", + "mdx-annotations": "^0.1.1", + "next": "13.3.0", + "openapi-types": "^12.1.0", + "postcss-focus-visible": "^6.0.4", + "react": "18.2.0", + "react-dom": "18.2.0", + "recma-nextjs-static-props": "^1.0.0", + "rehype-mdx-title": "^2.0.0", + "remark-gfm": "^3.0.1", + "shiki": "^0.11.1", + "tailwindcss": "^3.3.0", + "unist-util-visit": "^4.1.1", + "zustand": "^4.3.2" + }, + "devDependencies": { + "eslint": "8.26.0", + "eslint-config-next": "13.0.2", + "prettier": "^2.8.7", + "prettier-plugin-tailwindcss": "^0.2.6" + } +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 00000000..6573c253 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,9 @@ +module.exports = { + plugins: { + tailwindcss: {}, + 'postcss-focus-visible': { + replaceWith: '[data-focus-visible-added]', + }, + autoprefixer: {}, + }, +} diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 00000000..35bb2b2a --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,5 @@ +module.exports = { + singleQuote: true, + semi: false, + plugins: [require('prettier-plugin-tailwindcss')], +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 00000000..2deafb7b Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/img/architecture/acls.png b/public/img/architecture/acls.png new file mode 100644 index 00000000..271083fa Binary files /dev/null and b/public/img/architecture/acls.png differ diff --git a/public/img/architecture/high-level-dia.png b/public/img/architecture/high-level-dia.png new file mode 100644 index 00000000..71066048 Binary files /dev/null and b/public/img/architecture/high-level-dia.png differ diff --git a/public/img/architecture/management.png b/public/img/architecture/management.png new file mode 100644 index 00000000..3b22aedf Binary files /dev/null and b/public/img/architecture/management.png differ diff --git a/public/img/architecture/mesh.png b/public/img/architecture/mesh.png new file mode 100644 index 00000000..12ac5292 Binary files /dev/null and b/public/img/architecture/mesh.png differ diff --git a/public/img/architecture/netbird-peer-auto-tagging-newkey.png b/public/img/architecture/netbird-peer-auto-tagging-newkey.png new file mode 100644 index 00000000..5be065eb Binary files /dev/null and b/public/img/architecture/netbird-peer-auto-tagging-newkey.png differ diff --git a/public/img/architecture/peer-auto-tagging-setupkey.gif b/public/img/architecture/peer-auto-tagging-setupkey.gif new file mode 100644 index 00000000..e9902166 Binary files /dev/null and b/public/img/architecture/peer-auto-tagging-setupkey.gif differ diff --git a/public/img/architecture/relay.png b/public/img/architecture/relay.png new file mode 100644 index 00000000..c95d503e Binary files /dev/null and b/public/img/architecture/relay.png differ diff --git a/public/img/architecture/setup-keys.png b/public/img/architecture/setup-keys.png new file mode 100644 index 00000000..1b703d86 Binary files /dev/null and b/public/img/architecture/setup-keys.png differ diff --git a/public/img/architecture/signal.png b/public/img/architecture/signal.png new file mode 100644 index 00000000..4c575029 Binary files /dev/null and b/public/img/architecture/signal.png differ diff --git a/public/img/examples/wiretrustee-on-aws-ecs.png b/public/img/examples/wiretrustee-on-aws-ecs.png new file mode 100644 index 00000000..9571bd56 Binary files /dev/null and b/public/img/examples/wiretrustee-on-aws-ecs.png differ diff --git a/public/img/favicon.ico b/public/img/favicon.ico new file mode 100644 index 00000000..2bab8a50 Binary files /dev/null and b/public/img/favicon.ico differ diff --git a/public/img/getting-started/add-peer.png b/public/img/getting-started/add-peer.png new file mode 100644 index 00000000..ff4a0471 Binary files /dev/null and b/public/img/getting-started/add-peer.png differ diff --git a/public/img/getting-started/auth.png b/public/img/getting-started/auth.png new file mode 100644 index 00000000..71551c0f Binary files /dev/null and b/public/img/getting-started/auth.png differ diff --git a/public/img/getting-started/device-confirmation.png b/public/img/getting-started/device-confirmation.png new file mode 100644 index 00000000..deee99cd Binary files /dev/null and b/public/img/getting-started/device-confirmation.png differ diff --git a/public/img/getting-started/empty-peers.png b/public/img/getting-started/empty-peers.png new file mode 100644 index 00000000..d98127f3 Binary files /dev/null and b/public/img/getting-started/empty-peers.png differ diff --git a/public/img/getting-started/high-level-dia.png b/public/img/getting-started/high-level-dia.png new file mode 100644 index 00000000..8fc08a5a Binary files /dev/null and b/public/img/getting-started/high-level-dia.png differ diff --git a/public/img/getting-started/logo-full.png b/public/img/getting-started/logo-full.png new file mode 100644 index 00000000..13ce625a Binary files /dev/null and b/public/img/getting-started/logo-full.png differ diff --git a/public/img/getting-started/logo.png b/public/img/getting-started/logo.png new file mode 100644 index 00000000..4e880972 Binary files /dev/null and b/public/img/getting-started/logo.png differ diff --git a/public/img/getting-started/netbird-sso-login-cmd.gif b/public/img/getting-started/netbird-sso-login-cmd.gif new file mode 100644 index 00000000..eb46e83a Binary files /dev/null and b/public/img/getting-started/netbird-sso-login-cmd.gif differ diff --git a/public/img/getting-started/netbird-sso-login-ui.gif b/public/img/getting-started/netbird-sso-login-ui.gif new file mode 100644 index 00000000..ddf3cacc Binary files /dev/null and b/public/img/getting-started/netbird-sso-login-ui.gif differ diff --git a/public/img/getting-started/netbird-up.png b/public/img/getting-started/netbird-up.png new file mode 100644 index 00000000..08bb2083 Binary files /dev/null and b/public/img/getting-started/netbird-up.png differ diff --git a/public/img/getting-started/peerA.gif b/public/img/getting-started/peerA.gif new file mode 100644 index 00000000..33131ec1 Binary files /dev/null and b/public/img/getting-started/peerA.gif differ diff --git a/public/img/getting-started/peerB.gif b/public/img/getting-started/peerB.gif new file mode 100644 index 00000000..c2efd91d Binary files /dev/null and b/public/img/getting-started/peerB.gif differ diff --git a/public/img/getting-started/peers.gif b/public/img/getting-started/peers.gif new file mode 100644 index 00000000..c6edfbcc Binary files /dev/null and b/public/img/getting-started/peers.gif differ diff --git a/public/img/getting-started/peers.png b/public/img/getting-started/peers.png new file mode 100644 index 00000000..1f985329 Binary files /dev/null and b/public/img/getting-started/peers.png differ diff --git a/public/img/getting-started/systray.png b/public/img/getting-started/systray.png new file mode 100644 index 00000000..90d34f01 Binary files /dev/null and b/public/img/getting-started/systray.png differ diff --git a/public/img/how-to-guides/activity-monitoring.png b/public/img/how-to-guides/activity-monitoring.png new file mode 100644 index 00000000..3b9967c9 Binary files /dev/null and b/public/img/how-to-guides/activity-monitoring.png differ diff --git a/public/img/how-to-guides/individual-peer-login-expiration.png b/public/img/how-to-guides/individual-peer-login-expiration.png new file mode 100644 index 00000000..808e74fc Binary files /dev/null and b/public/img/how-to-guides/individual-peer-login-expiration.png differ diff --git a/public/img/how-to-guides/netbird-nameserver-add-button.png b/public/img/how-to-guides/netbird-nameserver-add-button.png new file mode 100644 index 00000000..b722d899 Binary files /dev/null and b/public/img/how-to-guides/netbird-nameserver-add-button.png differ diff --git a/public/img/how-to-guides/netbird-nameserver-all-group.png b/public/img/how-to-guides/netbird-nameserver-all-group.png new file mode 100644 index 00000000..1434d019 Binary files /dev/null and b/public/img/how-to-guides/netbird-nameserver-all-group.png differ diff --git a/public/img/how-to-guides/netbird-nameserver-custom.png b/public/img/how-to-guides/netbird-nameserver-custom.png new file mode 100644 index 00000000..b5d81a65 Binary files /dev/null and b/public/img/how-to-guides/netbird-nameserver-custom.png differ diff --git a/public/img/how-to-guides/netbird-nameserver-predefined-selected.png b/public/img/how-to-guides/netbird-nameserver-predefined-selected.png new file mode 100644 index 00000000..e53707a6 Binary files /dev/null and b/public/img/how-to-guides/netbird-nameserver-predefined-selected.png differ diff --git a/public/img/how-to-guides/netbird-nameserver-remote-resolver.png b/public/img/how-to-guides/netbird-nameserver-remote-resolver.png new file mode 100644 index 00000000..e80d1485 Binary files /dev/null and b/public/img/how-to-guides/netbird-nameserver-remote-resolver.png differ diff --git a/public/img/how-to-guides/netbird-nameserver-remote-route.png b/public/img/how-to-guides/netbird-nameserver-remote-route.png new file mode 100644 index 00000000..2b7c6b81 Binary files /dev/null and b/public/img/how-to-guides/netbird-nameserver-remote-route.png differ diff --git a/public/img/how-to-guides/netbird-nameserver-remote-rule.png b/public/img/how-to-guides/netbird-nameserver-remote-rule.png new file mode 100644 index 00000000..c863ff00 Binary files /dev/null and b/public/img/how-to-guides/netbird-nameserver-remote-rule.png differ diff --git a/public/img/how-to-guides/netbird-nameserver-selection-view-open.png b/public/img/how-to-guides/netbird-nameserver-selection-view-open.png new file mode 100644 index 00000000..b43f0dcd Binary files /dev/null and b/public/img/how-to-guides/netbird-nameserver-selection-view-open.png differ diff --git a/public/img/how-to-guides/netbird-nameserver-selection-view.png b/public/img/how-to-guides/netbird-nameserver-selection-view.png new file mode 100644 index 00000000..93c5b82d Binary files /dev/null and b/public/img/how-to-guides/netbird-nameserver-selection-view.png differ diff --git a/public/img/how-to-guides/netbird-network-routes-add-button.png b/public/img/how-to-guides/netbird-network-routes-add-button.png new file mode 100644 index 00000000..4604f5c7 Binary files /dev/null and b/public/img/how-to-guides/netbird-network-routes-add-button.png differ diff --git a/public/img/how-to-guides/netbird-network-routes-create-ha.png b/public/img/how-to-guides/netbird-network-routes-create-ha.png new file mode 100644 index 00000000..0a25c099 Binary files /dev/null and b/public/img/how-to-guides/netbird-network-routes-create-ha.png differ diff --git a/public/img/how-to-guides/netbird-network-routes-create.png b/public/img/how-to-guides/netbird-network-routes-create.png new file mode 100644 index 00000000..8c7a1483 Binary files /dev/null and b/public/img/how-to-guides/netbird-network-routes-create.png differ diff --git a/public/img/how-to-guides/netbird-network-routes-masquerading.png b/public/img/how-to-guides/netbird-network-routes-masquerading.png new file mode 100644 index 00000000..f149260f Binary files /dev/null and b/public/img/how-to-guides/netbird-network-routes-masquerading.png differ diff --git a/public/img/how-to-guides/netbird-network-routes-saved-new-ha.png b/public/img/how-to-guides/netbird-network-routes-saved-new-ha.png new file mode 100644 index 00000000..94956a6e Binary files /dev/null and b/public/img/how-to-guides/netbird-network-routes-saved-new-ha.png differ diff --git a/public/img/how-to-guides/netbird-network-routes-saved-new.png b/public/img/how-to-guides/netbird-network-routes-saved-new.png new file mode 100644 index 00000000..4cc6b66e Binary files /dev/null and b/public/img/how-to-guides/netbird-network-routes-saved-new.png differ diff --git a/public/img/how-to-guides/netbird-network-routes.png b/public/img/how-to-guides/netbird-network-routes.png new file mode 100644 index 00000000..795e202e Binary files /dev/null and b/public/img/how-to-guides/netbird-network-routes.png differ diff --git a/public/img/how-to-guides/peer-login-expiration.png b/public/img/how-to-guides/peer-login-expiration.png new file mode 100644 index 00000000..e0a4029d Binary files /dev/null and b/public/img/how-to-guides/peer-login-expiration.png differ diff --git a/public/img/how-to-guides/peer-needs-login.png b/public/img/how-to-guides/peer-needs-login.png new file mode 100644 index 00000000..b3075aa6 Binary files /dev/null and b/public/img/how-to-guides/peer-needs-login.png differ diff --git a/public/img/how-to-guides/user-invites.gif b/public/img/how-to-guides/user-invites.gif new file mode 100644 index 00000000..6474efeb Binary files /dev/null and b/public/img/how-to-guides/user-invites.gif differ diff --git a/public/img/integrations/identity-providers/self-hosted/auth0-create-interactive-login-app.png b/public/img/integrations/identity-providers/self-hosted/auth0-create-interactive-login-app.png new file mode 100644 index 00000000..14d5fbb4 Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/auth0-create-interactive-login-app.png differ diff --git a/public/img/integrations/identity-providers/self-hosted/auth0-grant-types.png b/public/img/integrations/identity-providers/self-hosted/auth0-grant-types.png new file mode 100644 index 00000000..938e2c9a Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/auth0-grant-types.png differ diff --git a/public/img/integrations/identity-providers/self-hosted/auth0-interactive-login-settings.png b/public/img/integrations/identity-providers/self-hosted/auth0-interactive-login-settings.png new file mode 100644 index 00000000..da5628e8 Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/auth0-interactive-login-settings.png differ diff --git a/public/img/integrations/identity-providers/self-hosted/azure_api_scope.png b/public/img/integrations/identity-providers/self-hosted/azure_api_scope.png new file mode 100755 index 00000000..c77feace Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/azure_api_scope.png differ diff --git a/public/img/integrations/identity-providers/self-hosted/keycloack-add-client-scope.png b/public/img/integrations/identity-providers/self-hosted/keycloack-add-client-scope.png new file mode 100644 index 00000000..79001777 Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/keycloack-add-client-scope.png differ diff --git a/public/img/integrations/identity-providers/self-hosted/keycloack-domain-token-claim-mapper-domain-category.png b/public/img/integrations/identity-providers/self-hosted/keycloack-domain-token-claim-mapper-domain-category.png new file mode 100644 index 00000000..6b05b331 Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/keycloack-domain-token-claim-mapper-domain-category.png differ diff --git a/public/img/integrations/identity-providers/self-hosted/keycloack-domain-token-claim-mapper-domain.png b/public/img/integrations/identity-providers/self-hosted/keycloack-domain-token-claim-mapper-domain.png new file mode 100644 index 00000000..1d1a2aff Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/keycloack-domain-token-claim-mapper-domain.png differ diff --git a/public/img/integrations/identity-providers/self-hosted/keycloack-domain-token-claim-mapper.png b/public/img/integrations/identity-providers/self-hosted/keycloack-domain-token-claim-mapper.png new file mode 100644 index 00000000..669ee2ca Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/keycloack-domain-token-claim-mapper.png differ diff --git a/public/img/integrations/identity-providers/self-hosted/keycloack-domain-token-claims-client.png b/public/img/integrations/identity-providers/self-hosted/keycloack-domain-token-claims-client.png new file mode 100644 index 00000000..df338aef Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/keycloack-domain-token-claims-client.png differ diff --git a/public/img/integrations/identity-providers/self-hosted/keycloak-access-settings.png b/public/img/integrations/identity-providers/self-hosted/keycloak-access-settings.png new file mode 100644 index 00000000..e788a554 Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/keycloak-access-settings.png differ diff --git a/public/img/integrations/identity-providers/self-hosted/keycloak-add-role.png b/public/img/integrations/identity-providers/self-hosted/keycloak-add-role.png new file mode 100644 index 00000000..d3f46c1f Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/keycloak-add-role.png differ diff --git a/public/img/integrations/identity-providers/self-hosted/keycloak-auth-grant.gif b/public/img/integrations/identity-providers/self-hosted/keycloak-auth-grant.gif new file mode 100644 index 00000000..50d883d4 Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/keycloak-auth-grant.gif differ diff --git a/public/img/integrations/identity-providers/self-hosted/keycloak-backend-client-auth.png b/public/img/integrations/identity-providers/self-hosted/keycloak-backend-client-auth.png new file mode 100644 index 00000000..bb692d6e Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/keycloak-backend-client-auth.png differ diff --git a/public/img/integrations/identity-providers/self-hosted/keycloak-backend-client-credentials.png b/public/img/integrations/identity-providers/self-hosted/keycloak-backend-client-credentials.png new file mode 100644 index 00000000..cd675eac Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/keycloak-backend-client-credentials.png differ diff --git a/public/img/integrations/identity-providers/self-hosted/keycloak-configure-audience-mapper-2.png b/public/img/integrations/identity-providers/self-hosted/keycloak-configure-audience-mapper-2.png new file mode 100644 index 00000000..dce65781 Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/keycloak-configure-audience-mapper-2.png differ diff --git a/public/img/integrations/identity-providers/self-hosted/keycloak-configure-audience-mapper.png b/public/img/integrations/identity-providers/self-hosted/keycloak-configure-audience-mapper.png new file mode 100644 index 00000000..9a54c13c Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/keycloak-configure-audience-mapper.png differ diff --git a/public/img/integrations/identity-providers/self-hosted/keycloak-create-backend-client.png b/public/img/integrations/identity-providers/self-hosted/keycloak-create-backend-client.png new file mode 100644 index 00000000..328889c9 Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/keycloak-create-backend-client.png differ diff --git a/public/img/integrations/identity-providers/self-hosted/keycloak-create-client-scope.png b/public/img/integrations/identity-providers/self-hosted/keycloak-create-client-scope.png new file mode 100644 index 00000000..bdef79ac Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/keycloak-create-client-scope.png differ diff --git a/public/img/integrations/identity-providers/self-hosted/keycloak-create-client.png b/public/img/integrations/identity-providers/self-hosted/keycloak-create-client.png new file mode 100644 index 00000000..2dbf841d Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/keycloak-create-client.png differ diff --git a/public/img/integrations/identity-providers/self-hosted/keycloak-create-realm.png b/public/img/integrations/identity-providers/self-hosted/keycloak-create-realm.png new file mode 100644 index 00000000..29a36b2d Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/keycloak-create-realm.png differ diff --git a/public/img/integrations/identity-providers/self-hosted/keycloak-create-user.png b/public/img/integrations/identity-providers/self-hosted/keycloak-create-user.png new file mode 100644 index 00000000..c6f20772 Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/keycloak-create-user.png differ diff --git a/public/img/integrations/identity-providers/self-hosted/keycloak-domain-token-claim-scope.png b/public/img/integrations/identity-providers/self-hosted/keycloak-domain-token-claim-scope.png new file mode 100644 index 00000000..4378c3e3 Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/keycloak-domain-token-claim-scope.png differ diff --git a/public/img/integrations/identity-providers/self-hosted/keycloak-enable-auth.png b/public/img/integrations/identity-providers/self-hosted/keycloak-enable-auth.png new file mode 100644 index 00000000..a512ba69 Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/keycloak-enable-auth.png differ diff --git a/public/img/integrations/identity-providers/self-hosted/keycloak-service-account-role.png b/public/img/integrations/identity-providers/self-hosted/keycloak-service-account-role.png new file mode 100644 index 00000000..b66dac97 Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/keycloak-service-account-role.png differ diff --git a/public/img/integrations/identity-providers/self-hosted/keycloak-set-password.png b/public/img/integrations/identity-providers/self-hosted/keycloak-set-password.png new file mode 100644 index 00000000..6ddf65d6 Binary files /dev/null and b/public/img/integrations/identity-providers/self-hosted/keycloak-set-password.png differ diff --git a/public/img/logo-dark.svg b/public/img/logo-dark.svg new file mode 100644 index 00000000..068c330e --- /dev/null +++ b/public/img/logo-dark.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + DOCS + netbird + + + + + + + diff --git a/public/img/logo.svg b/public/img/logo.svg new file mode 100644 index 00000000..e76157b7 --- /dev/null +++ b/public/img/logo.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + DOCS + netbird + + + + + + + diff --git a/public/img/overview/associate-peer-groups.png b/public/img/overview/associate-peer-groups.png new file mode 100644 index 00000000..9651d4d0 Binary files /dev/null and b/public/img/overview/associate-peer-groups.png differ diff --git a/public/img/overview/create-group-in-rule.png b/public/img/overview/create-group-in-rule.png new file mode 100644 index 00000000..21a45879 Binary files /dev/null and b/public/img/overview/create-group-in-rule.png differ diff --git a/public/img/overview/create-rule.png b/public/img/overview/create-rule.png new file mode 100644 index 00000000..c76ae001 Binary files /dev/null and b/public/img/overview/create-rule.png differ diff --git a/public/img/overview/delete-rule-menu.png b/public/img/overview/delete-rule-menu.png new file mode 100644 index 00000000..f9d6d312 Binary files /dev/null and b/public/img/overview/delete-rule-menu.png differ diff --git a/public/img/overview/delete-rule-popup.png b/public/img/overview/delete-rule-popup.png new file mode 100644 index 00000000..4d4746ca Binary files /dev/null and b/public/img/overview/delete-rule-popup.png differ diff --git a/public/img/overview/new-rule-list.png b/public/img/overview/new-rule-list.png new file mode 100644 index 00000000..0624d29e Binary files /dev/null and b/public/img/overview/new-rule-list.png differ diff --git a/src/components/Button.jsx b/src/components/Button.jsx new file mode 100644 index 00000000..0565122b --- /dev/null +++ b/src/components/Button.jsx @@ -0,0 +1,65 @@ +import Link from 'next/link' +import clsx from 'clsx' + +function ArrowIcon(props) { + return ( + + ) +} + +const variantStyles = { + primary: + 'rounded-full bg-zinc-900 py-1 px-3 text-white hover:bg-zinc-700 dark:bg-orange-400/10 dark:text-orange-400 dark:ring-1 dark:ring-inset dark:ring-orange-400/20 dark:hover:bg-orange-400/10 dark:hover:text-orange-300 dark:hover:ring-orange-300', + // 'rounded-full bg-zinc-900 py-1 px-3 text-white hover:bg-zinc-700 dark:bg-emerald-400/10 dark:text-emerald-400 dark:ring-1 dark:ring-inset dark:ring-emerald-400/20 dark:hover:bg-emerald-400/10 dark:hover:text-emerald-300 dark:hover:ring-emerald-300', + secondary: + 'rounded-full bg-zinc-100 py-1 px-3 text-zinc-900 hover:bg-zinc-200 dark:bg-zinc-800/40 dark:text-zinc-400 dark:ring-1 dark:ring-inset dark:ring-zinc-800 dark:hover:bg-zinc-800 dark:hover:text-zinc-300', + filled: + 'rounded-full bg-zinc-900 py-1 px-3 text-white hover:bg-zinc-700 dark:bg-orange-500 dark:text-white dark:hover:bg-orange-400', + // 'rounded-full bg-zinc-900 py-1 px-3 text-white hover:bg-zinc-700 dark:bg-emerald-500 dark:text-white dark:hover:bg-emerald-400', + outline: + 'rounded-full py-1 px-3 text-zinc-700 ring-1 ring-inset ring-zinc-900/10 hover:bg-zinc-900/2.5 hover:text-zinc-900 dark:text-zinc-400 dark:ring-white/10 dark:hover:bg-white/5 dark:hover:text-white', + text: 'text-orange-500 hover:text-orange-600 dark:text-orange-400 dark:hover:text-orange-500', + // text: 'text-emerald-500 hover:text-emerald-600 dark:text-emerald-400 dark:hover:text-emerald-500', +} + +export function Button({ + variant = 'primary', + className, + children, + arrow, + ...props +}) { + let Component = props.href ? Link : 'button' + + className = clsx( + 'inline-flex gap-0.5 justify-center overflow-hidden text-sm font-medium transition', + variantStyles[variant], + className + ) + + let arrowIcon = ( + + ) + + return ( + + {arrow === 'left' && arrowIcon} + {children} + {arrow === 'right' && arrowIcon} + + ) +} diff --git a/src/components/Code.jsx b/src/components/Code.jsx new file mode 100644 index 00000000..fa2324e5 --- /dev/null +++ b/src/components/Code.jsx @@ -0,0 +1,299 @@ +import { + Children, + createContext, + useContext, + useEffect, + useRef, + useState, +} from 'react' +import { Tab } from '@headlessui/react' +import clsx from 'clsx' +import { create } from 'zustand' + +import { Tag } from '@/components/Tag' + +const languageNames = { + js: 'JavaScript', + ts: 'TypeScript', + javascript: 'JavaScript', + typescript: 'TypeScript', + php: 'PHP', + python: 'Python', + ruby: 'Ruby', + go: 'Go', +} + +function getPanelTitle({ title, language }) { + return title ?? languageNames[language] ?? 'Code' +} + +function ClipboardIcon(props) { + return ( + + ) +} + +function CopyButton({ code }) { + let [copyCount, setCopyCount] = useState(0) + let copied = copyCount > 0 + + useEffect(() => { + if (copyCount > 0) { + let timeout = setTimeout(() => setCopyCount(0), 1000) + return () => { + clearTimeout(timeout) + } + } + }, [copyCount]) + + return ( + + ) +} + +function CodePanelHeader({ tag, label }) { + if (!tag && !label) { + return null + } + + return ( +
+ {tag && ( +
+ {tag} +
+ )} + {tag && label && ( + + )} + {label && ( + {label} + )} +
+ ) +} + +function CodePanel({ tag, label, code, children }) { + let child = Children.only(children) + + return ( +
+ +
+
{children}
+ +
+
+ ) +} + +function CodeGroupHeader({ title, children, selectedIndex }) { + let hasTabs = Children.count(children) > 1 + + if (!title && !hasTabs) { + return null + } + + return ( +
+ {title && ( +

+ {title} +

+ )} + {hasTabs && ( + + {Children.map(children, (child, childIndex) => ( + + {getPanelTitle(child.props)} + + ))} + + )} +
+ ) +} + +function CodeGroupPanels({ children, ...props }) { + let hasTabs = Children.count(children) > 1 + + if (hasTabs) { + return ( + + {Children.map(children, (child) => ( + + {child} + + ))} + + ) + } + + return {children} +} + +function usePreventLayoutShift() { + let positionRef = useRef() + let rafRef = useRef() + + useEffect(() => { + return () => { + window.cancelAnimationFrame(rafRef.current) + } + }, []) + + return { + positionRef, + preventLayoutShift(callback) { + let initialTop = positionRef.current.getBoundingClientRect().top + + callback() + + rafRef.current = window.requestAnimationFrame(() => { + let newTop = positionRef.current.getBoundingClientRect().top + window.scrollBy(0, newTop - initialTop) + }) + }, + } +} + +const usePreferredLanguageStore = create((set) => ({ + preferredLanguages: [], + addPreferredLanguage: (language) => + set((state) => ({ + preferredLanguages: [ + ...state.preferredLanguages.filter( + (preferredLanguage) => preferredLanguage !== language + ), + language, + ], + })), +})) + +function useTabGroupProps(availableLanguages) { + let { preferredLanguages, addPreferredLanguage } = usePreferredLanguageStore() + let [selectedIndex, setSelectedIndex] = useState(0) + let activeLanguage = [...availableLanguages].sort( + (a, z) => preferredLanguages.indexOf(z) - preferredLanguages.indexOf(a) + )[0] + let languageIndex = availableLanguages.indexOf(activeLanguage) + let newSelectedIndex = languageIndex === -1 ? selectedIndex : languageIndex + if (newSelectedIndex !== selectedIndex) { + setSelectedIndex(newSelectedIndex) + } + + let { positionRef, preventLayoutShift } = usePreventLayoutShift() + + return { + as: 'div', + ref: positionRef, + selectedIndex, + onChange: (newSelectedIndex) => { + preventLayoutShift(() => + addPreferredLanguage(availableLanguages[newSelectedIndex]) + ) + }, + } +} + +const CodeGroupContext = createContext(false) + +export function CodeGroup({ children, title, ...props }) { + let languages = Children.map(children, (child) => getPanelTitle(child.props)) + let tabGroupProps = useTabGroupProps(languages) + let hasTabs = Children.count(children) > 1 + let Container = hasTabs ? Tab.Group : 'div' + let containerProps = hasTabs ? tabGroupProps : {} + let headerProps = hasTabs + ? { selectedIndex: tabGroupProps.selectedIndex } + : {} + + return ( + + + + {children} + + {children} + + + ) +} + +export function Code({ children, ...props }) { + let isGrouped = useContext(CodeGroupContext) + + if (isGrouped) { + return + } + + return {children} +} + +export function Pre({ children, ...props }) { + let isGrouped = useContext(CodeGroupContext) + + if (isGrouped) { + return children + } + + return {children} +} diff --git a/src/components/Footer.jsx b/src/components/Footer.jsx new file mode 100644 index 00000000..8886834b --- /dev/null +++ b/src/components/Footer.jsx @@ -0,0 +1,230 @@ +import {forwardRef, Fragment, useEffect, useState} from 'react' +import Link from 'next/link' +import { useRouter } from 'next/router' +import { Transition } from '@headlessui/react' + +import { Button } from '@/components/Button' +import {apiNavigation, docsNavigation, navigation} from '@/components/Navigation' + +function CheckIcon(props) { + return ( + + ) +} + +function FeedbackButton(props) { + return ( + + + {page.title} + + + ) +} + +function PageNavigation() { + let router = useRouter() + let allPages = router.route.startsWith('/docs') ? docsNavigation.flatMap((group) => group.links) : apiNavigation.flatMap((group) => group.links) + let currentPageIndex = allPages.findIndex( + (page) => page.href === router.pathname + ) + + if (currentPageIndex === -1) { + return null + } + + let previousPage = allPages[currentPageIndex - 1] + let nextPage = allPages[currentPageIndex + 1] + + if (!previousPage && !nextPage) { + return null + } + + return ( +
+ {previousPage && ( +
+ +
+ )} + {nextPage && ( +
+ +
+ )} +
+ ) +} + +function TwitterIcon(props) { + return ( + + ) +} + +function GitHubIcon(props) { + return ( + + ) +} + +function DiscordIcon(props) { + return ( + + ) +} + +function SocialLink({ href, icon: Icon, children }) { + return ( + + {children} + + + ) +} + +function SmallPrint() { + return ( +
+

+ © Copyright {new Date().getFullYear()}. All rights reserved. +

+
+ + Follow us on Twitter + + + Follow us on GitHub + + + Join our Discord server + +
+
+ ) +} + +export function Footer() { + let router = useRouter() + + return ( +
+ + + +
+ ) +} diff --git a/src/components/GridPattern.jsx b/src/components/GridPattern.jsx new file mode 100644 index 00000000..d8337656 --- /dev/null +++ b/src/components/GridPattern.jsx @@ -0,0 +1,42 @@ +import { useId } from 'react' + +export function GridPattern({ width, height, x, y, squares, ...props }) { + let patternId = useId() + + return ( + + ) +} diff --git a/src/components/Guides.jsx b/src/components/Guides.jsx new file mode 100644 index 00000000..39500390 --- /dev/null +++ b/src/components/Guides.jsx @@ -0,0 +1,54 @@ +import { Button } from '@/components/Button' +import { Heading } from '@/components/Heading' + +const guides = [ + { + href: '/authentication', + name: 'Authentication', + description: 'Learn how to authenticate your API requests.', + }, + { + href: '/pagination', + name: 'Pagination', + description: 'Understand how to work with paginated responses.', + }, + { + href: '/errors', + name: 'Errors', + description: + 'Read about the different types of errors returned by the API.', + }, + { + href: '/webhooks', + name: 'Webhooks', + description: + 'Learn how to programmatically configure webhooks for your app.', + }, +] + +export function Guides() { + return ( +
+ + Guides + +
+ {guides.map((guide) => ( +
+

+ {guide.name} +

+

+ {guide.description} +

+

+ +

+
+ ))} +
+
+ ) +} diff --git a/src/components/Header.jsx b/src/components/Header.jsx new file mode 100644 index 00000000..a7ccdc7d --- /dev/null +++ b/src/components/Header.jsx @@ -0,0 +1,90 @@ +import { forwardRef } from 'react' +import Link from 'next/link' +import clsx from 'clsx' +import { motion, useScroll, useTransform } from 'framer-motion' + +import { Button } from '@/components/Button' +import { Logo } from '@/components/Logo' +import { + MobileNavigation, + useIsInsideMobileNavigation, +} from '@/components/MobileNavigation' +import { useMobileNavigationStore } from '@/components/MobileNavigation' +import { ModeToggle } from '@/components/ModeToggle' +import { MobileSearch, Search } from '@/components/Search' + +function TopLevelNavItem({ href, children }) { + return ( +
  • + + {children} + +
  • + ) +} + +export const Header = forwardRef(function Header({ className }, ref) { + let { isOpen: mobileNavIsOpen } = useMobileNavigationStore() + let isInsideMobileNavigation = useIsInsideMobileNavigation() + + let { scrollY } = useScroll() + let bgOpacityLight = useTransform(scrollY, [0, 72], [0.5, 0.9]) + let bgOpacityDark = useTransform(scrollY, [0, 72], [0.2, 0.8]) + + return ( + +
    + +
    + + + + +
    +
    + +
    +
    + + +
    +
    + +
    +
    + + ) +}) diff --git a/src/components/Heading.jsx b/src/components/Heading.jsx new file mode 100644 index 00000000..e3dd28ef --- /dev/null +++ b/src/components/Heading.jsx @@ -0,0 +1,102 @@ +import { useEffect, useRef } from 'react' +import Link from 'next/link' +import { useInView } from 'framer-motion' + +import { useSectionStore } from '@/components/SectionProvider' +import { Tag } from '@/components/Tag' +import { remToPx } from '@/lib/remToPx' + +function AnchorIcon(props) { + return ( + + ) +} + +function Eyebrow({ tag, label }) { + if (!tag && !label) { + return null + } + + return ( +
    + {tag && {tag}} + {tag && label && ( + + )} + {label && ( + {label} + )} +
    + ) +} + +function Anchor({ id, inView, children }) { + return ( + + {inView && ( +
    +
    + +
    +
    + )} + {children} + + ) +} + +export function Heading({ + level = 2, + children, + id, + tag, + label, + anchor = true, + ...props +}) { + let Component = `h${level}` + let ref = useRef() + let registerHeading = useSectionStore((s) => s.registerHeading) + + let inView = useInView(ref, { + margin: `${remToPx(-3.5)}px 0px 0px 0px`, + amount: 'all', + }) + + useEffect(() => { + if (level === 2) { + registerHeading({ id, ref, offsetRem: tag || label ? 8 : 6 }) + } + }) + + return ( + <> + + + {anchor ? ( + + {children} + + ) : ( + children + )} + + + ) +} diff --git a/src/components/HeroPattern.jsx b/src/components/HeroPattern.jsx new file mode 100644 index 00000000..01625893 --- /dev/null +++ b/src/components/HeroPattern.jsx @@ -0,0 +1,32 @@ +import { GridPattern } from '@/components/GridPattern' + +export function HeroPattern() { + return ( +
    +
    +
    + +
    + +
    +
    + ) +} diff --git a/src/components/Layout.jsx b/src/components/Layout.jsx new file mode 100644 index 00000000..46df45ce --- /dev/null +++ b/src/components/Layout.jsx @@ -0,0 +1,38 @@ +import Link from 'next/link' +import { motion } from 'framer-motion' + +import { Footer } from '@/components/Footer' +import { Header } from '@/components/Header' +import { Logo } from '@/components/Logo' +import { Navigation } from '@/components/Navigation' +import { Prose } from '@/components/Prose' +import { SectionProvider } from '@/components/SectionProvider' + +export function Layout({ children, sections = [] }) { + return ( + +
    + +
    +
    + + + +
    +
    + +
    +
    +
    +
    + {children} +
    +
    +
    +
    +
    + ) +} diff --git a/src/components/Libraries.jsx b/src/components/Libraries.jsx new file mode 100644 index 00000000..c3b452c6 --- /dev/null +++ b/src/components/Libraries.jsx @@ -0,0 +1,82 @@ +import Image from 'next/image' + +import { Button } from '@/components/Button' +import { Heading } from '@/components/Heading' +import logoGo from '@/images/logos/go.svg' +import logoNode from '@/images/logos/node.svg' +import logoPhp from '@/images/logos/php.svg' +import logoPython from '@/images/logos/python.svg' +import logoRuby from '@/images/logos/ruby.svg' + +const libraries = [ + { + href: '#', + name: 'PHP', + description: + 'A popular general-purpose scripting language that is especially suited to web development.', + logo: logoPhp, + }, + { + href: '#', + name: 'Ruby', + description: + 'A dynamic, open source programming language with a focus on simplicity and productivity.', + logo: logoRuby, + }, + { + href: '#', + name: 'Node.js', + description: + 'Node.js® is an open-source, cross-platform JavaScript runtime environment.', + logo: logoNode, + }, + { + href: '#', + name: 'Python', + description: + 'Python is a programming language that lets you work quickly and integrate systems more effectively.', + logo: logoPython, + }, + { + href: '#', + name: 'Go', + description: + 'An open-source programming language supported by Google with built-in concurrency.', + logo: logoGo, + }, +] + +export function Libraries() { + return ( +
    + + Official libraries + +
    + {libraries.map((library) => ( +
    +
    +

    + {library.name} +

    +

    + {library.description} +

    +

    + +

    +
    + +
    + ))} +
    +
    + ) +} diff --git a/src/components/Logo.jsx b/src/components/Logo.jsx new file mode 100644 index 00000000..f0389383 --- /dev/null +++ b/src/components/Logo.jsx @@ -0,0 +1,10 @@ +export function Logo(props) { + return ( +
    + some file + some file +
    + ) +} diff --git a/src/components/MobileNavigation.jsx b/src/components/MobileNavigation.jsx new file mode 100644 index 00000000..d37f0f2d --- /dev/null +++ b/src/components/MobileNavigation.jsx @@ -0,0 +1,115 @@ +import { createContext, Fragment, useContext } from 'react' +import { Dialog, Transition } from '@headlessui/react' +import { motion } from 'framer-motion' +import { create } from 'zustand' + +import { Header } from '@/components/Header' +import { Navigation } from '@/components/Navigation' + +function MenuIcon(props) { + return ( + + ) +} + +function XIcon(props) { + return ( + + ) +} + +const IsInsideMobileNavigationContext = createContext(false) + +export function useIsInsideMobileNavigation() { + return useContext(IsInsideMobileNavigationContext) +} + +export const useMobileNavigationStore = create((set) => ({ + isOpen: false, + open: () => set({ isOpen: true }), + close: () => set({ isOpen: false }), + toggle: () => set((state) => ({ isOpen: !state.isOpen })), +})) + +export function MobileNavigation() { + let isInsideMobileNavigation = useIsInsideMobileNavigation() + let { isOpen, toggle, close } = useMobileNavigationStore() + let ToggleIcon = isOpen ? XIcon : MenuIcon + + return ( + + + {!isInsideMobileNavigation && ( + + + +
    + + + + +
    + + + + + + + + +
    +
    + )} +
    + ) +} diff --git a/src/components/ModeToggle.jsx b/src/components/ModeToggle.jsx new file mode 100644 index 00000000..f58c6575 --- /dev/null +++ b/src/components/ModeToggle.jsx @@ -0,0 +1,54 @@ +function SunIcon(props) { + return ( + + ) +} + +function MoonIcon(props) { + return ( + + ) +} + +export function ModeToggle() { + function disableTransitionsTemporarily() { + document.documentElement.classList.add('[&_*]:!transition-none') + window.setTimeout(() => { + document.documentElement.classList.remove('[&_*]:!transition-none') + }, 0) + } + + function toggleMode() { + disableTransitionsTemporarily() + + let darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)') + let isSystemDarkMode = darkModeMediaQuery.matches + let isDarkMode = document.documentElement.classList.toggle('dark') + + if (isDarkMode === isSystemDarkMode) { + delete window.localStorage.isDarkMode + } else { + window.localStorage.isDarkMode = isDarkMode + } + } + + return ( + + ) +} diff --git a/src/components/Navigation.jsx b/src/components/Navigation.jsx new file mode 100644 index 00000000..82414418 --- /dev/null +++ b/src/components/Navigation.jsx @@ -0,0 +1,420 @@ +import {useEffect, useRef, useState} from 'react' +import Link from 'next/link' +import { useRouter } from 'next/router' +import clsx from 'clsx' +import { AnimatePresence, motion, useIsPresent } from 'framer-motion' + +import { Button } from '@/components/Button' +import { useIsInsideMobileNavigation } from '@/components/MobileNavigation' +import { useSectionStore } from '@/components/SectionProvider' +import { Tag } from '@/components/Tag' +import { remToPx } from '@/lib/remToPx' + +function useInitialValue(value, condition = true) { + let initialValue = useRef(value).current + return condition ? initialValue : value +} + +function TopLevelNavItem({ href, children }) { + return ( +
  • + + {children} + +
  • + ) +} + +function NavLink({ href, tag, active, isAnchorLink = false, children }) { + return ( + + {children} + {tag && ( + + {tag} + + )} + + ) +} + +function VisibleSectionHighlight({ group, pathname }) { + let [sections, visibleSections] = useInitialValue( + [ + useSectionStore((s) => s.sections), + useSectionStore((s) => s.visibleSections), + ], + useIsInsideMobileNavigation() + ) + + let isPresent = useIsPresent() + let firstVisibleSectionIndex = Math.max( + 0, + [{ id: '_top' }, ...sections].findIndex( + (section) => section.id === visibleSections[0] + ) + ) + let itemHeight = remToPx(2) + let height = isPresent + ? Math.max(1, visibleSections.length) * itemHeight + : itemHeight + let top = + group.links.findIndex((link) => link.href === pathname) * itemHeight + + firstVisibleSectionIndex * itemHeight + + return ( + + ) +} + +function ActivePageMarker({ group, pathname }) { + let itemHeight = remToPx(2) + let offset = remToPx(0.25) + let activePageIndex = group.links.findIndex((link) => link.href === pathname) + let top = offset + activePageIndex * itemHeight + + return ( + + ) +} + +function NavigationGroup({ group, className }) { + // If this is the mobile navigation then we always render the initial + // state, so that the state does not change during the close animation. + // The state will still update when we re-open (re-render) the navigation. + let isInsideMobileNavigation = useIsInsideMobileNavigation() + let [router, sections] = useInitialValue( + [useRouter(), useSectionStore((s) => s.sections)], + isInsideMobileNavigation + ) + + let isActiveGroup = + group.links.findIndex((link) => link.href === router.pathname) !== -1 + + return ( +
  • + + {group.title} + +
    + + {isActiveGroup && ( + + )} + + + + {isActiveGroup && ( + + )} + +
      + {group.links.map((link) => ( + + + {link.title} + + + {link.href === router.pathname && sections.length > 0 && ( + + {sections.map((section) => ( +
    • + + {section.title} + +
    • + ))} +
      + )} +
      +
      + ))} +
    +
    +
  • + ) +} + +export const docsNavigation = [ + { + title: 'Guides', + links: [ + { title: 'Introduction', href: '/docs/introduction' }, + { title: 'How Netbird Works', href: '/docs/how-netbird-works' }, + { title: 'How-to Guides', href: '/docs/how-to-guides' }, + { title: 'Getting Started', href: '/docs/getting-started' }, + { title: 'Integrations', href: '/docs/integrations' }, + { title: 'Examples', href: '/docs/examples' }, + { title: 'NetBird vs. Traditional VPN', href: '/docs/netbird-vs-traditional-vpn' }, + { title: 'Reference', href: '/docs/reference' }, + { title: 'Why Wireguard with NetBird?', href: '/docs/why-wireguard-with-netbird' }, + { title: 'Other', href: '/docs/other' }, + { title: 'FAQ', href: '/docs/faq' }, + ], + }, + // { + // title: 'Introduction', + // links: [ + // { title: 'Introduction', href: '/docs/introduction' }, + // { title: 'How Netbird Works', href: '/docs/how-netbird-works' }, + // { title: 'How-to Guides', href: '/docs/how-to-guides' }, + // { title: 'Getting Started', href: '/docs/getting-started' }, + // { title: 'Integrations', href: '/docs/integrations' }, + // { title: 'Examples', href: '/docs/examples' }, + // { title: 'NetBird vs. Traditional VPN', href: '/docs/netbird-vs-traditional-vpn' }, + // { title: 'Reference', href: '/docs/reference' }, + // { title: 'Why Wireguard with NetBird?', href: '/docs/why-wireguard-with-netbird' }, + // { title: 'Other', href: '/docs/other' }, + // { title: 'FAQ', href: '/docs/faq' }, + // ], + // }, + // { + // title: 'How Netbird Works', + // links: [ + // { title: 'Introduction', href: '/docs/introduction' }, + // { title: 'How Netbird Works', href: '/docs/how-netbird-works' }, + // { title: 'How-to Guides', href: '/docs/how-to-guides' }, + // { title: 'Getting Started', href: '/docs/getting-started' }, + // { title: 'Integrations', href: '/docs/integrations' }, + // { title: 'Examples', href: '/docs/examples' }, + // { title: 'NetBird vs. Traditional VPN', href: '/docs/netbird-vs-traditional-vpn' }, + // { title: 'Reference', href: '/docs/reference' }, + // { title: 'Why Wireguard with NetBird?', href: '/docs/why-wireguard-with-netbird' }, + // { title: 'Other', href: '/docs/other' }, + // { title: 'FAQ', href: '/docs/faq' }, + // ], + // }, + // { + // title: 'How-to Guides', + // links: [ + // { title: 'Introduction', href: '/docs/introduction' }, + // { title: 'How Netbird Works', href: '/docs/how-netbird-works' }, + // { title: 'How-to Guides', href: '/docs/how-to-guides' }, + // { title: 'Getting Started', href: '/docs/getting-started' }, + // { title: 'Integrations', href: '/docs/integrations' }, + // { title: 'Examples', href: '/docs/examples' }, + // { title: 'NetBird vs. Traditional VPN', href: '/docs/netbird-vs-traditional-vpn' }, + // { title: 'Reference', href: '/docs/reference' }, + // { title: 'Why Wireguard with NetBird?', href: '/docs/why-wireguard-with-netbird' }, + // { title: 'Other', href: '/docs/other' }, + // { title: 'FAQ', href: '/docs/faq' }, + // ], + // }, + // { + // title: 'Getting Started', + // links: [ + // { title: 'Introduction', href: '/docs/introduction' }, + // { title: 'How Netbird Works', href: '/docs/how-netbird-works' }, + // { title: 'How-to Guides', href: '/docs/how-to-guides' }, + // { title: 'Getting Started', href: '/docs/getting-started' }, + // { title: 'Integrations', href: '/docs/integrations' }, + // { title: 'Examples', href: '/docs/examples' }, + // { title: 'NetBird vs. Traditional VPN', href: '/docs/netbird-vs-traditional-vpn' }, + // { title: 'Reference', href: '/docs/reference' }, + // { title: 'Why Wireguard with NetBird?', href: '/docs/why-wireguard-with-netbird' }, + // { title: 'Other', href: '/docs/other' }, + // { title: 'FAQ', href: '/docs/faq' }, + // ], + // }, + // { + // title: 'Integrations', + // links: [ + // { title: 'Introduction', href: '/docs/introduction' }, + // { title: 'How Netbird Works', href: '/docs/how-netbird-works' }, + // { title: 'How-to Guides', href: '/docs/how-to-guides' }, + // { title: 'Getting Started', href: '/docs/getting-started' }, + // { title: 'Integrations', href: '/docs/integrations' }, + // { title: 'Examples', href: '/docs/examples' }, + // { title: 'NetBird vs. Traditional VPN', href: '/docs/netbird-vs-traditional-vpn' }, + // { title: 'Reference', href: '/docs/reference' }, + // { title: 'Why Wireguard with NetBird?', href: '/docs/why-wireguard-with-netbird' }, + // { title: 'Other', href: '/docs/other' }, + // { title: 'FAQ', href: '/docs/faq' }, + // ], + // }, + // { + // title: 'NetBird vs. Traditional VPN', + // links: [ + // { title: 'Introduction', href: '/docs/introduction' }, + // { title: 'How Netbird Works', href: '/docs/how-netbird-works' }, + // { title: 'How-to Guides', href: '/docs/how-to-guides' }, + // { title: 'Getting Started', href: '/docs/getting-started' }, + // { title: 'Integrations', href: '/docs/integrations' }, + // { title: 'Examples', href: '/docs/examples' }, + // { title: 'NetBird vs. Traditional VPN', href: '/docs/netbird-vs-traditional-vpn' }, + // { title: 'Reference', href: '/docs/reference' }, + // { title: 'Why Wireguard with NetBird?', href: '/docs/why-wireguard-with-netbird' }, + // { title: 'Other', href: '/docs/other' }, + // { title: 'FAQ', href: '/docs/faq' }, + // ], + // }, + // { + // title: 'Reference', + // links: [ + // { title: 'Introduction', href: '/docs/introduction' }, + // { title: 'How Netbird Works', href: '/docs/how-netbird-works' }, + // { title: 'How-to Guides', href: '/docs/how-to-guides' }, + // { title: 'Getting Started', href: '/docs/getting-started' }, + // { title: 'Integrations', href: '/docs/integrations' }, + // { title: 'Examples', href: '/docs/examples' }, + // { title: 'NetBird vs. Traditional VPN', href: '/docs/netbird-vs-traditional-vpn' }, + // { title: 'Reference', href: '/docs/reference' }, + // { title: 'Why Wireguard with NetBird?', href: '/docs/why-wireguard-with-netbird' }, + // { title: 'Other', href: '/docs/other' }, + // { title: 'FAQ', href: '/docs/faq' }, + // ], + // }, + // { + // title: 'Why Wireguard with NetBird?', + // links: [ + // { title: 'Introduction', href: '/docs/introduction' }, + // { title: 'How Netbird Works', href: '/docs/how-netbird-works' }, + // { title: 'How-to Guides', href: '/docs/how-to-guides' }, + // { title: 'Getting Started', href: '/docs/getting-started' }, + // { title: 'Integrations', href: '/docs/integrations' }, + // { title: 'Examples', href: '/docs/examples' }, + // { title: 'NetBird vs. Traditional VPN', href: '/docs/netbird-vs-traditional-vpn' }, + // { title: 'Reference', href: '/docs/reference' }, + // { title: 'Why Wireguard with NetBird?', href: '/docs/why-wireguard-with-netbird' }, + // { title: 'Other', href: '/docs/other' }, + // { title: 'FAQ', href: '/docs/faq' }, + // ], + // }, + // { + // title: 'Other', + // links: [ + // { title: 'Introduction', href: '/docs/introduction' }, + // { title: 'How Netbird Works', href: '/docs/how-netbird-works' }, + // { title: 'How-to Guides', href: '/docs/how-to-guides' }, + // { title: 'Getting Started', href: '/docs/getting-started' }, + // { title: 'Integrations', href: '/docs/integrations' }, + // { title: 'Examples', href: '/docs/examples' }, + // { title: 'NetBird vs. Traditional VPN', href: '/docs/netbird-vs-traditional-vpn' }, + // { title: 'Reference', href: '/docs/reference' }, + // { title: 'Why Wireguard with NetBird?', href: '/docs/why-wireguard-with-netbird' }, + // { title: 'Other', href: '/docs/other' }, + // { title: 'FAQ', href: '/docs/faq' }, + // ], + // }, + // { + // title: 'FAQ', + // links: [ + // { title: 'Introduction', href: '/docs/introduction' }, + // { title: 'How Netbird Works', href: '/docs/how-netbird-works' }, + // { title: 'How-to Guides', href: '/docs/how-to-guides' }, + // { title: 'Getting Started', href: '/docs/getting-started' }, + // { title: 'Integrations', href: '/docs/integrations' }, + // { title: 'Examples', href: '/docs/examples' }, + // { title: 'NetBird vs. Traditional VPN', href: '/docs/netbird-vs-traditional-vpn' }, + // { title: 'Reference', href: '/docs/reference' }, + // { title: 'Why Wireguard with NetBird?', href: '/docs/why-wireguard-with-netbird' }, + // { title: 'Other', href: '/docs/other' }, + // { title: 'FAQ', href: '/docs/faq' }, + // ], + // }, +] + +export const apiNavigation = [ + { + title: 'API', + links: [ + { title: 'Accounts', href: '/accounts' }, + { title: 'Users', href: '/users' }, + { title: 'Tokens', href: '/tokens' }, + { title: 'Peers', href: '/peers' }, + { title: 'Setup Keys', href: '/setup-keys' }, + { title: 'Groups', href: '/groups' }, + { title: 'Rules', href: '/rules' }, + { title: 'Policies', href: '/policies' }, + { title: 'Routes', href: '/routes' }, + { title: 'DNS', href: '/dns' }, + { title: 'Events', href: '/events' }, + ], + }, +] + +export function Navigation(props) { + let router = useRouter() + return ( + + ) +} diff --git a/src/components/Prose.jsx b/src/components/Prose.jsx new file mode 100644 index 00000000..9fff2b39 --- /dev/null +++ b/src/components/Prose.jsx @@ -0,0 +1,10 @@ +import clsx from 'clsx' + +export function Prose({ as: Component = 'div', className, ...props }) { + return ( + + ) +} diff --git a/src/components/Resources.jsx b/src/components/Resources.jsx new file mode 100644 index 00000000..fa528f1d --- /dev/null +++ b/src/components/Resources.jsx @@ -0,0 +1,159 @@ +import Link from 'next/link' +import { motion, useMotionTemplate, useMotionValue } from 'framer-motion' + +import { GridPattern } from '@/components/GridPattern' +import { Heading } from '@/components/Heading' +import { ChatBubbleIcon } from '@/components/icons/ChatBubbleIcon' +import { EnvelopeIcon } from '@/components/icons/EnvelopeIcon' +import { UserIcon } from '@/components/icons/UserIcon' +import { UsersIcon } from '@/components/icons/UsersIcon' + +const resources = [ + { + href: '/contacts', + name: 'Contacts', + description: + 'Learn about the contact model and how to create, retrieve, update, delete, and list contacts.', + icon: UserIcon, + pattern: { + y: 16, + squares: [ + [0, 1], + [1, 3], + ], + }, + }, + { + href: '/conversations', + name: 'Conversations', + description: + 'Learn about the conversation model and how to create, retrieve, update, delete, and list conversations.', + icon: ChatBubbleIcon, + pattern: { + y: -6, + squares: [ + [-1, 2], + [1, 3], + ], + }, + }, + { + href: '/messages', + name: 'Messages', + description: + 'Learn about the message model and how to create, retrieve, update, delete, and list messages.', + icon: EnvelopeIcon, + pattern: { + y: 32, + squares: [ + [0, 2], + [1, 4], + ], + }, + }, + { + href: '/groups', + name: 'Groups', + description: + 'Learn about the group model and how to create, retrieve, update, delete, and list groups.', + icon: UsersIcon, + pattern: { + y: 22, + squares: [[0, 1]], + }, + }, +] + +function ResourceIcon({ icon: Icon }) { + return ( +
    + {/*
    */} + + {/**/} +
    + ) +} + +function ResourcePattern({ mouseX, mouseY, ...gridProps }) { + let maskImage = useMotionTemplate`radial-gradient(180px at ${mouseX}px ${mouseY}px, white, transparent)` + let style = { maskImage, WebkitMaskImage: maskImage } + + return ( +
    +
    + +
    + + + + +
    + ) +} + +function Resource({ resource }) { + let mouseX = useMotionValue(0) + let mouseY = useMotionValue(0) + + function onMouseMove({ currentTarget, clientX, clientY }) { + let { left, top } = currentTarget.getBoundingClientRect() + mouseX.set(clientX - left) + mouseY.set(clientY - top) + } + + return ( +
    + +
    +
    + +

    + + + {resource.name} + +

    +

    + {resource.description} +

    +
    +
    + ) +} + +export function Resources() { + return ( +
    + + Resources + +
    + {resources.map((resource) => ( + + ))} +
    +
    + ) +} diff --git a/src/components/Search.jsx b/src/components/Search.jsx new file mode 100644 index 00000000..f787f8cd --- /dev/null +++ b/src/components/Search.jsx @@ -0,0 +1,517 @@ +import { forwardRef, Fragment, useEffect, useId, useRef, useState } from 'react' +import { useRouter } from 'next/router' +import { createAutocomplete } from '@algolia/autocomplete-core' +import { getAlgoliaResults } from '@algolia/autocomplete-preset-algolia' +import { Dialog, Transition } from '@headlessui/react' +import algoliasearch from 'algoliasearch/lite' +import clsx from 'clsx' + +const searchClient = algoliasearch( + process.env.NEXT_PUBLIC_DOCSEARCH_APP_ID, + process.env.NEXT_PUBLIC_DOCSEARCH_API_KEY +) + +function useAutocomplete() { + let id = useId() + let router = useRouter() + let [autocompleteState, setAutocompleteState] = useState({}) + + let [autocomplete] = useState(() => + createAutocomplete({ + id, + placeholder: 'Find something...', + defaultActiveItemId: 0, + onStateChange({ state }) { + setAutocompleteState(state) + }, + shouldPanelOpen({ state }) { + return state.query !== '' + }, + navigator: { + navigate({ itemUrl }) { + autocomplete.setIsOpen(true) + router.push(itemUrl) + }, + }, + getSources() { + return [ + { + sourceId: 'documentation', + getItemInputValue({ item }) { + return item.query + }, + getItemUrl({ item }) { + let url = new URL(item.url) + return `${url.pathname}${url.hash}` + }, + onSelect({ itemUrl }) { + router.push(itemUrl) + }, + getItems({ query }) { + return getAlgoliaResults({ + searchClient, + queries: [ + { + query, + indexName: process.env.NEXT_PUBLIC_DOCSEARCH_INDEX_NAME, + params: { + hitsPerPage: 5, + highlightPreTag: + '', + highlightPostTag: '', + }, + }, + ], + }) + }, + }, + ] + }, + }) + ) + + return { autocomplete, autocompleteState } +} + +function resolveResult(result) { + let allLevels = Object.keys(result.hierarchy) + let hierarchy = Object.entries(result._highlightResult.hierarchy).filter( + ([, { value }]) => Boolean(value) + ) + let levels = hierarchy.map(([level]) => level) + + let level = + result.type === 'content' + ? levels.pop() + : levels + .filter( + (level) => + allLevels.indexOf(level) <= allLevels.indexOf(result.type) + ) + .pop() + + return { + titleHtml: result._highlightResult.hierarchy[level].value, + hierarchyHtml: hierarchy + .slice(0, levels.indexOf(level)) + .map(([, { value }]) => value), + } +} + +function SearchIcon(props) { + return ( + + ) +} + +function NoResultsIcon(props) { + return ( + + ) +} + +function LoadingIcon(props) { + let id = useId() + + return ( + + ) +} + +function SearchResult({ result, resultIndex, autocomplete, collection }) { + let id = useId() + let { titleHtml, hierarchyHtml } = resolveResult(result) + + return ( +
  • 0 && 'border-t border-zinc-100 dark:border-zinc-800' + )} + aria-labelledby={`${id}-hierarchy ${id}-title`} + {...autocomplete.getItemProps({ + item: result, + source: collection.source, + })} + > +
  • + ) +} + +function SearchResults({ autocomplete, query, collection }) { + if (collection.items.length === 0) { + return ( +
    + +

    + Nothing found for{' '} + + ‘{query}’ + + . Please try again. +

    +
    + ) + } + + return ( +
      + {collection.items.map((result, resultIndex) => ( + + ))} +
    + ) +} + +const SearchInput = forwardRef(function SearchInput( + { autocomplete, autocompleteState, onClose }, + inputRef +) { + let inputProps = autocomplete.getInputProps({}) + + return ( +
    + + { + if ( + event.key === 'Escape' && + !autocompleteState.isOpen && + autocompleteState.query === '' + ) { + // In Safari, closing the dialog with the escape key can sometimes cause the scroll position to jump to the + // bottom of the page. This is a workaround for that until we can figure out a proper fix in Headless UI. + document.activeElement?.blur() + + onClose() + } else { + inputProps.onKeyDown(event) + } + }} + /> + {autocompleteState.status === 'stalled' && ( +
    + + {/**/} +
    + )} +
    + ) +}) + +function AlgoliaLogo(props) { + return ( + + + + + + + ) +} + +function SearchButton(props) { + let [modifierKey, setModifierKey] = useState() + + useEffect(() => { + setModifierKey( + /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform) ? '⌘' : 'Ctrl ' + ) + }, []) + + return ( + <> + + + + ) +} + +function SearchDialog({ open, setOpen, className }) { + let router = useRouter() + let formRef = useRef() + let panelRef = useRef() + let inputRef = useRef() + let { autocomplete, autocompleteState } = useAutocomplete() + + useEffect(() => { + if (!open) { + return + } + + function onRouteChange() { + setOpen(false) + } + + router.events.on('routeChangeStart', onRouteChange) + router.events.on('hashChangeStart', onRouteChange) + + return () => { + router.events.off('routeChangeStart', onRouteChange) + router.events.off('hashChangeStart', onRouteChange) + } + }, [open, setOpen, router]) + + useEffect(() => { + if (open) { + return + } + + function onKeyDown(event) { + if (event.key === 'k' && (event.metaKey || event.ctrlKey)) { + event.preventDefault() + setOpen(true) + } + } + + window.addEventListener('keydown', onKeyDown) + + return () => { + window.removeEventListener('keydown', onKeyDown) + } + }, [open, setOpen]) + + return ( + autocomplete.setQuery('')} + > + + +
    + + +
    + + +
    +
    + setOpen(false)} + /> +
    + {autocompleteState.isOpen && ( + <> + +

    + Search by{' '} + +

    + + )} +
    + +
    +
    +
    +
    +
    +
    + ) +} + +function useSearchProps() { + let buttonRef = useRef() + let [open, setOpen] = useState(false) + + return { + buttonProps: { + ref: buttonRef, + onClick() { + setOpen(true) + }, + }, + dialogProps: { + open, + setOpen(open) { + let { width, height } = buttonRef.current.getBoundingClientRect() + if (!open || (width !== 0 && height !== 0)) { + setOpen(open) + } + }, + }, + } +} + +export function Search() { + let [modifierKey, setModifierKey] = useState() + let { buttonProps, dialogProps } = useSearchProps() + + useEffect(() => { + setModifierKey( + /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform) ? '⌘' : 'Ctrl ' + ) + }, []) + + return ( +
    + + +
    + ) +} + +export function MobileSearch() { + let { buttonProps, dialogProps } = useSearchProps() + + return ( +
    + + +
    + ) +} diff --git a/src/components/SectionProvider.jsx b/src/components/SectionProvider.jsx new file mode 100644 index 00000000..95f1496c --- /dev/null +++ b/src/components/SectionProvider.jsx @@ -0,0 +1,117 @@ +import { + createContext, + useContext, + useEffect, + useLayoutEffect, + useState, +} from 'react' +import { createStore, useStore } from 'zustand' + +import { remToPx } from '@/lib/remToPx' + +function createSectionStore(sections) { + return createStore((set) => ({ + sections, + visibleSections: [], + setVisibleSections: (visibleSections) => + set((state) => + state.visibleSections.join() === visibleSections.join() + ? {} + : { visibleSections } + ), + registerHeading: ({ id, ref, offsetRem }) => + set((state) => { + return { + sections: state.sections.map((section) => { + if (section.id === id) { + return { + ...section, + headingRef: ref, + offsetRem, + } + } + return section + }), + } + }), + })) +} + +function useVisibleSections(sectionStore) { + let setVisibleSections = useStore(sectionStore, (s) => s.setVisibleSections) + let sections = useStore(sectionStore, (s) => s.sections) + + useEffect(() => { + function checkVisibleSections() { + let { innerHeight, scrollY } = window + let newVisibleSections = [] + + for ( + let sectionIndex = 0; + sectionIndex < sections.length; + sectionIndex++ + ) { + let { id, headingRef, offsetRem } = sections[sectionIndex] + let offset = remToPx(offsetRem) + let top = headingRef.current.getBoundingClientRect().top + scrollY + + if (sectionIndex === 0 && top - offset > scrollY) { + newVisibleSections.push('_top') + } + + let nextSection = sections[sectionIndex + 1] + let bottom = + (nextSection?.headingRef.current.getBoundingClientRect().top ?? + Infinity) + + scrollY - + remToPx(nextSection?.offsetRem ?? 0) + + if ( + (top > scrollY && top < scrollY + innerHeight) || + (bottom > scrollY && bottom < scrollY + innerHeight) || + (top <= scrollY && bottom >= scrollY + innerHeight) + ) { + newVisibleSections.push(id) + } + } + + setVisibleSections(newVisibleSections) + } + + let raf = window.requestAnimationFrame(() => checkVisibleSections()) + window.addEventListener('scroll', checkVisibleSections, { passive: true }) + window.addEventListener('resize', checkVisibleSections) + + return () => { + window.cancelAnimationFrame(raf) + window.removeEventListener('scroll', checkVisibleSections) + window.removeEventListener('resize', checkVisibleSections) + } + }, [setVisibleSections, sections]) +} + +const SectionStoreContext = createContext() + +const useIsomorphicLayoutEffect = + typeof window === 'undefined' ? useEffect : useLayoutEffect + +export function SectionProvider({ sections, children }) { + let [sectionStore] = useState(() => createSectionStore(sections)) + + useVisibleSections(sectionStore) + + useIsomorphicLayoutEffect(() => { + sectionStore.setState({ sections }) + }, [sectionStore, sections]) + + return ( + + {children} + + ) +} + +export function useSectionStore(selector) { + let store = useContext(SectionStoreContext) + return useStore(store, selector) +} diff --git a/src/components/Tag.jsx b/src/components/Tag.jsx new file mode 100644 index 00000000..902cb8f5 --- /dev/null +++ b/src/components/Tag.jsx @@ -0,0 +1,58 @@ +import clsx from 'clsx' + +const variantStyles = { + medium: 'rounded-lg px-1.5 ring-1 ring-inset', +} + +const colorStyles = { + emerald: { + small: 'text-emerald-500 dark:text-emerald-400', + medium: + 'ring-emerald-300 dark:ring-emerald-400/30 bg-emerald-400/10 text-emerald-500 dark:text-emerald-400', + }, + sky: { + small: 'text-sky-500', + medium: + 'ring-sky-300 bg-sky-400/10 text-sky-500 dark:ring-sky-400/30 dark:bg-sky-400/10 dark:text-sky-400', + }, + amber: { + small: 'text-amber-500', + medium: + 'ring-amber-300 bg-amber-400/10 text-amber-500 dark:ring-amber-400/30 dark:bg-amber-400/10 dark:text-amber-400', + }, + rose: { + small: 'text-red-500 dark:text-rose-500', + medium: + 'ring-rose-200 bg-rose-50 text-red-500 dark:ring-rose-500/20 dark:bg-rose-400/10 dark:text-rose-400', + }, + zinc: { + small: 'text-zinc-400 dark:text-zinc-500', + medium: + 'ring-zinc-200 bg-zinc-50 text-zinc-500 dark:ring-zinc-500/20 dark:bg-zinc-400/10 dark:text-zinc-400', + }, +} + +const valueColorMap = { + get: 'emerald', + post: 'sky', + put: 'amber', + delete: 'rose', +} + +export function Tag({ + children, + variant = 'medium', + color = valueColorMap[children.toLowerCase()] ?? 'emerald', +}) { + return ( + + {children} + + ) +} diff --git a/src/components/icons/BellIcon.jsx b/src/components/icons/BellIcon.jsx new file mode 100644 index 00000000..09062dd9 --- /dev/null +++ b/src/components/icons/BellIcon.jsx @@ -0,0 +1,17 @@ +export function BellIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/BoltIcon.jsx b/src/components/icons/BoltIcon.jsx new file mode 100644 index 00000000..1278a7fe --- /dev/null +++ b/src/components/icons/BoltIcon.jsx @@ -0,0 +1,11 @@ +export function BoltIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/BookIcon.jsx b/src/components/icons/BookIcon.jsx new file mode 100644 index 00000000..d560f41c --- /dev/null +++ b/src/components/icons/BookIcon.jsx @@ -0,0 +1,17 @@ +export function BookIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/CalendarIcon.jsx b/src/components/icons/CalendarIcon.jsx new file mode 100644 index 00000000..8d0a57c8 --- /dev/null +++ b/src/components/icons/CalendarIcon.jsx @@ -0,0 +1,23 @@ +export function CalendarIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/CartIcon.jsx b/src/components/icons/CartIcon.jsx new file mode 100644 index 00000000..e4246246 --- /dev/null +++ b/src/components/icons/CartIcon.jsx @@ -0,0 +1,15 @@ +export function CartIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/ChatBubbleIcon.jsx b/src/components/icons/ChatBubbleIcon.jsx new file mode 100644 index 00000000..b929ec64 --- /dev/null +++ b/src/components/icons/ChatBubbleIcon.jsx @@ -0,0 +1,17 @@ +export function ChatBubbleIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/CheckIcon.jsx b/src/components/icons/CheckIcon.jsx new file mode 100644 index 00000000..33d2b249 --- /dev/null +++ b/src/components/icons/CheckIcon.jsx @@ -0,0 +1,17 @@ +export function CheckIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/ChevronRightLeftIcon.jsx b/src/components/icons/ChevronRightLeftIcon.jsx new file mode 100644 index 00000000..2dbaa18c --- /dev/null +++ b/src/components/icons/ChevronRightLeftIcon.jsx @@ -0,0 +1,17 @@ +export function ChevronRightLeftIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/ClipboardIcon.jsx b/src/components/icons/ClipboardIcon.jsx new file mode 100644 index 00000000..9c8c55ce --- /dev/null +++ b/src/components/icons/ClipboardIcon.jsx @@ -0,0 +1,17 @@ +export function ClipboardIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/CogIcon.jsx b/src/components/icons/CogIcon.jsx new file mode 100644 index 00000000..57023fc7 --- /dev/null +++ b/src/components/icons/CogIcon.jsx @@ -0,0 +1,19 @@ +export function CogIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/CopyIcon.jsx b/src/components/icons/CopyIcon.jsx new file mode 100644 index 00000000..aadede17 --- /dev/null +++ b/src/components/icons/CopyIcon.jsx @@ -0,0 +1,17 @@ +export function CopyIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/DocumentIcon.jsx b/src/components/icons/DocumentIcon.jsx new file mode 100644 index 00000000..b576db04 --- /dev/null +++ b/src/components/icons/DocumentIcon.jsx @@ -0,0 +1,17 @@ +export function DocumentIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/EnvelopeIcon.jsx b/src/components/icons/EnvelopeIcon.jsx new file mode 100644 index 00000000..17ab4fa9 --- /dev/null +++ b/src/components/icons/EnvelopeIcon.jsx @@ -0,0 +1,17 @@ +export function EnvelopeIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/FaceSmileIcon.jsx b/src/components/icons/FaceSmileIcon.jsx new file mode 100644 index 00000000..4c01755a --- /dev/null +++ b/src/components/icons/FaceSmileIcon.jsx @@ -0,0 +1,17 @@ +export function FaceSmileIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/FolderIcon.jsx b/src/components/icons/FolderIcon.jsx new file mode 100644 index 00000000..c59b6e0a --- /dev/null +++ b/src/components/icons/FolderIcon.jsx @@ -0,0 +1,22 @@ +export function FolderIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/LinkIcon.jsx b/src/components/icons/LinkIcon.jsx new file mode 100644 index 00000000..2dace231 --- /dev/null +++ b/src/components/icons/LinkIcon.jsx @@ -0,0 +1,12 @@ +export function LinkIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/ListIcon.jsx b/src/components/icons/ListIcon.jsx new file mode 100644 index 00000000..ea45c0a6 --- /dev/null +++ b/src/components/icons/ListIcon.jsx @@ -0,0 +1,17 @@ +export function ListIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/MagnifyingGlassIcon.jsx b/src/components/icons/MagnifyingGlassIcon.jsx new file mode 100644 index 00000000..309359c2 --- /dev/null +++ b/src/components/icons/MagnifyingGlassIcon.jsx @@ -0,0 +1,13 @@ +export function MagnifyingGlassIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/MapPinIcon.jsx b/src/components/icons/MapPinIcon.jsx new file mode 100644 index 00000000..b581a6d6 --- /dev/null +++ b/src/components/icons/MapPinIcon.jsx @@ -0,0 +1,19 @@ +export function MapPinIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/PackageIcon.jsx b/src/components/icons/PackageIcon.jsx new file mode 100644 index 00000000..cb0bc98d --- /dev/null +++ b/src/components/icons/PackageIcon.jsx @@ -0,0 +1,16 @@ +export function PackageIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/PaperAirplaneIcon.jsx b/src/components/icons/PaperAirplaneIcon.jsx new file mode 100644 index 00000000..6f96ea84 --- /dev/null +++ b/src/components/icons/PaperAirplaneIcon.jsx @@ -0,0 +1,17 @@ +export function PaperAirplaneIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/PaperClipIcon.jsx b/src/components/icons/PaperClipIcon.jsx new file mode 100644 index 00000000..cdd72437 --- /dev/null +++ b/src/components/icons/PaperClipIcon.jsx @@ -0,0 +1,12 @@ +export function PaperClipIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/ShapesIcon.jsx b/src/components/icons/ShapesIcon.jsx new file mode 100644 index 00000000..5dd29fd0 --- /dev/null +++ b/src/components/icons/ShapesIcon.jsx @@ -0,0 +1,17 @@ +export function ShapesIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/ShirtIcon.jsx b/src/components/icons/ShirtIcon.jsx new file mode 100644 index 00000000..30c67b92 --- /dev/null +++ b/src/components/icons/ShirtIcon.jsx @@ -0,0 +1,11 @@ +export function ShirtIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/SquaresPlusIcon.jsx b/src/components/icons/SquaresPlusIcon.jsx new file mode 100644 index 00000000..39a507a4 --- /dev/null +++ b/src/components/icons/SquaresPlusIcon.jsx @@ -0,0 +1,17 @@ +export function SquaresPlusIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/TagIcon.jsx b/src/components/icons/TagIcon.jsx new file mode 100644 index 00000000..c7e57b03 --- /dev/null +++ b/src/components/icons/TagIcon.jsx @@ -0,0 +1,19 @@ +export function TagIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/UserIcon.jsx b/src/components/icons/UserIcon.jsx new file mode 100644 index 00000000..350e3c42 --- /dev/null +++ b/src/components/icons/UserIcon.jsx @@ -0,0 +1,24 @@ +export function UserIcon(props) { + return ( + + ) +} diff --git a/src/components/icons/UsersIcon.jsx b/src/components/icons/UsersIcon.jsx new file mode 100644 index 00000000..68ef1312 --- /dev/null +++ b/src/components/icons/UsersIcon.jsx @@ -0,0 +1,28 @@ +export function UsersIcon(props) { + return ( + + ) +} diff --git a/src/components/mdx.jsx b/src/components/mdx.jsx new file mode 100644 index 00000000..f33dfe51 --- /dev/null +++ b/src/components/mdx.jsx @@ -0,0 +1,112 @@ +import Link from 'next/link' +import clsx from 'clsx' + +import { Heading } from '@/components/Heading' + +export const a = Link +export { Button } from '@/components/Button' +export { CodeGroup, Code as code, Pre as pre } from '@/components/Code' + +export const h2 = function H2(props) { + return +} + +function InfoIcon(props) { + return ( + + ) +} + +export function Note({ children }) { + return ( +
    + +
    + {children} +
    +
    + ) +} + +export function Row({ children }) { + return ( +
    + {children} +
    + ) +} + +export function Col({ children, sticky = false }) { + return ( +
    :first-child]:mt-0 [&>:last-child]:mb-0', + sticky && 'xl:sticky xl:top-24' + )} + > + {children} +
    + ) +} + +export function Properties({ children }) { + return ( +
    +
      + {children} +
    +
    + ) +} + +export function Property({ name, type, required, min, max, minLen, maxLen, enumList, children }) { + return ( +
  • +
    +
    Name
    +
    + {name} +
    +
    Type
    +
    + {type} +
    +
    Required
    +
    + {required && 'required'} +
    +
    + {!required && 'optional'} +
    +
    Enum
    +
    + {/*{enumList && "Possible values: [" + enumList.split(',').forEach((type) => ({type})) + "]"}*/} + {/* {enumList &&
    Possible Values: [
    }enumList.split(',').map(type => ''+ type + '')}*/} + {min && !max &&
    Possible Values: >={min}
    } + {max && !min &&
    Possible Values: <={max}
    } + {min && max &&
    Possible Values: >={min} and <={max}
    } + {minLen && !maxLen &&
    Possible Values: >={minLen} {type === "string" ? "characters" : "objects"}
    } + {maxLen && !minLen &&
    Possible Values: <={maxLen} {type === "string" ? "characters" : "objects"}
    } + {minLen && maxLen &&
    Possible Values: >={minLen} {type === "string" ? "characters" : "objects"} and <={maxLen} {type === "string" ? "characters" : "objects"}
    } +
    +
    Description
    +
    + {children} +
    +
    +
  • + ) +} diff --git a/src/images/logos/go.svg b/src/images/logos/go.svg new file mode 100644 index 00000000..7f7b19de --- /dev/null +++ b/src/images/logos/go.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/src/images/logos/node.svg b/src/images/logos/node.svg new file mode 100644 index 00000000..1d09de22 --- /dev/null +++ b/src/images/logos/node.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/images/logos/php.svg b/src/images/logos/php.svg new file mode 100644 index 00000000..0a9ac462 --- /dev/null +++ b/src/images/logos/php.svg @@ -0,0 +1,10 @@ + + + + + diff --git a/src/images/logos/python.svg b/src/images/logos/python.svg new file mode 100644 index 00000000..9bceb587 --- /dev/null +++ b/src/images/logos/python.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/src/images/logos/ruby.svg b/src/images/logos/ruby.svg new file mode 100644 index 00000000..b22a5bf1 --- /dev/null +++ b/src/images/logos/ruby.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/images/static/.nojekyll b/src/images/static/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/src/lib/remToPx.js b/src/lib/remToPx.js new file mode 100644 index 00000000..9ae9628d --- /dev/null +++ b/src/lib/remToPx.js @@ -0,0 +1,8 @@ +export function remToPx(remValue) { + let rootFontSize = + typeof window === 'undefined' + ? 16 + : parseFloat(window.getComputedStyle(document.documentElement).fontSize) + + return parseFloat(remValue) * rootFontSize +} diff --git a/src/pages/_app.jsx b/src/pages/_app.jsx new file mode 100644 index 00000000..1b515c95 --- /dev/null +++ b/src/pages/_app.jsx @@ -0,0 +1,40 @@ +import Head from 'next/head' +import { Router, useRouter } from 'next/router' +import { MDXProvider } from '@mdx-js/react' + +import { Layout } from '@/components/Layout' +import * as mdxComponents from '@/components/mdx' +import { useMobileNavigationStore } from '@/components/MobileNavigation' + +import '@/styles/tailwind.css' +import 'focus-visible' + +function onRouteChange() { + useMobileNavigationStore.getState().close() +} + +Router.events.on('routeChangeStart', onRouteChange) +Router.events.on('hashChangeStart', onRouteChange) + +export default function App({ Component, pageProps }) { + let router = useRouter() + + return ( + <> + + {router.pathname === '/' ? ( + NetBird Docs + ) : ( + router.pathname.startsWith('/docs') ? + {`${pageProps.title} - NetBird Docs`} : {`${pageProps.title} - NetBird API`} + )} + + + + + + + + + ) +} diff --git a/src/pages/_document.jsx b/src/pages/_document.jsx new file mode 100644 index 00000000..db198263 --- /dev/null +++ b/src/pages/_document.jsx @@ -0,0 +1,50 @@ +import { Head, Html, Main, NextScript } from 'next/document' + +const modeScript = ` + let darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)') + + updateMode() + darkModeMediaQuery.addEventListener('change', updateModeWithoutTransitions) + window.addEventListener('storage', updateModeWithoutTransitions) + + function updateMode() { + let isSystemDarkMode = darkModeMediaQuery.matches + let isDarkMode = window.localStorage.isDarkMode === 'true' || (!('isDarkMode' in window.localStorage) && isSystemDarkMode) + + if (isDarkMode) { + document.documentElement.classList.add('dark') + } else { + document.documentElement.classList.remove('dark') + } + + if (isDarkMode === isSystemDarkMode) { + delete window.localStorage.isDarkMode + } + } + + function disableTransitionsTemporarily() { + document.documentElement.classList.add('[&_*]:!transition-none') + window.setTimeout(() => { + document.documentElement.classList.remove('[&_*]:!transition-none') + }, 0) + } + + function updateModeWithoutTransitions() { + disableTransitionsTemporarily() + updateMode() + } +` + +export default function Document() { + return ( + + +