Zwei Widgets: Zettel mit kleinem Markdown, Bild je Nutzer

Zettel: ein Textfeld, mehr nicht — InkNote gibt es schon. Gelesen wird
gesetzt (Ueberschriften, Haken, Aufzaehlungen, fett/kursiv/code, Strich),
geschrieben wird roh. Haken lassen sich ohne Bearbeiten setzen und
aendern die Quelle. Gespeichert wird 800 ms nach dem letzten Anschlag,
sofort beim Verlassen und beim Schliessen des Tabs (keepalive statt
sendBeacon: der kann nur POST und traegt keine Kopfzeilen). Grenze 4000
Zeichen, vom Server durchgesetzt.

Bild: genau eines je Nutzer, hochgeladen oder verlinkt. Die erste Datei,
die HoneyOS ablegt — in bilder/ neben honeyos.json, benannt nach einem
Fingerabdruck der Nutzerkennung. Ein neues Bild ersetzt das alte samt
Datei, die Adresse traegt einen Versionsanteil gegen den Zwischen-
speicher. SVG kommt nicht herein (Text, kann Skripte tragen), und der
Typ wird an den Bytes erkannt, nicht an der Angabe des Browsers.

Der Rahmen kann jetzt Widgets mit eigenem DOM: 'stabil' haelt den Inhalt,
'montiert' verdrahtet ihn. Ohne das haette das Anklopfen bei den Apps
alle zwei Minuten den Text unter den Fingern ausgetauscht.

Nebenbei: Der Aufrufhelfer setzte bei jedem Schreibzugriff
Content-Type: application/json — auch fuer Bildbytes.
This commit is contained in:
marc committed 2026-08-31 00:44:20 +02:00
1 parent b7cc06b7c1
commit c0abfbb491
9 files changed
+857 -20

No files matched your search

+49
View File
@@ -8,6 +8,7 @@ import (
"strings"
"testing"
"honeyos/internal/auth"
"honeyos/internal/store"
"honeyos/internal/weather"
)
@@ -128,3 +129,51 @@ func TestHoneyBarOhneBacktickInKommentaren(t *testing.T) {
}
}
}
// Der Zettel ist ein Zettel, kein Dokument: Die Grenze muss der Server ziehen,
// nicht das Formular — sonst schreibt sie jeder Aufruf aus, der das Formular
// umgeht.
func TestZettelHatEineGrenze(t *testing.T) {
s := newTestServer(t, "")
h := s.routes()
put := func(text string) int {
body, _ := json.Marshal(map[string]string{"text": text})
req := httptest.NewRequest("PUT", "/api/note", strings.NewReader(string(body)))
rec := httptest.NewRecorder()
h.ServeHTTP(rec, req)
return rec.Code
}
if code := put("# Einkauf\n- [ ] Milch"); code != http.StatusNoContent {
t.Fatalf("kurzer Zettel: %d, erwartet 204", code)
}
if code := put(strings.Repeat("x", 4001)); code != http.StatusBadRequest {
t.Errorf("zu langer Zettel: %d, erwartet 400", code)
}
rec := httptest.NewRecorder()
h.ServeHTTP(rec, httptest.NewRequest("GET", "/api/note", nil))
var out map[string]string
json.Unmarshal(rec.Body.Bytes(), &out)
if out["text"] != "# Einkauf\n- [ ] Milch" {
t.Errorf("zurück kam %q — der zu lange Zettel darf den guten nicht überschrieben haben", out["text"])
}
}
// Fremde Zettel gehen niemanden etwas an: Der Schlüssel für die App-Liste darf
// sie nicht öffnen, auch nicht lesend.
func TestZettelNichtUeberDenSchluessel(t *testing.T) {
idp := newFakeIdP(t, "honeyos")
s := newTestServer(t, idp.URL)
s.cfg.AppsKey = "geheim123"
s.auth = auth.New(s.cfg)
req := httptest.NewRequest("GET", "/api/note", nil)
req.Header.Set("X-HoneyOS-Key", "geheim123")
rec := httptest.NewRecorder()
s.routes().ServeHTTP(rec, req)
if rec.Code != http.StatusUnauthorized {
t.Fatalf("mit Schlüssel: %d, erwartet 401", rec.Code)
}
}