Add initial project files

This commit is contained in:
luskbyte
2026-04-24 15:36:53 -03:00
parent f1d8fefc5a
commit f618d107ed
13 changed files with 1937 additions and 0 deletions
+121
View File
@@ -0,0 +1,121 @@
package handlers
import (
"docker-manager/docker"
"encoding/json"
"fmt"
"io"
"net/http"
)
func ListContainersHandler(w http.ResponseWriter, r *http.Request, client *docker.Client) {
var containers []map[string]interface{}
if err := client.GetJSON("/containers/json?all=true", &containers); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
if containers == nil {
w.Write([]byte("[]"))
} else {
json.NewEncoder(w).Encode(containers)
}
}
func StartContainerHandler(w http.ResponseWriter, r *http.Request, client *docker.Client, id string) {
if resp, err := client.Do("POST", fmt.Sprintf("/containers/%s/start", id), nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
} else {
defer resp.Body.Close()
w.WriteHeader(resp.StatusCode)
}
}
func StopContainerHandler(w http.ResponseWriter, r *http.Request, client *docker.Client, id string) {
if resp, err := client.Do("POST", fmt.Sprintf("/containers/%s/stop", id), nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
} else {
defer resp.Body.Close()
w.WriteHeader(resp.StatusCode)
}
}
func RestartContainerHandler(w http.ResponseWriter, r *http.Request, client *docker.Client, id string) {
if resp, err := client.Do("POST", fmt.Sprintf("/containers/%s/restart", id), nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
} else {
defer resp.Body.Close()
w.WriteHeader(resp.StatusCode)
}
}
func RemoveContainerHandler(w http.ResponseWriter, r *http.Request, client *docker.Client, id string) {
if resp, err := client.Do("DELETE", fmt.Sprintf("/containers/%s?force=true", id), nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
} else {
defer resp.Body.Close()
w.WriteHeader(resp.StatusCode)
}
}
func GetLogsHandler(w http.ResponseWriter, r *http.Request, client *docker.Client, id string) {
body, err := client.DoStream("GET", fmt.Sprintf("/containers/%s/logs?stdout=true&stderr=true&tail=200", id), nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer body.Close()
// Docker logs returns docker stream format, need to parse it
logs := parseLogs(body)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"logs": logs})
}
// Docker log stream format: [8 bytes header][payload]
// Header: [1 byte stream type][3 bytes padding][4 bytes payload length]
func parseLogs(body io.Reader) string {
var result string
buffer := make([]byte, 4096)
for {
n, err := body.Read(buffer)
if err != nil && err != io.EOF {
break
}
if n == 0 {
break
}
// Parse docker stream format
data := buffer[:n]
for i := 0; i < len(data); {
if i+8 > len(data) {
break
}
// Skip 8-byte header
i += 8
// Read until next header or end
start := i
for i < len(data) && i+8 <= len(data) {
i += 8
if i > len(data) {
i = len(data)
break
}
}
if start < len(data) {
result += string(data[start : len(data)])
i = len(data)
}
}
}
// Fallback: if stream format parsing fails, just return the raw data as text
if result == "" {
return string(buffer)
}
return result
}
+71
View File
@@ -0,0 +1,71 @@
package handlers
import (
"docker-manager/docker"
"encoding/json"
"fmt"
"net/http"
"strings"
)
func ListImagesHandler(w http.ResponseWriter, r *http.Request, client *docker.Client) {
var images []map[string]interface{}
if err := client.GetJSON("/images/json", &images); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
if images == nil {
w.Write([]byte("[]"))
} else {
json.NewEncoder(w).Encode(images)
}
}
func RemoveImageHandler(w http.ResponseWriter, r *http.Request, client *docker.Client, id string) {
if resp, err := client.Do("DELETE", fmt.Sprintf("/images/%s?force=true", id), nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
} else {
defer resp.Body.Close()
w.WriteHeader(resp.StatusCode)
}
}
func PullImageHandler(w http.ResponseWriter, r *http.Request, client *docker.Client) {
name := r.URL.Query().Get("name")
if name == "" {
http.Error(w, "name parameter required", http.StatusBadRequest)
return
}
// Strip tag if contains ":"
ref := name
if strings.Contains(name, ":") {
parts := strings.Split(name, ":")
ref = parts[0]
}
body, err := client.DoStream("POST", fmt.Sprintf("/images/create?fromImage=%s", ref), nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer body.Close()
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Transfer-Encoding", "chunked")
// Stream pull progress as JSON lines
decoder := json.NewDecoder(body)
for {
var line map[string]interface{}
if err := decoder.Decode(&line); err != nil {
break
}
json.NewEncoder(w).Encode(line)
if flusher, ok := w.(http.Flusher); ok {
flusher.Flush()
}
}
}
+18
View File
@@ -0,0 +1,18 @@
package handlers
import (
"docker-manager/docker"
"encoding/json"
"net/http"
)
func InfoHandler(w http.ResponseWriter, r *http.Request, client *docker.Client) {
var info map[string]interface{}
if err := client.GetJSON("/info", &info); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(info)
}