diff --git a/goutils b/goutils index bc9e96eb..bcc4b498 160000 --- a/goutils +++ b/goutils @@ -1 +1 @@ -Subproject commit bc9e96ebf387ae954638f73ac7309d464cc207f9 +Subproject commit bcc4b498f87881bfa2ddd7873a49211c76f3ec03 diff --git a/internal/api/handler.go b/internal/api/handler.go index 2094b38d..fc080106 100644 --- a/internal/api/handler.go +++ b/internal/api/handler.go @@ -149,6 +149,9 @@ func NewHandler(requireAuth bool) *gin.Engine { proxmox.GET("/journalctl/:node/:vmid", proxmoxApi.Journalctl) proxmox.GET("/journalctl/:node/:vmid/:service", proxmoxApi.Journalctl) proxmox.GET("/stats/:node/:vmid", proxmoxApi.Stats) + proxmox.POST("/lxc/:node/:vmid/start", proxmoxApi.Start) + proxmox.POST("/lxc/:node/:vmid/stop", proxmoxApi.Stop) + proxmox.POST("/lxc/:node/:vmid/restart", proxmoxApi.Restart) } } diff --git a/internal/api/v1/docs/swagger.json b/internal/api/v1/docs/swagger.json index 4d63d8a9..1cdf3a8b 100644 --- a/internal/api/v1/docs/swagger.json +++ b/internal/api/v1/docs/swagger.json @@ -310,6 +310,168 @@ "operationId": "journalctl" } }, + "/api/v1/proxmox/lxc/:node/:vmid/restart": { + "post": { + "description": "Restart LXC container by node and vmid", + "produces": [ + "application/json" + ], + "tags": [ + "proxmox" + ], + "summary": "Restart LXC container", + "parameters": [ + { + "type": "string", + "name": "node", + "in": "path", + "required": true + }, + { + "type": "integer", + "name": "vmid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/SuccessResponse" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, + "404": { + "description": "Node not found", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + } + }, + "x-id": "lxcRestart", + "operationId": "lxcRestart" + } + }, + "/api/v1/proxmox/lxc/:node/:vmid/start": { + "post": { + "description": "Start LXC container by node and vmid", + "produces": [ + "application/json" + ], + "tags": [ + "proxmox" + ], + "summary": "Start LXC container", + "parameters": [ + { + "type": "string", + "name": "node", + "in": "path", + "required": true + }, + { + "type": "integer", + "name": "vmid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/SuccessResponse" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, + "404": { + "description": "Node not found", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + } + }, + "x-id": "lxcStart", + "operationId": "lxcStart" + } + }, + "/api/v1/proxmox/lxc/:node/:vmid/stop": { + "post": { + "description": "Stop LXC container by node and vmid", + "produces": [ + "application/json" + ], + "tags": [ + "proxmox" + ], + "summary": "Stop LXC container", + "parameters": [ + { + "type": "string", + "name": "node", + "in": "path", + "required": true + }, + { + "type": "integer", + "name": "vmid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/SuccessResponse" + } + }, + "400": { + "description": "Invalid request", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, + "404": { + "description": "Node not found", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + } + }, + "x-id": "lxcStop", + "operationId": "lxcStop" + } + }, "/api/v1/proxmox/stats/{node}/{vmid}": { "get": { "description": "Get proxmox stats in format of \"STATUS|CPU%%|MEM USAGE/LIMIT|MEM%%|NET I/O|BLOCK I/O\"", diff --git a/internal/api/v1/docs/swagger.yaml b/internal/api/v1/docs/swagger.yaml index 7f361253..314598d6 100644 --- a/internal/api/v1/docs/swagger.yaml +++ b/internal/api/v1/docs/swagger.yaml @@ -2183,6 +2183,111 @@ paths: - proxmox - websocket x-id: journalctl + /api/v1/proxmox/lxc/:node/:vmid/restart: + post: + description: Restart LXC container by node and vmid + parameters: + - in: path + name: node + required: true + type: string + - in: path + name: vmid + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/SuccessResponse' + "400": + description: Invalid request + schema: + $ref: '#/definitions/ErrorResponse' + "404": + description: Node not found + schema: + $ref: '#/definitions/ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/ErrorResponse' + summary: Restart LXC container + tags: + - proxmox + x-id: lxcRestart + /api/v1/proxmox/lxc/:node/:vmid/start: + post: + description: Start LXC container by node and vmid + parameters: + - in: path + name: node + required: true + type: string + - in: path + name: vmid + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/SuccessResponse' + "400": + description: Invalid request + schema: + $ref: '#/definitions/ErrorResponse' + "404": + description: Node not found + schema: + $ref: '#/definitions/ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/ErrorResponse' + summary: Start LXC container + tags: + - proxmox + x-id: lxcStart + /api/v1/proxmox/lxc/:node/:vmid/stop: + post: + description: Stop LXC container by node and vmid + parameters: + - in: path + name: node + required: true + type: string + - in: path + name: vmid + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/SuccessResponse' + "400": + description: Invalid request + schema: + $ref: '#/definitions/ErrorResponse' + "404": + description: Node not found + schema: + $ref: '#/definitions/ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/ErrorResponse' + summary: Stop LXC container + tags: + - proxmox + x-id: lxcStop /api/v1/proxmox/stats/{node}/{vmid}: get: consumes: diff --git a/internal/api/v1/proxmox/common.go b/internal/api/v1/proxmox/common.go new file mode 100644 index 00000000..10477d0b --- /dev/null +++ b/internal/api/v1/proxmox/common.go @@ -0,0 +1,6 @@ +package proxmoxapi + +type ActionRequest struct { + Node string `uri:"node" binding:"required"` + VMID int `uri:"vmid" binding:"required"` +} diff --git a/internal/api/v1/proxmox/restart.go b/internal/api/v1/proxmox/restart.go new file mode 100644 index 00000000..fb6a7a4b --- /dev/null +++ b/internal/api/v1/proxmox/restart.go @@ -0,0 +1,42 @@ +package proxmoxapi + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/yusing/godoxy/internal/proxmox" + apitypes "github.com/yusing/goutils/apitypes" +) + +// @x-id "lxcRestart" +// @BasePath /api/v1 +// @Summary Restart LXC container +// @Description Restart LXC container by node and vmid +// @Tags proxmox +// @Produce json +// @Param path path ActionRequest true "Request" +// @Success 200 {object} apitypes.SuccessResponse +// @Failure 400 {object} apitypes.ErrorResponse "Invalid request" +// @Failure 404 {object} apitypes.ErrorResponse "Node not found" +// @Failure 500 {object} apitypes.ErrorResponse +// @Router /api/v1/proxmox/lxc/:node/:vmid/restart [post] +func Restart(c *gin.Context) { + var req ActionRequest + if err := c.ShouldBindUri(&req); err != nil { + c.JSON(http.StatusBadRequest, apitypes.Error("invalid request", err)) + return + } + + node, ok := proxmox.Nodes.Get(req.Node) + if !ok { + c.JSON(http.StatusNotFound, apitypes.Error("node not found")) + return + } + + if err := node.LXCAction(c.Request.Context(), req.VMID, proxmox.LXCReboot); err != nil { + c.Error(apitypes.InternalServerError(err, "failed to restart container")) + return + } + + c.JSON(http.StatusOK, apitypes.Success("container restarted")) +} diff --git a/internal/api/v1/proxmox/start.go b/internal/api/v1/proxmox/start.go new file mode 100644 index 00000000..1f2dfe3f --- /dev/null +++ b/internal/api/v1/proxmox/start.go @@ -0,0 +1,42 @@ +package proxmoxapi + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/yusing/godoxy/internal/proxmox" + apitypes "github.com/yusing/goutils/apitypes" +) + +// @x-id "lxcStart" +// @BasePath /api/v1 +// @Summary Start LXC container +// @Description Start LXC container by node and vmid +// @Tags proxmox +// @Produce json +// @Param path path ActionRequest true "Request" +// @Success 200 {object} apitypes.SuccessResponse +// @Failure 400 {object} apitypes.ErrorResponse "Invalid request" +// @Failure 404 {object} apitypes.ErrorResponse "Node not found" +// @Failure 500 {object} apitypes.ErrorResponse +// @Router /api/v1/proxmox/lxc/:node/:vmid/start [post] +func Start(c *gin.Context) { + var req ActionRequest + if err := c.ShouldBindUri(&req); err != nil { + c.JSON(http.StatusBadRequest, apitypes.Error("invalid request", err)) + return + } + + node, ok := proxmox.Nodes.Get(req.Node) + if !ok { + c.JSON(http.StatusNotFound, apitypes.Error("node not found")) + return + } + + if err := node.LXCAction(c.Request.Context(), req.VMID, proxmox.LXCStart); err != nil { + c.Error(apitypes.InternalServerError(err, "failed to start container")) + return + } + + c.JSON(http.StatusOK, apitypes.Success("container started")) +} diff --git a/internal/api/v1/proxmox/stop.go b/internal/api/v1/proxmox/stop.go new file mode 100644 index 00000000..363d5e73 --- /dev/null +++ b/internal/api/v1/proxmox/stop.go @@ -0,0 +1,42 @@ +package proxmoxapi + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/yusing/godoxy/internal/proxmox" + apitypes "github.com/yusing/goutils/apitypes" +) + +// @x-id "lxcStop" +// @BasePath /api/v1 +// @Summary Stop LXC container +// @Description Stop LXC container by node and vmid +// @Tags proxmox +// @Produce json +// @Param path path ActionRequest true "Request" +// @Success 200 {object} apitypes.SuccessResponse +// @Failure 400 {object} apitypes.ErrorResponse "Invalid request" +// @Failure 404 {object} apitypes.ErrorResponse "Node not found" +// @Failure 500 {object} apitypes.ErrorResponse +// @Router /api/v1/proxmox/lxc/:node/:vmid/stop [post] +func Stop(c *gin.Context) { + var req ActionRequest + if err := c.ShouldBindUri(&req); err != nil { + c.JSON(http.StatusBadRequest, apitypes.Error("invalid request", err)) + return + } + + node, ok := proxmox.Nodes.Get(req.Node) + if !ok { + c.JSON(http.StatusNotFound, apitypes.Error("node not found")) + return + } + + if err := node.LXCAction(c.Request.Context(), req.VMID, proxmox.LXCShutdown); err != nil { + c.Error(apitypes.InternalServerError(err, "failed to stop container")) + return + } + + c.JSON(http.StatusOK, apitypes.Success("container stopped")) +} diff --git a/internal/go-proxmox b/internal/go-proxmox index 38d75007..8560d075 160000 --- a/internal/go-proxmox +++ b/internal/go-proxmox @@ -1 +1 @@ -Subproject commit 38d75007b2da486f64c1fb181b9add6e558b693b +Subproject commit 8560d07538e8f28914f306d1d246484ae2454a17