feat: redesign responsive console and fix restore regressions

This commit is contained in:
2026-10-15 01:08:55 +08:00
parent 3bf197206a
commit 56c8f76e32
49 changed files with 3455 additions and 447 deletions
+3
View File
@@ -205,6 +205,9 @@ func (a *API) config(w http.ResponseWriter, r *http.Request, i store.Instance, a
return
}
var document any
if strings.TrimSpace(in.Content) == "" {
in.Content = "[]\n"
}
if yaml.Unmarshal([]byte(in.Content), &document) != nil {
fail(w, 400, "invalid YAML")
return
+6 -1
View File
@@ -3,7 +3,9 @@ package api
import (
"context"
"errors"
"git.misaka.ren/m1saka/dsh-web/internal/docker"
"git.misaka.ren/m1saka/dsh-web/internal/store"
"net/http"
"net/url"
"os"
"path/filepath"
@@ -57,7 +59,10 @@ func (a *API) configStop(ctx context.Context, id string) error {
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
if err := a.docker.Do(ctx, "POST", "/containers/"+url.PathEscape(id)+"/stop?t=10", nil, nil); err != nil {
return err
var apiErr *docker.APIError
if !errors.As(err, &apiErr) || apiErr.StatusCode != http.StatusNotModified {
return err
}
}
c, err := a.docker.Inspect(ctx, id)
if err != nil {
+42
View File
@@ -5,6 +5,7 @@ import (
"context"
"encoding/json"
"fmt"
"git.misaka.ren/m1saka/dsh-web/internal/docker"
"git.misaka.ren/m1saka/dsh-web/internal/store"
"golang.org/x/crypto/bcrypt"
"mime/multipart"
@@ -20,6 +21,47 @@ import (
"time"
)
func TestConfigStopAlreadyStopped(t *testing.T) {
for _, state := range []string{`{}`, `{"Running":true}`, `{"Paused":true}`, `{"Restarting":true}`} {
t.Run(state, func(t *testing.T) {
inspected := false
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
w.WriteHeader(http.StatusNotModified)
return
}
inspected = true
fmt.Fprintf(w, `{"State":%s}`, state)
}))
defer srv.Close()
a := &API{docker: docker.New(srv.URL)}
err := a.configStop(context.Background(), "test")
if !inspected || (err == nil) != (state == `{}`) {
t.Fatalf("inspected=%v error=%v", inspected, err)
}
})
}
}
func TestConfigEmptyProfileRoundTrip(t *testing.T) {
if os.Geteuid() != 0 {
t.Skip("requires chown")
}
a, i, home := configTestAPI(t, false)
w := configCall(a, i, "GET", "profile", "")
if w.Code != 200 {
t.Fatal(w.Code, w.Body.String())
}
w = configCall(a, i, "PUT", "profile", w.Body.String())
if w.Code != 200 {
t.Fatal(w.Code, w.Body.String())
}
content, err := os.ReadFile(filepath.Join(home, "profiles/web/cordis.patch.yml"))
if err != nil || string(content) != "[]\n" {
t.Fatalf("content=%q error=%v", content, err)
}
}
func TestConfigPasswordBackupImport(t *testing.T) {
if os.Geteuid() != 0 {
t.Skip("requires chown")