mirror of
https://github.com/hanzoai/vm.git
synced 2026-10-01 00:47:56 +00:00
- Complete DO CreateMachine (was stub), add AWS Lightsail and Hetzner providers - Add Plan model with seed catalog ($5-$49 tiers, Hetzner-first routing) - Add Volume model with Hetzner and DO block storage support - Register plan and volume API routes
199 lines
5.0 KiB
Go
199 lines
5.0 KiB
Go
// Copyright 2025 Hanzo Industries Inc. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package controllers
|
|
|
|
import (
|
|
"encoding/json"
|
|
|
|
"github.com/hanzoai/visor/object"
|
|
"github.com/hanzoai/visor/service"
|
|
)
|
|
|
|
// GetVolumes
|
|
// @Title GetVolumes
|
|
// @Tag Volume API
|
|
// @Description get all volumes for an owner
|
|
// @Param owner query string true "The owner"
|
|
// @Success 200 {array} object.Volume
|
|
// @router /get-volumes [get]
|
|
func (c *ApiController) GetVolumes() {
|
|
owner := c.Input().Get("owner")
|
|
volumes, err := object.GetVolumes(owner)
|
|
if err != nil {
|
|
c.ResponseError(err.Error())
|
|
return
|
|
}
|
|
c.ResponseOk(volumes)
|
|
}
|
|
|
|
// GetVolume
|
|
// @Title GetVolume
|
|
// @Tag Volume API
|
|
// @Description get a single volume
|
|
// @Param owner query string true "The owner"
|
|
// @Param name query string true "The volume name"
|
|
// @Success 200 {object} object.Volume
|
|
// @router /get-volume [get]
|
|
func (c *ApiController) GetVolume() {
|
|
owner := c.Input().Get("owner")
|
|
name := c.Input().Get("name")
|
|
|
|
volume, err := object.GetVolume(owner, name)
|
|
if err != nil {
|
|
c.ResponseError(err.Error())
|
|
return
|
|
}
|
|
c.ResponseOk(volume)
|
|
}
|
|
|
|
// CreateVolume
|
|
// @Title CreateVolume
|
|
// @Tag Volume API
|
|
// @Description create a block storage volume via a provider
|
|
// @Param owner query string true "The owner"
|
|
// @Param provider query string true "The provider name"
|
|
// @Param body body service.CreateVolumeSpec true "The volume spec"
|
|
// @Success 200 {object} object.Volume
|
|
// @router /create-volume [post]
|
|
func (c *ApiController) CreateVolume() {
|
|
owner := c.Input().Get("owner")
|
|
providerName := c.Input().Get("provider")
|
|
|
|
if owner == "" || providerName == "" {
|
|
c.ResponseError("owner and provider query parameters are required")
|
|
return
|
|
}
|
|
|
|
var spec service.CreateVolumeSpec
|
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &spec)
|
|
if err != nil {
|
|
c.ResponseError(err.Error())
|
|
return
|
|
}
|
|
|
|
volume, err := object.CreateVolumeCloud(owner, providerName, &spec)
|
|
if err != nil {
|
|
c.ResponseError(err.Error())
|
|
return
|
|
}
|
|
|
|
c.ResponseOk(volume)
|
|
}
|
|
|
|
// DeleteVolume
|
|
// @Title DeleteVolume
|
|
// @Tag Volume API
|
|
// @Description delete a volume
|
|
// @Param owner query string true "The owner"
|
|
// @Param provider query string true "The provider name"
|
|
// @Param name query string true "The volume cloud ID"
|
|
// @Success 200 {object} controllers.Response
|
|
// @router /delete-volume [post]
|
|
func (c *ApiController) DeleteVolume() {
|
|
owner := c.Input().Get("owner")
|
|
providerName := c.Input().Get("provider")
|
|
name := c.Input().Get("name")
|
|
|
|
err := object.DeleteVolumeCloud(owner, providerName, name)
|
|
if err != nil {
|
|
c.ResponseError(err.Error())
|
|
return
|
|
}
|
|
|
|
c.Data["json"] = wrapActionResponse(true, nil)
|
|
c.ServeJSON()
|
|
}
|
|
|
|
// AttachVolume
|
|
// @Title AttachVolume
|
|
// @Tag Volume API
|
|
// @Description attach a volume to a machine
|
|
// @router /attach-volume [post]
|
|
func (c *ApiController) AttachVolume() {
|
|
owner := c.Input().Get("owner")
|
|
providerName := c.Input().Get("provider")
|
|
volumeName := c.Input().Get("volume")
|
|
machineName := c.Input().Get("machine")
|
|
|
|
err := object.AttachVolumeCloud(owner, providerName, volumeName, machineName)
|
|
if err != nil {
|
|
c.ResponseError(err.Error())
|
|
return
|
|
}
|
|
|
|
c.Data["json"] = wrapActionResponse(true, nil)
|
|
c.ServeJSON()
|
|
}
|
|
|
|
// DetachVolume
|
|
// @Title DetachVolume
|
|
// @Tag Volume API
|
|
// @Description detach a volume from its machine
|
|
// @router /detach-volume [post]
|
|
func (c *ApiController) DetachVolume() {
|
|
owner := c.Input().Get("owner")
|
|
providerName := c.Input().Get("provider")
|
|
volumeName := c.Input().Get("volume")
|
|
|
|
err := object.DetachVolumeCloud(owner, providerName, volumeName)
|
|
if err != nil {
|
|
c.ResponseError(err.Error())
|
|
return
|
|
}
|
|
|
|
c.Data["json"] = wrapActionResponse(true, nil)
|
|
c.ServeJSON()
|
|
}
|
|
|
|
// ResizeVolume
|
|
// @Title ResizeVolume
|
|
// @Tag Volume API
|
|
// @Description resize a volume
|
|
// @router /resize-volume [post]
|
|
func (c *ApiController) ResizeVolume() {
|
|
owner := c.Input().Get("owner")
|
|
providerName := c.Input().Get("provider")
|
|
volumeName := c.Input().Get("volume")
|
|
sizeStr := c.Input().Get("size")
|
|
|
|
size := 0
|
|
if sizeStr != "" {
|
|
size = parseInt(sizeStr)
|
|
}
|
|
if size <= 0 {
|
|
c.ResponseError("size parameter must be a positive integer (GB)")
|
|
return
|
|
}
|
|
|
|
err := object.ResizeVolumeCloud(owner, providerName, volumeName, size)
|
|
if err != nil {
|
|
c.ResponseError(err.Error())
|
|
return
|
|
}
|
|
|
|
c.Data["json"] = wrapActionResponse(true, nil)
|
|
c.ServeJSON()
|
|
}
|
|
|
|
func parseInt(s string) int {
|
|
n := 0
|
|
for _, c := range s {
|
|
if c >= '0' && c <= '9' {
|
|
n = n*10 + int(c-'0')
|
|
}
|
|
}
|
|
return n
|
|
}
|