commit a2a2bcb2069388c40c88c1b6716a94d58ba39a56 Author: Malasaur Date: Sun Mar 15 21:16:04 2026 +0100 Genesis commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2ea215f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.env +venv +__pycache__ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f304bc2 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,9 @@ +aiohappyeyeballs==2.6.1 +aiohttp==3.13.3 +aiosignal==1.4.0 +attrs==25.4.0 +frozenlist==1.8.0 +idna==3.11 +multidict==6.7.1 +propcache==0.4.1 +yarl==1.23.0 diff --git a/src/api.json b/src/api.json new file mode 100644 index 0000000..39c581c --- /dev/null +++ b/src/api.json @@ -0,0 +1,3761 @@ +{ + "schemes": [], + "swagger": "2.0", + "info": { + "description": "PufferPanel API interface for both the panel and daemon. Endpoints starting with /daemon or /proxy are for nodes.", + "title": "PufferPanel API", + "contact": { + "name": "PufferPanel", + "url": "https://pufferpanel.com" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "2.0" + }, + "host": "", + "basePath": "", + "paths": { + "/api/nodes": { + "get": { + "description": "Gets all nodes registered to the panel", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Get nodes", + "responses": { + "200": { + "description": "Nodes", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.NodeView" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + }, + "post": { + "description": "Creates a node", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Create node", + "responses": { + "200": { + "description": "Node created", + "schema": { + "$ref": "#/definitions/models.NodeView" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/api/nodes/{id}": { + "get": { + "description": "Gets information about a single node", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Get node", + "parameters": [ + { + "type": "string", + "description": "Node Id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Nodes", + "schema": { + "$ref": "#/definitions/models.NodeView" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + }, + "put": { + "description": "Updates a node with given information", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Update node", + "parameters": [ + { + "type": "string", + "description": "Node Id", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Node information", + "name": "node", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.NodeView" + } + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + }, + "delete": { + "description": "Deletes the node", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Deletes a node", + "parameters": [ + { + "type": "string", + "description": "Node Id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/api/nodes/{id}/deployment": { + "get": { + "description": "Gets the secret information needed to deploy a node.", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Gets the data to deploy a node", + "parameters": [ + { + "type": "string", + "description": "Node Id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.Deployment" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/api/self": { + "get": { + "description": "Gets the user information of the current user", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Get your user info", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.UserView" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + }, + "put": { + "description": "Updates the value of a panel setting", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Update a panel setting", + "parameters": [ + { + "type": "string", + "description": "The config key", + "name": "key", + "in": "path", + "required": true + }, + { + "description": "The new value for the setting", + "name": "value", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.ChangeSetting" + } + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/api/self/oauth2": { + "get": { + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Gets registered oauth2 clients under this user", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Client" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + }, + "post": { + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Create an account-level OAuth2 client", + "parameters": [ + { + "description": "Information for the client to create", + "name": "client", + "in": "body", + "schema": { + "$ref": "#/definitions/models.Client" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.CreatedClient" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/api/self/oauth2/{id}": { + "delete": { + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Deletes an account-level OAuth2 client", + "parameters": [ + { + "type": "string", + "description": "Information for the client to create", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/api/servers": { + "get": { + "description": "Gets servers, and allowing for filtering of servers. * is a wildcard that can be used for text inputs", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Get servers", + "parameters": [ + { + "type": "string", + "description": "Username to filter on, default is current user if NOT admin", + "name": "username", + "in": "query" + }, + { + "type": "integer", + "description": "Node ID to filter on", + "name": "node", + "in": "query" + }, + { + "type": "string", + "description": "Name of server to filter on", + "name": "name", + "in": "query" + }, + { + "type": "integer", + "description": "Max number of results to return", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "What page to get back for many results", + "name": "page", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.ServerSearchResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + }, + "post": { + "description": "Creates a server", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Makes a server", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "id", + "in": "path" + }, + { + "description": "Creation information", + "name": "server", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.ServerCreation" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.CreateServerResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/api/servers/{id}": { + "get": { + "description": "Gets a particular server", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Get a server", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.GetServerResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + }, + "put": { + "description": "Creates a server", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Makes a server", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "id", + "in": "path" + }, + { + "description": "Creation information", + "name": "server", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.ServerCreation" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.CreateServerResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + }, + "delete": { + "description": "Deletes a server from the panel", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Deletes a server", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/api/servers/{id}/name": { + "post": { + "description": "Renames a server", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Rename server", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "id", + "in": "path" + }, + { + "type": "string", + "description": "Server Name", + "name": "name", + "in": "path" + } + ], + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/api/servers/{id}/oauth2": { + "get": { + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Gets server-level OAuth2 credentials for the logged in user", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Client" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + }, + "post": { + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Creates server-level OAuth2 credentials for the logged in user", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Client to create", + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/models.Client" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.CreatedClient" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/api/servers/{id}/oauth2/{clientId}": { + "delete": { + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Deletes server-level OAuth2 credential", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Client ID", + "name": "clientId", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/api/servers/{id}/user": { + "get": { + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Gets all users for a server", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.PermissionView" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/api/servers/{id}/users/{email}": { + "put": { + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Edits access to a server", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Email of user", + "name": "email", + "in": "path", + "required": true + }, + { + "description": "New permissions to apply", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.PermissionView" + } + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + }, + "delete": { + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Removes access to a server", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Email of user", + "name": "email", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/api/settings/{key}": { + "get": { + "description": "Gets the value currently being used for the specified config key", + "produces": ["application/json"], + "summary": "Value a panel setting", + "parameters": [ + { + "type": "string", + "description": "The config key", + "name": "key", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.SettingResponse" + } + } + } + } + }, + "/api/templates": { + "get": { + "description": "Gets a template if registered", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Get single template", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.Template" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/api/templates/import": { + "post": { + "description": "Gets all templates which can be imported from https://github.com/PufferPanel/templates", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Gets importable templates", + "parameters": [ + { + "description": "Template", + "name": "template", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/pufferpanel.Server" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/api/templates/import/{name}": { + "post": { + "description": "Imports the given template from our main repo", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Import template from repo", + "parameters": [ + { + "type": "string", + "description": "Template", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/api/templates/{name}": { + "put": { + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Adds or updates a template", + "parameters": [ + { + "description": "Template", + "name": "template", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/pufferpanel.Server" + } + }, + { + "type": "string", + "description": "Template name", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + }, + "delete": { + "description": "Deletes template", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Deletes template", + "parameters": [ + { + "type": "string", + "description": "Template", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/api/userSettings": { + "get": { + "description": "Gets all settings specific to the current user", + "produces": ["application/json"], + "summary": "Get a user setting", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.UserSettingView" + } + } + } + } + } + }, + "/api/userSettings/{key}": { + "put": { + "description": "Updates the value of a user setting", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Update a user setting", + "parameters": [ + { + "type": "string", + "description": "The config key", + "name": "key", + "in": "path", + "required": true + }, + { + "description": "The new value for the setting", + "name": "value", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.ChangeUserSetting" + } + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/api/users": { + "get": { + "description": "Gets users, and allowing for filtering of users. * is a wildcard that can be used for text inputs", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Get users", + "parameters": [ + { + "description": "Filters to search on", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.UserSearch" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.UserSearchResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + }, + "post": { + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Create user", + "parameters": [ + { + "description": "New user information", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.UserView" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.UserView" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/api/users/{id}": { + "get": { + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Get a user", + "parameters": [ + { + "type": "integer", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.UserView" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + }, + "post": { + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Update user", + "parameters": [ + { + "type": "integer", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "New user information", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.UserView" + } + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + }, + "delete": { + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Delete user", + "parameters": [ + { + "type": "integer", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/api/users/{id}/perms": { + "get": { + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Gets user permissions", + "parameters": [ + { + "type": "integer", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.PermissionView" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + }, + "put": { + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Sets user permissions", + "parameters": [ + { + "type": "integer", + "description": "User ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "New permissions", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.PermissionView" + } + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/daemon": { + "get": { + "description": "Easy way to tell if the daemon is running is by using this endpoint", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Is daemon up", + "responses": { + "200": { + "description": "Service running", + "schema": { + "$ref": "#/definitions/pufferpanel.DaemonRunning" + } + } + } + }, + "head": { + "description": "Gets a list of features supported by the node", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Get supported features", + "responses": { + "200": { + "description": "Features", + "schema": { + "$ref": "#/definitions/daemon.Features" + } + } + } + } + }, + "/daemon/server/{id}": { + "get": { + "description": "Gets the given server data from an admin's view", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Gets server data as admin", + "parameters": [ + { + "type": "string", + "description": "Server Identifier", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Data for this server", + "schema": { + "$ref": "#/definitions/pufferpanel.ServerDataAdmin" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + }, + "put": { + "description": "Creates the server", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Create server", + "parameters": [ + { + "type": "string", + "description": "Server Identifier", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Server to create", + "name": "server", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/pufferpanel.Server" + } + } + ], + "responses": { + "200": { + "description": "Server created", + "schema": { + "$ref": "#/definitions/pufferpanel.ServerIdResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + }, + "post": { + "description": "Updates a server", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Updates a server", + "parameters": [ + { + "type": "string", + "description": "Server Identifier", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + }, + "delete": { + "description": "Deletes the given server", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Deletes server", + "parameters": [ + { + "type": "string", + "description": "Server Identifier", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "Server deleted", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/daemon/server/{id}/archive/{filename}": { + "post": { + "description": "Archives file(s) with the", + "consumes": ["application/json"], + "summary": "Archive file(s)", + "parameters": [ + { + "type": "string", + "description": "Server Identifier", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Destination", + "name": "filename", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "If file(s) was archived", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/daemon/server/{id}/console": { + "get": { + "description": "Gets the given server logs since a certain time period", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Gets server logs", + "parameters": [ + { + "type": "string", + "description": "Server Identifier", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "integer", + "default": 0, + "description": "Only get data from after this UNIX timestamp", + "name": "time", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Logs for this server", + "schema": { + "$ref": "#/definitions/pufferpanel.ServerLogs" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + }, + "post": { + "description": "Runs a command in the server", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Run command", + "parameters": [ + { + "type": "string", + "description": "Server Identifier", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Command to run", + "name": "commands", + "in": "body", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "If command was ran", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/daemon/server/{id}/data": { + "get": { + "description": "Gets the given server data", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Gets server data", + "parameters": [ + { + "type": "string", + "description": "Server Identifier", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Data for this server", + "schema": { + "$ref": "#/definitions/pufferpanel.ServerData" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + }, + "post": { + "description": "Edits the given server data", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Edit server data", + "parameters": [ + { + "type": "string", + "description": "Server Identifier", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Server data", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/pufferpanel.ServerData" + } + } + ], + "responses": { + "204": { + "description": "Server edited", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/daemon/server/{id}/extract/{filename}": { + "get": { + "description": "Extracts files from an archive", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Extract files", + "parameters": [ + { + "type": "string", + "description": "Server Identifier", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "File name", + "name": "filename", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Destination directory (URI Parameter)", + "name": "destination", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "If file was extracted", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/daemon/server/{id}/file/{filename}": { + "get": { + "description": "Gets a file or a file list from the server", + "consumes": ["application/json"], + "produces": ["application/json", "application/octet-stream"], + "summary": "Get file/list", + "parameters": [ + { + "type": "string", + "description": "Server Identifier", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "File name", + "name": "filename", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "File List", + "schema": { + "$ref": "#/definitions/messages.FileDesc" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + }, + "put": { + "description": "Puts a file or folder on the server", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Put file/folder", + "parameters": [ + { + "type": "string", + "description": "Server Identifier", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "File name", + "name": "filename", + "in": "path", + "required": true + }, + { + "type": "boolean", + "description": "If this is a folder", + "name": "folder", + "in": "path", + "required": true + }, + { + "type": "file", + "description": "File to place", + "name": "file", + "in": "formData" + } + ], + "responses": { + "204": { + "description": "If file/folder was created", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + }, + "delete": { + "description": "Deletes a file from the server", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Delete file", + "parameters": [ + { + "type": "string", + "description": "Server Identifier", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "File name", + "name": "filename", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "If file was deleted", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/daemon/server/{id}/install": { + "post": { + "description": "installs the given server", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Installs server", + "parameters": [ + { + "type": "string", + "description": "Server Identifier", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "boolean", + "description": "Wait for the operation to complete", + "name": "wait", + "in": "query" + } + ], + "responses": { + "202": { + "description": "Install has been queued", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/daemon/server/{id}/kill": { + "post": { + "description": "Stops the given server forcefully", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Kill server", + "parameters": [ + { + "type": "string", + "description": "Server Identifier", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "Server killed", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/daemon/server/{id}/reload": { + "post": { + "description": "Reloads the server from disk", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Reload server", + "parameters": [ + { + "type": "string", + "description": "Server Identifier", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "Reloaded server", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/daemon/server/{id}/start": { + "post": { + "description": "Starts the given server", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Starts server", + "parameters": [ + { + "type": "string", + "description": "Server Identifier", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "boolean", + "description": "Wait for the operation to complete", + "name": "wait", + "in": "query" + } + ], + "responses": { + "202": { + "description": "Start has been queued", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "204": { + "description": "Server started", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/daemon/server/{id}/stats": { + "get": { + "description": "Gets the given server stats", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Gets server stats", + "parameters": [ + { + "type": "string", + "description": "Server Identifier", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Stats for this server", + "schema": { + "$ref": "#/definitions/pufferpanel.ServerStats" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/daemon/server/{id}/status": { + "get": { + "description": "Gets the given server status", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Gets server status", + "parameters": [ + { + "type": "string", + "description": "Server Identifier", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/pufferpanel.ServerRunning" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + }, + "/daemon/server/{id}/stop": { + "post": { + "description": "Stops the given server", + "consumes": ["application/json"], + "produces": ["application/json"], + "summary": "Stop server", + "parameters": [ + { + "type": "string", + "description": "Server Identifier", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "boolean", + "description": "Wait for the operation to complete", + "name": "wait", + "in": "query" + } + ], + "responses": { + "202": { + "description": "Stop has been queued", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "204": { + "description": "Server stopped", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Empty" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Error" + } + } + } + } + } + }, + "definitions": { + "daemon.Features": { + "type": "object", + "properties": { + "features": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "messages.FileDesc": { + "type": "object", + "properties": { + "extension": { + "type": "string" + }, + "isFile": { + "type": "boolean" + }, + "modifyTime": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "size": { + "type": "integer" + } + } + }, + "models.ChangeSetting": { + "type": "object", + "properties": { + "value": {} + } + }, + "models.ChangeUserSetting": { + "type": "object", + "properties": { + "value": { + "type": "string" + } + } + }, + "models.Client": { + "type": "object", + "properties": { + "client_id": { + "type": "string" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "models.CreateServerResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "models.CreatedClient": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "secret": { + "type": "string" + } + } + }, + "models.Deployment": { + "type": "object", + "properties": { + "clientId": { + "type": "string" + }, + "clientSecret": { + "type": "string" + }, + "publicKey": { + "type": "string" + } + } + }, + "models.GetServerResponse": { + "type": "object", + "properties": { + "permissions": { + "$ref": "#/definitions/models.PermissionView" + }, + "server": { + "$ref": "#/definitions/models.ServerView" + } + } + }, + "models.NodeView": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "isLocal": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "privateHost": { + "type": "string" + }, + "privatePort": { + "type": "integer" + }, + "publicHost": { + "type": "string" + }, + "publicPort": { + "type": "integer" + }, + "sftpPort": { + "type": "integer" + } + } + }, + "models.PermissionView": { + "type": "object", + "properties": { + "admin": { + "type": "boolean" + }, + "createServers": { + "type": "boolean" + }, + "deleteServers": { + "type": "boolean" + }, + "deployNodes": { + "type": "boolean" + }, + "editNodes": { + "type": "boolean" + }, + "editServerAdmin": { + "type": "boolean" + }, + "editServerData": { + "type": "boolean" + }, + "editServerUsers": { + "type": "boolean" + }, + "editTemplates": { + "type": "boolean" + }, + "editUsers": { + "type": "boolean" + }, + "email": { + "type": "string" + }, + "installServer": { + "type": "boolean" + }, + "panelSettings": { + "type": "boolean" + }, + "putServerFiles": { + "type": "boolean" + }, + "sendServerConsole": { + "type": "boolean" + }, + "serverIdentifier": { + "type": "string" + }, + "sftpServer": { + "type": "boolean" + }, + "startServer": { + "type": "boolean" + }, + "stopServer": { + "type": "boolean" + }, + "username": { + "type": "string" + }, + "viewNodes": { + "type": "boolean" + }, + "viewServerConsole": { + "type": "boolean" + }, + "viewServerFiles": { + "type": "boolean" + }, + "viewServerStats": { + "type": "boolean" + }, + "viewServers": { + "type": "boolean" + }, + "viewTemplates": { + "type": "boolean" + }, + "viewUsers": { + "type": "boolean" + } + } + }, + "models.ServerCreation": { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/pufferpanel.Variable" + } + }, + "display": { + "type": "string" + }, + "environment": {}, + "id": { + "type": "string" + }, + "install": { + "type": "array", + "items": {} + }, + "name": { + "type": "string" + }, + "node": { + "type": "integer" + }, + "requirements": { + "$ref": "#/definitions/pufferpanel.Requirements" + }, + "run": { + "$ref": "#/definitions/pufferpanel.Execution" + }, + "supportedEnvironments": { + "type": "array", + "items": {} + }, + "tasks": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/pufferpanel.Task" + } + }, + "type": { + "type": "string" + }, + "uninstall": { + "type": "array", + "items": {} + }, + "users": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "models.ServerSearchResponse": { + "type": "object", + "properties": { + "paging": { + "$ref": "#/definitions/response.Paging" + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/definitions/models.ServerView" + } + } + } + }, + "models.ServerUserView": { + "type": "object", + "properties": { + "scopes": { + "type": "array", + "items": { + "type": "string" + } + }, + "username": { + "type": "string" + } + } + }, + "models.ServerView": { + "type": "object", + "properties": { + "data": {}, + "id": { + "type": "string" + }, + "ip": { + "type": "string" + }, + "name": { + "type": "string" + }, + "node": { + "$ref": "#/definitions/models.NodeView" + }, + "nodeId": { + "type": "integer" + }, + "port": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "users": { + "type": "array", + "items": { + "$ref": "#/definitions/models.ServerUserView" + } + } + } + }, + "models.SettingResponse": { + "type": "object", + "properties": { + "value": {} + } + }, + "models.Template": { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/pufferpanel.Variable" + } + }, + "display": { + "type": "string" + }, + "environment": {}, + "id": { + "type": "string" + }, + "install": { + "type": "array", + "items": {} + }, + "name": { + "type": "string" + }, + "readme": { + "type": "string" + }, + "requirements": { + "$ref": "#/definitions/pufferpanel.Requirements" + }, + "run": { + "$ref": "#/definitions/pufferpanel.Execution" + }, + "supportedEnvironments": { + "type": "array", + "items": {} + }, + "tasks": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/pufferpanel.Task" + } + }, + "type": { + "type": "string" + }, + "uninstall": { + "type": "array", + "items": {} + } + } + }, + "models.User": { + "type": "object", + "properties": { + "otpActive": { + "type": "boolean" + } + } + }, + "models.UserSearch": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "page": { + "type": "integer" + }, + "pageLimit": { + "type": "integer" + }, + "username": { + "type": "string" + } + } + }, + "models.UserSearchResponse": { + "type": "object", + "properties": { + "paging": { + "$ref": "#/definitions/response.Paging" + }, + "users": { + "type": "array", + "items": { + "$ref": "#/definitions/models.UserView" + } + } + } + }, + "models.UserSettingView": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "models.UserView": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "newPassword": { + "type": "string" + }, + "password": { + "description": "ONLY SHOW WHEN COPYING", + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "pufferpanel.DaemonRunning": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + }, + "pufferpanel.Error": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "metadata": { + "type": "object", + "additionalProperties": true + }, + "msg": { + "type": "string" + } + } + }, + "pufferpanel.Execution": { + "type": "object", + "properties": { + "arguments": { + "type": "array", + "items": { + "type": "string" + } + }, + "autorecover": { + "type": "boolean" + }, + "autorestart": { + "type": "boolean" + }, + "autostart": { + "type": "boolean" + }, + "command": { + "type": "string" + }, + "disabled": { + "type": "boolean" + }, + "environmentVars": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "post": { + "type": "array", + "items": {} + }, + "pre": { + "type": "array", + "items": {} + }, + "program": { + "type": "string" + }, + "stop": { + "type": "string" + }, + "stopCode": { + "type": "integer" + }, + "workingDirectory": { + "type": "string" + } + } + }, + "pufferpanel.Requirements": { + "type": "object", + "properties": { + "arch": { + "type": "string" + }, + "binaries": { + "type": "array", + "items": { + "type": "string" + } + }, + "os": { + "type": "string" + } + } + }, + "pufferpanel.Server": { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/pufferpanel.Variable" + } + }, + "display": { + "type": "string" + }, + "environment": {}, + "id": { + "type": "string" + }, + "install": { + "type": "array", + "items": {} + }, + "requirements": { + "$ref": "#/definitions/pufferpanel.Requirements" + }, + "run": { + "$ref": "#/definitions/pufferpanel.Execution" + }, + "supportedEnvironments": { + "type": "array", + "items": {} + }, + "tasks": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/pufferpanel.Task" + } + }, + "type": { + "type": "string" + }, + "uninstall": { + "type": "array", + "items": {} + } + } + }, + "pufferpanel.ServerData": { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/pufferpanel.Variable" + } + } + } + }, + "pufferpanel.ServerDataAdmin": { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/pufferpanel.Variable" + } + }, + "display": { + "type": "string" + }, + "environment": {}, + "id": { + "type": "string" + }, + "install": { + "type": "array", + "items": {} + }, + "requirements": { + "$ref": "#/definitions/pufferpanel.Requirements" + }, + "run": { + "$ref": "#/definitions/pufferpanel.Execution" + }, + "supportedEnvironments": { + "type": "array", + "items": {} + }, + "tasks": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/pufferpanel.Task" + } + }, + "type": { + "type": "string" + }, + "uninstall": { + "type": "array", + "items": {} + } + } + }, + "pufferpanel.ServerIdResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "pufferpanel.ServerLogs": { + "type": "object", + "properties": { + "epoch": { + "type": "integer" + }, + "logs": { + "type": "string" + } + } + }, + "pufferpanel.ServerRunning": { + "type": "object", + "properties": { + "running": { + "type": "boolean" + } + } + }, + "pufferpanel.ServerStats": { + "type": "object", + "properties": { + "cpu": { + "type": "number" + }, + "memory": { + "type": "number" + } + } + }, + "pufferpanel.Task": { + "type": "object", + "required": ["name", "operations"], + "properties": { + "cronSchedule": { + "type": "string" + }, + "name": { + "type": "string" + }, + "operations": { + "type": "array", + "items": {} + } + } + }, + "pufferpanel.Variable": { + "type": "object", + "properties": { + "desc": { + "type": "string" + }, + "display": { + "type": "string" + }, + "internal": { + "type": "boolean" + }, + "options": { + "type": "array", + "items": { + "$ref": "#/definitions/pufferpanel.VariableOption" + } + }, + "required": { + "type": "boolean" + }, + "type": { + "type": "string" + }, + "userEdit": { + "type": "boolean" + }, + "value": {} + } + }, + "pufferpanel.VariableOption": { + "type": "object", + "properties": { + "display": { + "type": "string" + }, + "value": {} + } + }, + "response.Empty": { + "type": "object" + }, + "response.Error": { + "type": "object", + "properties": { + "error": { + "$ref": "#/definitions/pufferpanel.Error" + } + } + }, + "response.Paging": { + "type": "object", + "properties": { + "maxSize": { + "type": "integer" + }, + "page": { + "type": "integer" + }, + "pageSize": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + } + } +} diff --git a/src/main copy.py b/src/main copy.py new file mode 100644 index 0000000..b9fd8b5 --- /dev/null +++ b/src/main copy.py @@ -0,0 +1,242 @@ +import asyncio +import base64 +import binascii +import json +import time +from re import compile +from typing import Any, AsyncGenerator, Callable + +import aiohttp + + +class Minecraft: + def __init__( + self, + host: str, + client_id: str, + client_secret: str, + *, + server_id: str | None = None, + server_name: str | None = None, + session: aiohttp.ClientSession | None = None, + poll_interval: float = 1.0, + timeout: float = 15.0, + verify_ssl: bool = True, + ): + if not host: + raise ValueError("host is required") + if not client_id or not client_secret: + raise ValueError("client_id and client_secret are required") + if not server_id and not server_name: + raise ValueError("server_id or server_name is required") + + self._base_url = host.rstrip("/") + self._client_id = client_id + self._client_secret = client_secret + self._server_id = server_id + self._server_name = server_name + self._poll_interval = poll_interval + self._ssl = None if verify_ssl else False + + self._session = session or aiohttp.ClientSession( + timeout=aiohttp.ClientTimeout(total=timeout) + ) + self._own_session = session is None + self._token: str | None = None + self._token_expires_at = 0.0 + self._token_lock = asyncio.Lock() + + self._hooks: list[Callable] = [] + + async def close(self): + if self._own_session and not self._session.closed: + await self._session.close() + + async def command(self, command: str) -> str: + "Runs a command on the Server console." + if not command: + raise ValueError("command is required") + await self._ensure_server_id() + await self._request( + "POST", + f"/api/servers/{self._server_id}/console", + json_body=command, + ) + return "" + + async def logs_stream(self) -> AsyncGenerator[str, Any]: + "Streams the Server logs as a generator of strings." + await self._ensure_server_id() + # Start from "now" to avoid dumping the full backlog. + last_epoch = int(time.time() * 1_000_000) + + while True: + epoch, lines = await self._fetch_logs(time_param=last_epoch) + if epoch is not None and epoch > last_epoch: + last_epoch = epoch + for line in lines: + yield line + await asyncio.sleep(self._poll_interval) + + async def logs_tail(self, back: int = 10) -> AsyncGenerator[str, Any]: + "Returns the last `back` lines of the Server logs as a generator of strings." + if back <= 0: + return + yield # pragma: no cover - keeps this as an async generator + await self._ensure_server_id() + _, lines = await self._fetch_logs(time_param=0) + for line in lines[-back:]: + yield line + + async def logs_tails(self, back: int = 10) -> AsyncGenerator[str, Any]: + "Alias for logs_tail (kept for backward compatibility)." + async for line in self.logs_tail(back=back): + yield line + + def onConsoleLog(self, pattern: str | None = None) -> Callable: + "Registers a callback function to be called when a line matching the given pattern is logged." + + def wrapper(func: Callable): + compiled = compile(pattern) if pattern else None + + async def callback(line: str): + if compiled is None: + func(line) + return + + match = compiled.fullmatch(line) + if match is None: + return + + groups = match.groups() + if groups: + await func(*groups) + else: + await func(line) + + self._hooks.append(callback) + return func + + return wrapper + + async def loop(self): + async for line in self.logs_stream(): + for hook in self._hooks: + await hook(line) + + async def _ensure_token(self) -> None: + async with self._token_lock: + if self._token and time.monotonic() < (self._token_expires_at - 30): + return + url = f"{self._base_url}/oauth2/token" + payload = { + "grant_type": "client_credentials", + "client_id": self._client_id, + "client_secret": self._client_secret, + } + async with self._session.post(url, data=payload, ssl=self._ssl) as resp: + data = await self._read_json(resp) + token = data.get("access_token") + if not token: + raise RuntimeError("Failed to obtain access token from PufferPanel") + expires_in = int(data.get("expires_in", 3600)) + self._token = token + self._token_expires_at = time.monotonic() + max(expires_in, 0) + + async def _ensure_server_id(self) -> None: + if self._server_id: + return + if not self._server_name: + raise RuntimeError("server_id is not set and server_name is missing") + data = await self._request_json("GET", "/api/servers") + servers = data.get("servers", []) + for server in servers: + if server.get("name") == self._server_name: + self._server_id = server.get("id") + break + if not self._server_id: + raise RuntimeError(f"Server named {self._server_name} was not found") + + async def _request( + self, + method: str, + path: str, + *, + params: dict[str, Any] | None = None, + json_body: Any | None = None, + ) -> None: + await self._ensure_token() + url = f"{self._base_url}{path}" + headers = {"Authorization": f"Bearer {self._token}"} + async with self._session.request( + method, + url, + params=params, + json=json_body, + headers=headers, + ssl=self._ssl, + ) as resp: + if resp.status >= 400: + body = await resp.text() + raise RuntimeError( + f"PufferPanel API error {resp.status} for {method} {path}: {body}" + ) + + async def _request_json( + self, + method: str, + path: str, + *, + params: dict[str, Any] | None = None, + json_body: Any | None = None, + ) -> dict[str, Any]: + await self._ensure_token() + url = f"{self._base_url}{path}" + headers = {"Authorization": f"Bearer {self._token}"} + async with self._session.request( + method, + url, + params=params, + json=json_body, + headers=headers, + ssl=self._ssl, + ) as resp: + return await self._read_json(resp) + + async def _read_json(self, resp: aiohttp.ClientResponse) -> dict[str, Any]: + text = await resp.text() + if resp.status >= 400: + raise RuntimeError( + f"PufferPanel API error {resp.status} for {resp.request_info.method} " + f"{resp.request_info.url}: {text}" + ) + if not text: + return {} + try: + return json.loads(text) + except json.JSONDecodeError as exc: + raise RuntimeError(f"Invalid JSON response: {text}") from exc + + async def _fetch_logs(self, *, time_param: int) -> tuple[int | None, list[str]]: + data = await self._request_json( + "GET", + f"/api/servers/{self._server_id}/console", + params={"time": time_param}, + ) + epoch = data.get("epoch") + raw_logs = data.get("logs") or "" + decoded = self._decode_logs(raw_logs) + lines = decoded.splitlines() if decoded else [] + return epoch, lines + + def _decode_logs(self, raw_logs: str) -> str: + if not raw_logs: + return "" + try: + decoded = base64.b64decode(raw_logs) + except (binascii.Error, ValueError): + return raw_logs + try: + return decoded.decode("utf-8", errors="replace") + except Exception: + return decoded.decode(errors="replace") diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..c1c9527 --- /dev/null +++ b/src/main.py @@ -0,0 +1,90 @@ +import binascii +from asyncio import Lock +from base64 import b64decode +from time import monotonic, time +from typing import Any, Callable, Literal + +from aiohttp import ClientSession + + +class Minecraft: + def __init__(self, host: str, client_id: str, client_secret: str, server_id: str): + self.host = host.rstrip("/") + self.client_id = client_id + self.client_secret = client_secret + self.server_id = server_id + + self.session = ClientSession() + + self.token: str | None = None + self.token_expiry: float = 0.0 + self.token_lock = Lock() + + self.hooks: list[Callable] = [] + + async def close(self): + if not self.session.closed: + await self.session.close() + + async def _request( + self, + method: Literal["GET", "POST"], + path: str, + params: dict[str, Any] | None = None, + body: Any | None = None, + ) -> Any: + async with self.token_lock: + if self.token and monotonic() < self.token_expiry - 30: + return + + url = f"{self.host}/oauth2/token" + payload = { + "grant_type": "client_credentials", + "client_id": self.client_id, + "client_secret": self.client_secret, + } + + async with self.session.post(url, data=payload) as response: + if response.status == 200: + data = await response.json() + else: + raise RuntimeError("Error") + + token = data.get("access_token") + if not token: + raise RuntimeError("Error") + + expires_in = int(data.get("expires_in", 3600)) + self.token = token + self.token_expiry = monotonic() + expires_in + + url = f"{self.host}{path}" + headers = {"Authorization": f"Bearer {self.token}"} + async with self.session.request( + method, url, params=params, json=body, headers=headers + ) as response: + return await response.json() + + async def _fetch_logs(self, last_epoch: int) -> AsyncGenerator[str, None]: + data = await self._request( + "GET", f"/api/servers/{self.server_id}/console", params={"time": last_epoch} + ) + epoch = data.get("epoch") + raw_logs = data.get("logs", "") + try: + ... + except (binascii.Error, ValueError): + decoded = raw_logs + + async def command(self, command: str) -> str: + await self._request( + "POST", f"/api/servers/{self.server_id}/console", body=command + ) + + return "" # TODO: return command output + + async def logs_stream(self) -> AsyncGenerator[str, None]: + last_epoch = int(time() * 1_000_000) + + while True: + ... diff --git a/test.py b/test.py new file mode 100644 index 0000000..5b51d11 --- /dev/null +++ b/test.py @@ -0,0 +1,62 @@ +import asyncio +import os +import sys +from pathlib import Path + + +def load_env_file(path: Path) -> None: + if not path.exists(): + return + for raw in path.read_text(encoding="utf-8").splitlines(): + line = raw.strip() + if not line or line.startswith("#"): + continue + if "=" not in line: + continue + key, value = line.split("=", 1) + key = key.strip() + value = value.strip().strip('"').strip("'") + os.environ.setdefault(key, value) + + +async def main() -> None: + root = Path(__file__).resolve().parent + load_env_file(root / ".env") + + sys.path.insert(0, str(root / "src")) + from src.main import Minecraft + + host = os.environ.get("SERVER_HOST") + client_id = os.environ.get("PUFFERPANEL_CLIENT_ID") + client_secret = os.environ.get("PUFFERPANEL_CLIENT_SECRET") + if not host or not client_id or not client_secret: + raise RuntimeError( + "Missing SERVER_HOST / PUFFERPANEL_CLIENT_ID / PUFFERPANEL_CLIENT_SECRET" + ) + + mc = Minecraft( + host=host, + client_id=client_id, + client_secret=client_secret, + server_name="Retards Server", + poll_interval=0.5, + ) + + try: + await mc.command("say hi from test.py") + + tail = [line async for line in mc.logs_tail(5)] + print("tail (last 5 lines):") + for line in tail: + print(line) + + print("streaming next log line...") + async for line in mc.logs_stream(): + print("stream:", line) + break + finally: + await mc.close() + + +if __name__ == "__main__": + asyncio.run(main())