feat!: Token-Schluessel und Wer-bin-ich entfernt, Version 1.0
Beides brachte allein einen Anzeigenamen. Seit der Benutzer als Widget auf dem Schreibtisch steht, kommt der aus der HoneyOS-Anmeldung — keine App muss mehr etwas dafuer bereitstellen, und beide Leisten tragen ueberall dasselbe. Der Token-Schluessel hatte zudem eine scharfe Kante: Zeigte das Feld Wer-bin-ich auf eine fremde Adresse, ging der Ausweis der App dorthin. Weg sind damit auch checkAuth, askMe und die Anmeldezeile auf den Kacheln. Alte honeyos.json-Dateien werden weiter gelesen; die Felder verfallen beim naechsten Speichern still. 1.0, weil die Bedienung sich aendert und der Stand rund ist: zwei Leisten mit gleichem Inhalt, Widgets fuer Benutzer und Wetter, die HoneyBar auch in fremden Apps. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b999a66fed
commit
fc4dd12c5e
7 files changed
+22
-168
No files matched your search
@@ -43,7 +43,6 @@ const state = {
|
||||
oidc: false,
|
||||
settings: { ...DEFAULT_SETTINGS },
|
||||
probes: {}, // appId -> "up" | "down" | "unknown"
|
||||
auth: {}, // appId -> {status: "in"|"out"|"blind", name}
|
||||
wins: {}, // id -> {el, task, min}
|
||||
zTop: 10,
|
||||
};
|
||||
@@ -650,7 +649,6 @@ async function probeApps() {
|
||||
);
|
||||
paintProbes();
|
||||
renderStats();
|
||||
checkAuth();
|
||||
}
|
||||
|
||||
async function probe(url) {
|
||||
@@ -666,91 +664,11 @@ async function probe(url) {
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Anmeldestand ──────────────────────────────────────────────────────────
|
||||
* Wenn eine App einen "me"-Endpunkt in der Konfiguration hat, fragen wir ihn
|
||||
* mit den Keksen des Browsers. Das funktioniert nur, weil alle Adressen unter
|
||||
* derselben Domain liegen — sonst schickt der Browser die Sitzung nicht mit.
|
||||
*
|
||||
* Antwortet die App nicht oder verbietet CORS, ist der Stand "blind": dann
|
||||
* zeigt HoneyOS eben nur die Lampe wie bisher. Kein Fehler, keine Meldung —
|
||||
* die Anzeige ist eine Zugabe, nichts, worauf sich jemand verlassen muss.
|
||||
*/
|
||||
async function checkAuth() {
|
||||
await Promise.all(
|
||||
state.cfg.apps
|
||||
.filter((a) => a.me)
|
||||
.map(async (app) => {
|
||||
state.auth[app.id] = await askMe(app);
|
||||
})
|
||||
);
|
||||
paintProbes();
|
||||
applyAuthName();
|
||||
}
|
||||
|
||||
async function askMe(app) {
|
||||
const ctl = new AbortController();
|
||||
const t = setTimeout(() => ctl.abort(), 5000);
|
||||
try {
|
||||
const headers = {};
|
||||
// Apps mit Bearer-Token statt Cookie: der Schlüssel steht in der
|
||||
// Konfiguration, der Wert im localStorage — und der ist pro Domain.
|
||||
// Von hier aus lesbar ist er nur, wenn HoneyOS dieselbe Domain ist.
|
||||
if (app.tokenKey) {
|
||||
try {
|
||||
const tok = localStorage.getItem(app.tokenKey);
|
||||
if (tok) headers.Authorization = `Bearer ${tok}`;
|
||||
} catch {
|
||||
/* kein Speicherzugriff — dann ohne */
|
||||
}
|
||||
}
|
||||
const res = await fetch(resolveUrl(app, app.me), {
|
||||
credentials: "include",
|
||||
cache: "no-store",
|
||||
headers,
|
||||
signal: ctl.signal,
|
||||
});
|
||||
if (res.status === 401 || res.status === 403) return { status: "out" };
|
||||
if (!res.ok) return { status: "blind" };
|
||||
return { status: "in", name: extractName(await res.json()) };
|
||||
} catch {
|
||||
return { status: "blind" };
|
||||
} finally {
|
||||
clearTimeout(t);
|
||||
}
|
||||
}
|
||||
|
||||
/** Relative Pfade beziehen sich auf die App-Adresse, absolute gelten wie sie sind. */
|
||||
function resolveUrl(app, path) {
|
||||
if (!path) return app.url;
|
||||
if (/^https?:\/\//i.test(path)) return path;
|
||||
return app.url.replace(/\/+$/, "") + (path.startsWith("/") ? path : "/" + path);
|
||||
}
|
||||
|
||||
/** Jede App antwortet anders — hier wird der Anzeigename herausgefischt. */
|
||||
function extractName(data) {
|
||||
const u = (data && (data.user || data.profile)) || data || {};
|
||||
return u.display_name || u.displayName || u.name || u.username || u.preferred_username || "";
|
||||
}
|
||||
|
||||
/** Nennt eine App einen Namen, gilt der — er kommt vom Anbieter, die
|
||||
* Einstellung ist nur der Platzhalter, solange niemand antwortet. */
|
||||
function applyAuthName() {
|
||||
const named = Object.values(state.auth).find((a) => a.status === "in" && a.name);
|
||||
if (named) {
|
||||
state.cfg.user = named.name;
|
||||
renderUser();
|
||||
renderStats();
|
||||
}
|
||||
}
|
||||
|
||||
const STATE_TEXT = { up: "online", down: "keine antwort", unknown: "prüfe …" };
|
||||
const AUTH_TEXT = { in: "angemeldet", out: "nicht angemeldet" };
|
||||
|
||||
function stateLine(appId) {
|
||||
const s = state.probes[appId] || "unknown";
|
||||
const a = state.auth[appId];
|
||||
const extra = a && AUTH_TEXT[a.status] ? ` · ${a.status === "in" && a.name ? a.name : AUTH_TEXT[a.status]}` : "";
|
||||
return { s, text: STATE_TEXT[s] + (s === "up" ? extra : "") };
|
||||
return { s, text: STATE_TEXT[s] };
|
||||
}
|
||||
|
||||
function paintProbes() {
|
||||
@@ -1211,12 +1129,6 @@ function openApps() {
|
||||
</select></label>
|
||||
<label class="ae-field"><span>Taste</span>
|
||||
<input data-i="${i}" data-f="key" value="${escapeAttr(a.key || "")}" maxlength="1" placeholder="1" /></label>
|
||||
<label class="ae-field"><span>Wer-bin-ich</span>
|
||||
<input data-i="${i}" data-f="me" value="${escapeAttr(a.me || "")}" placeholder="/api/v1/auth/me" spellcheck="false" /></label>
|
||||
<p class="ae-hint">optional — zeigt an, ob du dort angemeldet bist</p>
|
||||
<label class="ae-field"><span>Token-Schlüssel</span>
|
||||
<input data-i="${i}" data-f="tokenKey" value="${escapeAttr(a.tokenKey || "")}" placeholder="leer lassen" spellcheck="false" /></label>
|
||||
<p class="ae-hint">nur für Apps mit Bearer-Token im localStorage statt Cookie</p>
|
||||
<div class="ae-actions">
|
||||
<button class="btn btn-danger" data-del="${i}">löschen</button>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user