Initial commit: DSH Web Manager

This commit is contained in:
2026-10-13 09:34:47 +08:00
commit 85332bbc56
135 changed files with 19712 additions and 0 deletions
+128
View File
@@ -0,0 +1,128 @@
package api
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/url"
"os"
)
// Reconcile checks persisted identities against Docker without deleting unknown
// containers. Orphans require explicit administrator review, never blind adoption.
func (a *API) Reconcile(ctx context.Context) error {
a.mu.Lock()
defer a.mu.Unlock()
filters, _ := json.Marshal(map[string][]string{"label": {"dsh-mgr.instance"}})
var containers []struct {
ID string
State string
Labels map[string]string
}
if err := a.docker.Do(ctx, "GET", "/containers/json?all=1&filters="+url.QueryEscape(string(filters)), nil, &containers); err != nil {
return err
}
rows, err := a.s.DB.QueryContext(ctx, "SELECT id,uid,container_id,status FROM instances")
if err != nil {
return err
}
type record struct{ id, uid, container, status string }
var records []record
for rows.Next() {
var r record
if err = rows.Scan(&r.id, &r.uid, &r.container, &r.status); err != nil {
break
}
records = append(records, r)
}
if err == nil {
err = rows.Err()
}
rows.Close()
if err != nil {
return err
}
known := make(map[string]record)
for _, r := range records {
known[r.id] = r
}
matched := make(map[string]bool)
panel := os.Getenv("PANEL_CONTAINER_ID")
var networkErrors []error
for _, c := range containers {
id := c.Labels["dsh-mgr.instance"]
r, ok := known[id]
if !ok || r.container != c.ID || r.uid != c.Labels["dsh-mgr.owner"] {
a.s.AddAudit("", "reconcile.orphan", c.ID, "container identity requires administrator review")
continue
}
matched[id] = true
if panel != "" {
if networkErr := a.reconcilePanelNetwork(ctx, id, panel); networkErr != nil {
if _, err = a.s.DB.ExecContext(ctx, "UPDATE instances SET status='error' WHERE id=?", id); err != nil {
return err
}
a.s.AddAudit(r.uid, "reconcile.network_error", id, networkErr.Error())
networkErrors = append(networkErrors, fmt.Errorf("instance %s: %w", id, networkErr))
continue
}
}
if r.status != "error" {
if _, err = a.s.DB.ExecContext(ctx, "UPDATE instances SET status=? WHERE id=?", c.State, id); err != nil {
return err
}
}
}
for _, r := range records {
if r.container == "" || matched[r.id] || r.status == "error" {
continue
}
if _, err = a.s.DB.ExecContext(ctx, "UPDATE instances SET status='error' WHERE id=?", r.id); err != nil {
return err
}
a.s.AddAudit(r.uid, "reconcile.missing", r.id, "recorded container is absent or identity mismatch")
}
if ctx.Err() != nil {
return fmt.Errorf("reconcile: %w", ctx.Err())
}
return errors.Join(networkErrors...)
}
func (a *API) reconcilePanelNetwork(ctx context.Context, id, panel string) error {
name := runtimeNetwork(id)
var network struct {
ID, Name, Driver string
Labels map[string]string
}
if err := a.docker.Do(ctx, "GET", "/networks/"+url.PathEscape(name), nil, &network); err != nil {
return fmt.Errorf("inspect instance network: %w", err)
}
if network.ID == "" || network.Name != name || network.Driver != "bridge" || network.Labels["dsh-mgr.instance"] != id {
return fmt.Errorf("instance network identity mismatch")
}
container, err := a.docker.Inspect(ctx, panel)
if err != nil {
return fmt.Errorf("inspect panel: %w", err)
}
if container.ID == "" {
return fmt.Errorf("panel container identity missing")
}
for _, endpoint := range container.NetworkSettings.Networks {
if endpoint.NetworkID == network.ID {
return nil
}
}
if err := a.docker.Do(ctx, "POST", "/networks/"+url.PathEscape(network.ID)+"/connect", map[string]string{"Container": container.ID}, nil); err != nil {
// Another reconciliation may have connected the panel after inspection.
if current, inspectErr := a.docker.Inspect(ctx, container.ID); inspectErr == nil && current.ID == container.ID {
for _, endpoint := range current.NetworkSettings.Networks {
if endpoint.NetworkID == network.ID {
return nil
}
}
}
return fmt.Errorf("connect panel network: %w", err)
}
return nil
}