R-516: the formal forms in Go literals move into the bundle in the te-form
gates / gates (push) Successful in 50s
gates / gates (push) Successful in 50s
22 Go-literal messages (escrow handler, share password page, export upload, network-storage uid/remove/attach failures) are bundle keys with te-form Hungarian and English; a detached attach failure renders in the reader's language. The NAS refusal says „Válassz". Setup wizard, recovery-info.txt (R-554) and the SMART/fill-watch wire texts are left. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -181,13 +181,13 @@ func (s *Server) escrowStartAPIHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// this state; a legacy-open box must claim/set a password first).
|
||||
hash := s.effectivePasswordHash()
|
||||
if hash == "" {
|
||||
escrowJSON(w, http.StatusForbidden, nil, "A vezérlőpult jelszava nincs beállítva — előbb állítson be jelszót.")
|
||||
escrowJSON(w, http.StatusForbidden, nil, s.msg(r, "api.escrow.dashboard_password_missing"))
|
||||
return
|
||||
}
|
||||
ip := rateKey(r)
|
||||
if s.escrowRateLimited(ip) {
|
||||
s.logger.Printf("[WARN] [web] escrow start rate limited for %s", ip)
|
||||
escrowJSON(w, http.StatusTooManyRequests, nil, "Túl sok sikertelen próbálkozás, próbálja újra 1 perc múlva.")
|
||||
escrowJSON(w, http.StatusTooManyRequests, nil, s.msg(r, "api.escrow.too_many_attempts"))
|
||||
return
|
||||
}
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(r.FormValue("password"))); err != nil {
|
||||
@@ -212,7 +212,7 @@ func (s *Server) escrowStartAPIHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if s.backupMgr != nil && s.backupMgr.OffboxConfigured() {
|
||||
if err := s.escrowStage(r.Context()); err != nil {
|
||||
s.logger.Printf("[WARN] [web] escrow start: re-stage failed (ceremony NOT started): %v", err) // err carries no secret
|
||||
escrowJSON(w, http.StatusBadGateway, nil, "A távoli mentés jelszavának letéti előkészítése nem sikerült — a folyamat nem indult el. Próbálja újra.")
|
||||
escrowJSON(w, http.StatusBadGateway, nil, s.msg(r, "api.escrow.stage_failed"))
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -232,7 +232,7 @@ func (s *Server) escrowStartAPIHandler(w http.ResponseWriter, r *http.Request) {
|
||||
resp, status, err := agent.EscrowCeremonyStart(r.Context())
|
||||
if err != nil {
|
||||
if status == http.StatusConflict {
|
||||
escrowJSON(w, http.StatusConflict, nil, "Egy kódkészítés már folyamatban van — várja meg, míg befejeződik.")
|
||||
escrowJSON(w, http.StatusConflict, nil, s.msg(r, "api.escrow.already_running"))
|
||||
return
|
||||
}
|
||||
s.logger.Printf("[WARN] [web] escrow start: agent trigger: %v", err)
|
||||
@@ -277,11 +277,11 @@ func (s *Server) escrowClaimAPIHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if err != nil {
|
||||
switch status {
|
||||
case http.StatusGone: // 410 — the code was minted but never shown; permanently void.
|
||||
escrowJSON(w, http.StatusGone, nil, "A kód létrejött, de nem lett megjelenítve — biztonsági okból újra nem kérhető le. Indítsa újra a folyamatot: az új kód a régit érvényteleníti.")
|
||||
escrowJSON(w, http.StatusGone, nil, s.msg(r, "api.escrow.code_not_shown"))
|
||||
case http.StatusNotFound: // 404 — no ceremony has run (e.g. phase:none post-reboot). F-C:
|
||||
// this used to fall through to a 502; a bad-gateway class code for "nothing to claim"
|
||||
// is wrong. Relay a clean, honest 4xx.
|
||||
escrowJSON(w, http.StatusNotFound, nil, "Nincs aktív helyreállítási folyamat — előbb indítsa el a kódkészítést.")
|
||||
escrowJSON(w, http.StatusNotFound, nil, s.msg(r, "api.escrow.no_active_job"))
|
||||
case http.StatusConflict: // 409 — the ceremony state doesn't allow a claim right now. F-C.
|
||||
escrowJSON(w, http.StatusConflict, nil, "A folyamat jelenlegi állapotában a kód nem kérhető le.")
|
||||
default: // status 0 (agent unreachable / transport error) or a genuine agent 5xx — a real
|
||||
|
||||
@@ -319,7 +319,7 @@ func (s *Server) apiUploadFinalize(w http.ResponseWriter, r *http.Request) {
|
||||
logx.Warnf(s.logger, "[web] fab upload finalize size mismatch: got %d, declared %d — part deleted",
|
||||
job.received, job.declared)
|
||||
uploadJSON(w, http.StatusUnprocessableEntity, map[string]interface{}{
|
||||
"ok": false, "error": "A feltöltött méret nem egyezik — próbálja újra.",
|
||||
"ok": false, "error": s.msg(r, "api.export.upload_size_mismatch"),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package web
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -35,8 +34,8 @@ const (
|
||||
// validMappedID reports whether id is a valid container uid/gid (F4 range check).
|
||||
func validMappedID(id int) bool { return id >= mappedIDMin && id <= mappedIDMax }
|
||||
|
||||
// netAddUIDRangeMsg is the friendly F4 refusal for an out-of-range mapped uid/gid.
|
||||
const netAddUIDRangeMsg = "Az alkalmazás felhasználói azonosítója (uid) érvénytelen. Adjon meg 1 és 65533 közötti értéket — a legtöbb médiaalkalmazás az 1000-est használja."
|
||||
// netAddUIDRangeKey is the friendly F4 refusal for an out-of-range mapped uid/gid (bundle key, R-516).
|
||||
const netAddUIDRangeKey = "api.netstorage.uid_range"
|
||||
|
||||
// netAddOutdatedMsg is the sync add-time refusal (machine code "agent_outdated") when the agent
|
||||
// predates the coupled verify-before-commit add semantics (pre-v0.81.0).
|
||||
@@ -130,7 +129,7 @@ func (s *Server) handleNetStorageAdd(w http.ResponseWriter, r *http.Request) {
|
||||
// here with a friendly Hungarian 400 — nothing is installed.
|
||||
if !validMappedID(uid) || !validMappedID(gid) {
|
||||
logx.Debugf(s.logger, "[web] netstorage add %q refused by validation: uid/gid out of range (uid=%d gid=%d)", name, uid, gid)
|
||||
writeDiskJSON(w, http.StatusBadRequest, false, netAddUIDRangeMsg, nil)
|
||||
writeDiskJSON(w, http.StatusBadRequest, false, s.msg(r, netAddUIDRangeKey), nil)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -179,7 +178,7 @@ func (s *Server) handleNetStorageAddStatus(w http.ResponseWriter, r *http.Reques
|
||||
}
|
||||
writeDiskJSON(w, http.StatusOK, true, "", map[string]any{
|
||||
"phase": job.Phase, "name": job.Name, "category": job.Category,
|
||||
"message": job.Message, "detail": trimNetDetail(job.Detail), "warn": job.Warn,
|
||||
"message": s.netAddJobMessage(r, job), "detail": trimNetDetail(job.Detail), "warn": job.Warn,
|
||||
"path": job.Path, "started_at": job.StartedAt, "updated_at": job.UpdatedAt,
|
||||
})
|
||||
}
|
||||
@@ -306,9 +305,7 @@ func (s *Server) handleNetStorageRemove(w http.ResponseWriter, r *http.Request)
|
||||
// files deleted anyway → an unreapable autofs mount until host reboot — the C6B-F2 orphan).
|
||||
if apps := s.deployedAppsOnPath(where); len(apps) > 0 {
|
||||
s.logger.Printf("[WARN] [web] netstorage remove %q refused: deployed app(s) on the share: %s", name, strings.Join(apps, ", "))
|
||||
writeDiskJSON(w, http.StatusConflict, false, fmt.Sprintf(
|
||||
"A tároló nem távolítható el, amíg alkalmazás használja: %s. Előbb távolítsa el vagy költöztesse át az alkalmazást.",
|
||||
strings.Join(apps, ", ")), nil)
|
||||
writeDiskJSON(w, http.StatusConflict, false, s.msg(r, "api.netstorage.remove_in_use", strings.Join(apps, ", ")), nil)
|
||||
return
|
||||
}
|
||||
agent, err := s.netAgentForAdd()
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -41,13 +42,17 @@ type netAgent interface {
|
||||
|
||||
// netAddJob is the poll-visible orchestration state (GET /api/storage/netstorage/add/status).
|
||||
type netAddJob struct {
|
||||
Name string `json:"name"`
|
||||
Phase string `json:"phase"` // agent_add | verifying | probing | registering | done | failed
|
||||
Category string `json:"category,omitempty"` // failure category (agent classifier vocabulary + not_writable/probe_io)
|
||||
Message string `json:"message,omitempty"` // the category's Hungarian customer message (§3.2)
|
||||
Detail string `json:"detail,omitempty"` // raw English/journal detail (the collapsible)
|
||||
Warn string `json:"warn,omitempty"` // non-fatal note on a successful add (e.g. probe cleanup)
|
||||
Path string `json:"path,omitempty"` // the registered in-guest path (done only)
|
||||
Name string `json:"name"`
|
||||
Phase string `json:"phase"` // agent_add | verifying | probing | registering | done | failed
|
||||
Category string `json:"category,omitempty"` // failure category (agent classifier vocabulary + not_writable/probe_io)
|
||||
Message string `json:"message,omitempty"` // the category's Hungarian customer message (§3.2)
|
||||
Detail string `json:"detail,omitempty"` // raw English/journal detail (the collapsible)
|
||||
Warn string `json:"warn,omitempty"` // non-fatal note on a successful add (e.g. probe cleanup)
|
||||
Path string `json:"path,omitempty"` // the registered in-guest path (done only)
|
||||
// server and mappedUID (R-516) parameterize the failure message, which the status handler renders
|
||||
// in the reader's language.
|
||||
server string
|
||||
mappedUID int
|
||||
StartedAt time.Time `json:"started_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
@@ -183,7 +188,8 @@ func (s *Server) runNetAdd(agent netAgent, req agentapi.AddNetStorageRequest, la
|
||||
fail := func(category, detail string) {
|
||||
job.Phase = netAddPhaseFailed
|
||||
job.Category = category
|
||||
job.Message = netAddMessage(category, req.Server, req.MappedUID)
|
||||
job.server, job.mappedUID = req.Server, req.MappedUID
|
||||
job.Message = s.netAddMessage(s.boxLang(), category, req.Server, req.MappedUID)
|
||||
job.Detail = detail
|
||||
job.UpdatedAt = time.Now().UTC()
|
||||
s.netAdd.set(job)
|
||||
@@ -321,41 +327,40 @@ func (s *Server) pollAgentVerify(ctx context.Context, agent netAgent, jobID stri
|
||||
}
|
||||
}
|
||||
|
||||
// netAddMessage maps a failure category to the EXACT Hungarian customer message (§3.2). server and
|
||||
// mappedUID parameterize the unreachable/not_writable texts (host id = mapped uid + 100000).
|
||||
func netAddMessage(category, server string, mappedUID int) string {
|
||||
// netAddMessage maps a failure category to the customer message (§3.2) in lang. server and mappedUID
|
||||
// parameterize the unreachable/not_writable texts (host id = mapped uid + 100000). R-516: the texts
|
||||
// live in the bundle (api.netstorage.add.*), in the te-form, in both languages.
|
||||
func (s *Server) netAddMessage(lang, category, server string, mappedUID int) string {
|
||||
switch category {
|
||||
case "unreachable":
|
||||
msg := "A szerver nem érhető el (" + server + "). Ellenőrizze az IP-címet, és hogy a NAS be van-e kapcsolva."
|
||||
msg := s.msgLang(lang, "api.netstorage.add.unreachable", server)
|
||||
// R-66 Leg C: a single-label non-IP server (»FELHOM«) is almost always a Windows/NetBIOS
|
||||
// network name, which this box generally cannot resolve — name the trap instead of letting
|
||||
// the generic text teach nothing. Purely lexical on the submitted value: NO NetBIOS/mDNS
|
||||
// resolution is ever attempted, and an IP or dotted DNS name never gets nagged about this.
|
||||
if looksLikeFlatNetworkName(server) {
|
||||
msg += " Tipp: a(z) »" + server + "« Windows-hálózati névnek tűnik — használja az eszköz IP-címét."
|
||||
msg += s.msgLang(lang, "api.netstorage.add.flat_name_tip", server)
|
||||
}
|
||||
return msg
|
||||
case "nfs_export":
|
||||
return "A megosztás nem található, vagy a NAS nem engedélyezi ennek a gépnek a hozzáférését. Ellenőrizze az export útvonalát, és hogy a NAS engedélyezi-e a Felhom gép IP-címét."
|
||||
case "smb_auth":
|
||||
return "Hibás SMB felhasználónév vagy jelszó."
|
||||
case "smb_share":
|
||||
return "A megadott SMB-megosztás nem található a szerveren. Ellenőrizze a megosztás nevét."
|
||||
case "timeout":
|
||||
return "Időtúllépés a csatolás közben — a szerver elérhető a hálózaton, de a megosztás nem csatolható. Ellenőrizze a NAS NFS/SMB szolgáltatását."
|
||||
case "nfs_export", "smb_auth", "smb_share", "timeout", "not_network_fs", "probe_io", "busy":
|
||||
return s.msgLang(lang, "api.netstorage.add."+category)
|
||||
case "not_writable":
|
||||
return "A megosztás csatolható, de az alkalmazások nem tudnak rá írni. NFS esetén kapcsolja be a NAS-on a „minden felhasználó leképezése” (map all users / all squash) beállítást a megosztáson — vagy állítsa a fájlok tulajdonosát a(z) " + fmt.Sprint(mappedUID+100000) + " azonosítóra. SMB esetén ellenőrizze, hogy a felhasználónak írási joga van a megosztáson."
|
||||
case "not_network_fs":
|
||||
return "A hálózati tárhely csatolása a rendszeren belül nem jött létre megfelelően. Próbálja újra a csatlakoztatást; ha a hiba ismétlődik, jelezze az üzemeltetőnek."
|
||||
case "probe_io":
|
||||
return "Írási hiba a megosztáson (az adat nem olvasható vissza hibátlanul). Ellenőrizze a megosztást és a hálózatot."
|
||||
case "busy":
|
||||
return "Már folyamatban van egy csatlakoztatás. Várja meg, amíg befejeződik."
|
||||
return s.msgLang(lang, "api.netstorage.add.not_writable", mappedUID+100000)
|
||||
default: // mount_failed + agent_error + register_failed + any future agent code
|
||||
return "A csatolás sikertelen. Részletek alább."
|
||||
return s.msgLang(lang, "api.netstorage.add.failed")
|
||||
}
|
||||
}
|
||||
|
||||
// netAddJobMessage renders a failed job's message in the READER's language (the job ran detached,
|
||||
// with no request; it keeps the category and its parameters). A job with no category passes its own
|
||||
// Message through.
|
||||
func (s *Server) netAddJobMessage(r *http.Request, job *netAddJob) string {
|
||||
if job.Category == "" {
|
||||
return job.Message
|
||||
}
|
||||
return s.netAddMessage(s.langFor(r), job.Category, job.server, job.mappedUID)
|
||||
}
|
||||
|
||||
// looksLikeFlatNetworkName reports whether a submitted server value is a single-label non-IP name
|
||||
// (no dot, not an IP literal) — the NetBIOS-name shape. `nas.local` (dotted) and any parseable IP
|
||||
// (v4 or v6 — colons carry the v6 case through ParseIP) are NOT flagged: the hint must never nag
|
||||
|
||||
@@ -176,7 +176,7 @@ func TestNetAdd_AgentVerifyFailed_MappedMessage(t *testing.T) {
|
||||
t.Fatalf("phase/category = %s/%s, want failed/nfs_export", job.Phase, job.Category)
|
||||
}
|
||||
// The EXACT §3.2 merged message (NFSv4 cannot distinguish not-found from not-permitted).
|
||||
want := "A megosztás nem található, vagy a NAS nem engedélyezi ennek a gépnek a hozzáférését. Ellenőrizze az export útvonalát, és hogy a NAS engedélyezi-e a Felhom gép IP-címét."
|
||||
want := "A megosztás nem található, vagy a NAS nem engedélyezi ennek a gépnek a hozzáférését. Ellenőrizd az export útvonalát, és hogy a NAS engedélyezi-e a Felhom gép IP-címét."
|
||||
if job.Message != want {
|
||||
t.Errorf("nfs_export message:\n got %q\nwant %q", job.Message, want)
|
||||
}
|
||||
|
||||
@@ -152,6 +152,8 @@ func TestDebugDumpNetworkSection(t *testing.T) {
|
||||
|
||||
// R-66 Leg C — the NetBIOS trap named on an unreachable-class add failure.
|
||||
func TestNetAddMessageNetBIOSHint(t *testing.T) {
|
||||
s := testServer(t)
|
||||
netAddMessage := func(c, srv string, uid int) string { return s.netAddMessage("hu", c, srv, uid) }
|
||||
const hintMark = "Windows-hálózati névnek tűnik"
|
||||
|
||||
// C1: single-label non-IP name + unreachable → hint present, naming the submitted value.
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/i18n"
|
||||
)
|
||||
|
||||
var goStringLit = regexp.MustCompile(`"(?:[^"\\]|\\.)*"`)
|
||||
|
||||
// R-516 (2026-10-06 night) — the formal („ön") forms that lived in GO LITERALS, where the bundle gate
|
||||
// cannot read them: the escrow and share handlers, the export upload, the network-storage attach and
|
||||
// remove errors, the uid refusal. They are bundle keys now, te-form in Hungarian, with English.
|
||||
//
|
||||
// Left on purpose (named so the scan's exemptions are not a wish): internal/setup and
|
||||
// internal/recovery (the setup wizard and the household's recovery-info.txt — R-554, the operator's
|
||||
// call); internal/notify and internal/fillwatch (the SMART and fill-watch texts are the hub event's
|
||||
// MESSAGE — wire text the hub mails as is; converting them needs the household-copy producer shape).
|
||||
//
|
||||
// COMPANION RED-PROOF (observed): put back „próbálja újra" in share_handlers.go → the source scan fails
|
||||
// naming the file and the stem. Restored.
|
||||
func TestR516_GoLiteralFormalFormsAreGone(t *testing.T) {
|
||||
b, err := i18n.Shared()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := map[string]string{
|
||||
"api.escrow.too_many_attempts": "Túl sok sikertelen próbálkozás, próbáld újra 1 perc múlva.",
|
||||
"api.escrow.no_active_job": "Nincs aktív helyreállítási folyamat — előbb indítsd el a kódkészítést.",
|
||||
"api.share.form_invalid": "Érvénytelen űrlap — töltsd újra az oldalt.",
|
||||
"api.export.upload_size_mismatch": "A feltöltött méret nem egyezik — próbáld újra.",
|
||||
"api.netstorage.add.smb_share": "A megadott SMB-megosztás nem található a szerveren. Ellenőrizd a megosztás nevét.",
|
||||
"api.netstorage.add.busy": "Már folyamatban van egy csatlakoztatás. Várd meg, amíg befejeződik.",
|
||||
"api.netstorage.uid_range": "Az alkalmazás felhasználói azonosítója (uid) érvénytelen. Adj meg 1 és 65533 közötti értéket — a legtöbb médiaalkalmazás az 1000-est használja.",
|
||||
"api.netstorage.add.not_network_fs": "A hálózati tárhely csatolása a rendszeren belül nem jött létre megfelelően. Próbáld újra a csatlakoztatást; ha a hiba ismétlődik, jelezd az üzemeltetőnek.",
|
||||
}
|
||||
for key, w := range want {
|
||||
if got, fellBack, ok := b.Text("hu", key); !ok || fellBack || got != w {
|
||||
t.Errorf("hu %s: %q, want %q", key, got, w)
|
||||
}
|
||||
if _, fellBack, ok := b.Text("en", key); !ok || fellBack {
|
||||
t.Errorf("en %s: missing (fell back to Hungarian)", key)
|
||||
}
|
||||
}
|
||||
|
||||
// Source scan over the Go literals this row converted. ASCII-folded, so an accent cannot hide one.
|
||||
fold := strings.NewReplacer("á", "a", "é", "e", "í", "i", "ó", "o", "ö", "o", "ő", "o", "ú", "u", "ü", "u", "ű", "u")
|
||||
stems := []string{"probalja", "toltse ujra", "allitson be", "varja meg", "inditsa", "ellenorizze",
|
||||
"hasznalja az eszkoz", "kapcsolja be", "allitsa a", "jelezze az", "tavolitsa el vagy", "adjon meg", "valasszon csatlakoztatott"}
|
||||
skip := map[string]bool{"setup": true, "recovery": true, "notify": true, "fillwatch": true}
|
||||
root := filepath.Join("..")
|
||||
scanned := 0
|
||||
err = filepath.Walk(root, func(p string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
if skip[info.Name()] || info.Name() == "testdata" {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if !strings.HasSuffix(p, ".go") || strings.HasSuffix(p, "_test.go") {
|
||||
return nil
|
||||
}
|
||||
data, rerr := os.ReadFile(p)
|
||||
if rerr != nil {
|
||||
return rerr
|
||||
}
|
||||
scanned++
|
||||
for i, line := range strings.Split(string(data), "\n") {
|
||||
tl := strings.TrimSpace(line)
|
||||
if strings.HasPrefix(tl, "//") || !strings.Contains(tl, `"`) {
|
||||
continue
|
||||
}
|
||||
// Only literals with a space are sentences; a bundle KEY (no space) may carry a stem in its name.
|
||||
var sentences []string
|
||||
for _, m := range goStringLit.FindAllString(tl, -1) {
|
||||
if strings.Contains(m, " ") {
|
||||
sentences = append(sentences, m)
|
||||
}
|
||||
}
|
||||
lv := fold.Replace(strings.ToLower(strings.Join(sentences, " ")))
|
||||
for _, st := range stems {
|
||||
if strings.Contains(lv, st) {
|
||||
t.Errorf("%s:%d still carries the formal %q: %s", p, i+1, st, tl)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if scanned < 50 {
|
||||
t.Fatalf("the scan read only %d Go files — it judged nothing", scanned)
|
||||
}
|
||||
// Positive control: the fold turns a formal literal into a matching stem.
|
||||
if lv := fold.Replace(strings.ToLower(`"Próbálja újra."`)); !strings.Contains(lv, "probalja") {
|
||||
t.Fatal("the ASCII fold does not work — the scan proves nothing")
|
||||
}
|
||||
}
|
||||
|
||||
// A detached attach job's failure is rendered in the READER's language, not frozen in the language of
|
||||
// the moment it failed.
|
||||
//
|
||||
// COMPANION RED-PROOF (observed): make handleNetStorageAddStatus return job.Message again → the English
|
||||
// reader gets the Hungarian sentence and this fails. Restored.
|
||||
func TestR516_NetAddFailureFollowsTheReader(t *testing.T) {
|
||||
s := testServer(t)
|
||||
job := &netAddJob{Name: "media", Phase: netAddPhaseFailed, Category: "unreachable", server: "FELHOM", mappedUID: 1000}
|
||||
job.Message = s.netAddMessage("hu", job.Category, job.server, job.mappedUID)
|
||||
s.netAdd.set(job)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
s.handleNetStorageAddStatus(rr, httptest.NewRequest("GET", "/api/storage/network/add/status?lang=en", nil))
|
||||
body := rr.Body.String()
|
||||
if !strings.Contains(body, "The server cannot be reached (FELHOM)") || !strings.Contains(body, "looks like a Windows network name") {
|
||||
t.Fatalf("an English reader must get the English failure with its tip: %s", body)
|
||||
}
|
||||
rr = httptest.NewRecorder()
|
||||
s.handleNetStorageAddStatus(rr, httptest.NewRequest("GET", "/api/storage/network/add/status?lang=hu", nil))
|
||||
if !strings.Contains(rr.Body.String(), "Ellenőrizd az IP-címet") {
|
||||
t.Fatalf("a Hungarian reader must get the te-form sentence: %s", rr.Body.String())
|
||||
}
|
||||
}
|
||||
@@ -110,13 +110,13 @@ func (s *Server) shareGuestPasswordHandler(w http.ResponseWriter, r *http.Reques
|
||||
}
|
||||
_ = r.ParseForm()
|
||||
if !s.validShareCSRF(r) {
|
||||
s.renderSharePasswordPage(w, r, "Érvénytelen űrlap — töltse újra az oldalt.")
|
||||
s.renderSharePasswordPage(w, r, s.msg(r, "api.share.form_invalid"))
|
||||
return
|
||||
}
|
||||
ip := rateKey(r)
|
||||
if s.shareRateLimited(ip) {
|
||||
s.logger.Printf("[WARN] [web] share password rate limited for %s", ip)
|
||||
s.renderSharePasswordPage(w, r, "Túl sok sikertelen próbálkozás, próbálja újra 1 perc múlva")
|
||||
s.renderSharePasswordPage(w, r, s.msg(r, "api.share.too_many_attempts"))
|
||||
return
|
||||
}
|
||||
if bcrypt.CompareHashAndPassword([]byte(pwHash), []byte(r.FormValue("password"))) != nil {
|
||||
|
||||
Reference in New Issue
Block a user