generated from Dicken/dickendock
round32: Bestaetigen-Button fuer Scan-Ergebnisse, API-Scan auf wirklich nutzbare Endpunkte eingegrenzt, Firefox-Android-Hinweis mit Alternativen
This commit is contained in:
@@ -298,6 +298,14 @@ Danach: `lp <suchbegriff>` in die Adresszeile, Enter – landet bei eindeutigem
|
|||||||
Treffer direkt beim Dienst, sonst auf der LaunchPad-Startseite mit bereits
|
Treffer direkt beim Dienst, sonst auf der LaunchPad-Startseite mit bereits
|
||||||
eingetragenem Suchbegriff.
|
eingetragenem Suchbegriff.
|
||||||
|
|
||||||
|
**Android:** Firefox für Android unterstützt Schlagwort-Suchen (auch keine
|
||||||
|
synchronisierten Desktop-Kürzel) grundsätzlich nicht – eine bekannte
|
||||||
|
Firefox-Einschränkung, kein LaunchPad-Problem. Auf Android funktioniert die
|
||||||
|
Chrome-Anleitung oben unverändert. Alternative für jeden Browser: die
|
||||||
|
LaunchPad-Seite über „Zum Startbildschirm hinzufügen" als App-Symbol
|
||||||
|
ablegen (LaunchPad ist als PWA installierbar) – ein Fingertipp genügt dann
|
||||||
|
bis zum Suchfeld.
|
||||||
|
|
||||||
## Deployment auf unterschiedlichen Geräten
|
## Deployment auf unterschiedlichen Geräten
|
||||||
|
|
||||||
Läuft überall, wo Docker Compose verfügbar ist. Ein paar konkrete Wege:
|
Läuft überall, wo Docker Compose verfügbar ist. Ein paar konkrete Wege:
|
||||||
|
|||||||
@@ -62,7 +62,11 @@ function fetchRaw(url: string, timeoutMs = 2500): Promise<RawProbeResult | null>
|
|||||||
* Hinweis-Scanner, kein Sicherheits-/Pentesting-Werkzeug.
|
* Hinweis-Scanner, kein Sicherheits-/Pentesting-Werkzeug.
|
||||||
*/
|
*/
|
||||||
const CANDIDATE_PATHS: { path: string; type: string }[] = [
|
const CANDIDATE_PATHS: { path: string; type: string }[] = [
|
||||||
// Generische REST-/OpenAPI-/GraphQL-Konventionen
|
// Generische REST-/OpenAPI-/GraphQL-Konventionen - bewusst nur Pfade, die
|
||||||
|
// auf eine tatsächlich NUTZBARE/dokumentierte API hindeuten. Reine
|
||||||
|
// Status-/Health-/Metrik-Endpunkte (/health, /metrics, /actuator, ...)
|
||||||
|
// wurden bewusst rausgenommen: die bestätigen nur "hier läuft Software",
|
||||||
|
// sind aber keine API, mit der man tatsächlich etwas anfangen kann.
|
||||||
{ path: "/openapi.json", type: "OpenAPI" },
|
{ path: "/openapi.json", type: "OpenAPI" },
|
||||||
{ path: "/openapi.yaml", type: "OpenAPI" },
|
{ path: "/openapi.yaml", type: "OpenAPI" },
|
||||||
{ path: "/swagger.json", type: "OpenAPI (Swagger)" },
|
{ path: "/swagger.json", type: "OpenAPI (Swagger)" },
|
||||||
@@ -72,7 +76,6 @@ const CANDIDATE_PATHS: { path: string; type: string }[] = [
|
|||||||
{ path: "/swagger/index.html", type: "Swagger-UI" },
|
{ path: "/swagger/index.html", type: "Swagger-UI" },
|
||||||
{ path: "/swagger-ui", type: "Swagger-UI" },
|
{ path: "/swagger-ui", type: "Swagger-UI" },
|
||||||
{ path: "/redoc", type: "OpenAPI (ReDoc)" },
|
{ path: "/redoc", type: "OpenAPI (ReDoc)" },
|
||||||
{ path: "/docs", type: "API-Dokumentation" },
|
|
||||||
{ path: "/graphql", type: "GraphQL" },
|
{ path: "/graphql", type: "GraphQL" },
|
||||||
{ path: "/graphiql", type: "GraphQL" },
|
{ path: "/graphiql", type: "GraphQL" },
|
||||||
{ path: "/api/graphql", type: "GraphQL" },
|
{ path: "/api/graphql", type: "GraphQL" },
|
||||||
@@ -84,17 +87,8 @@ const CANDIDATE_PATHS: { path: string; type: string }[] = [
|
|||||||
{ path: "/rest", type: "REST-API" },
|
{ path: "/rest", type: "REST-API" },
|
||||||
{ path: "/rpc", type: "JSON-RPC" },
|
{ path: "/rpc", type: "JSON-RPC" },
|
||||||
{ path: "/jsonrpc", type: "JSON-RPC" },
|
{ path: "/jsonrpc", type: "JSON-RPC" },
|
||||||
{ path: "/api/status", type: "REST-API" },
|
// Konkrete, in Homelabs verbreitete Software - jeweils ein Endpunkt IHRER
|
||||||
{ path: "/api/system", type: "REST-API" },
|
// eigenen echten API (nicht nur ein Status-Ping), deshalb behalten.
|
||||||
{ path: "/api/info", type: "REST-API" },
|
|
||||||
{ path: "/api/version", type: "REST-API" },
|
|
||||||
{ path: "/api/config", type: "REST-API" },
|
|
||||||
{ path: "/actuator", type: "Spring-Boot-Actuator" },
|
|
||||||
{ path: "/actuator/health", type: "Spring-Boot-Actuator" },
|
|
||||||
{ path: "/metrics", type: "Metriken (Prometheus-Format)" },
|
|
||||||
{ path: "/healthz", type: "Health-Endpunkt" },
|
|
||||||
{ path: "/health", type: "Health-Endpunkt" },
|
|
||||||
// Konkrete, in Homelabs verbreitete Software
|
|
||||||
{ path: "/api/config/core", type: "Home Assistant API" }, // erfordert Auth, meldet sich aber als API
|
{ path: "/api/config/core", type: "Home Assistant API" }, // erfordert Auth, meldet sich aber als API
|
||||||
{ path: "/api2/json/version", type: "Proxmox API" },
|
{ path: "/api2/json/version", type: "Proxmox API" },
|
||||||
{ path: "/api/status", type: "Portainer API" },
|
{ path: "/api/status", type: "Portainer API" },
|
||||||
@@ -102,7 +96,6 @@ const CANDIDATE_PATHS: { path: string; type: string }[] = [
|
|||||||
{ path: "/admin/api.php", type: "Pi-hole API" },
|
{ path: "/admin/api.php", type: "Pi-hole API" },
|
||||||
{ path: "/System/Info/Public", type: "Jellyfin API" },
|
{ path: "/System/Info/Public", type: "Jellyfin API" },
|
||||||
{ path: "/identity", type: "Plex API" },
|
{ path: "/identity", type: "Plex API" },
|
||||||
{ path: "/api/health", type: "Grafana API" },
|
|
||||||
{ path: "/api/self", type: "Unifi-Controller API" },
|
{ path: "/api/self", type: "Unifi-Controller API" },
|
||||||
{ path: "/api/v2/server/about", type: "Nextcloud API" },
|
{ path: "/api/v2/server/about", type: "Nextcloud API" },
|
||||||
{ path: "/ocs/v1.php/cloud/capabilities", type: "Nextcloud API" },
|
{ path: "/ocs/v1.php/cloud/capabilities", type: "Nextcloud API" },
|
||||||
|
|||||||
@@ -129,7 +129,13 @@ function NameChangesReview({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function NewServicesList({ newServices }: { newServices: Service[] }) {
|
function NewServicesList({
|
||||||
|
newServices,
|
||||||
|
onResolve,
|
||||||
|
}: {
|
||||||
|
newServices: Service[];
|
||||||
|
onResolve: (id: string) => void;
|
||||||
|
}) {
|
||||||
if (newServices.length === 0) return null;
|
if (newServices.length === 0) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -139,8 +145,19 @@ function NewServicesList({ newServices }: { newServices: Service[] }) {
|
|||||||
</p>
|
</p>
|
||||||
<ul className="max-h-60 space-y-1 overflow-y-auto">
|
<ul className="max-h-60 space-y-1 overflow-y-auto">
|
||||||
{newServices.map((s) => (
|
{newServices.map((s) => (
|
||||||
<li key={s.id} className="text-black/70 dark:text-white/70">
|
<li key={s.id} className="flex items-center justify-between gap-2">
|
||||||
{s.displayName} ({s.hostname}:{s.port})
|
<span className="text-black/70 dark:text-white/70">
|
||||||
|
{s.displayName} ({s.hostname}:{s.port})
|
||||||
|
</span>
|
||||||
|
<Button
|
||||||
|
size="icon"
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => onResolve(s.id)}
|
||||||
|
title="Bestätigen (aus dieser Liste entfernen)"
|
||||||
|
aria-label="Bestätigen"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faCheck} />
|
||||||
|
</Button>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
@@ -375,7 +392,20 @@ function ApiScannerCard() {
|
|||||||
<ul className="mt-4 max-h-72 space-y-2 overflow-y-auto text-sm">
|
<ul className="mt-4 max-h-72 space-y-2 overflow-y-auto text-sm">
|
||||||
{lastNewFindings.map(({ serviceId, serviceName, apis: entries }) => (
|
{lastNewFindings.map(({ serviceId, serviceName, apis: entries }) => (
|
||||||
<li key={serviceId} className="rounded-lg border border-emerald-500/30 bg-emerald-500/5 p-2">
|
<li key={serviceId} className="rounded-lg border border-emerald-500/30 bg-emerald-500/5 p-2">
|
||||||
<div className="font-medium text-black dark:text-white">{serviceName}</div>
|
<div className="flex items-center justify-between gap-2">
|
||||||
|
<div className="font-medium text-black dark:text-white">{serviceName}</div>
|
||||||
|
<Button
|
||||||
|
size="icon"
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() =>
|
||||||
|
setLastNewFindings((prev) => prev.filter((f) => f.serviceId !== serviceId))
|
||||||
|
}
|
||||||
|
title="Bestätigen (aus dieser Liste entfernen)"
|
||||||
|
aria-label="Bestätigen"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faCheck} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
<ul className="mt-1 flex flex-wrap gap-2">
|
<ul className="mt-1 flex flex-wrap gap-2">
|
||||||
{entries.map((e) => (
|
{entries.map((e) => (
|
||||||
<li key={e.id}>
|
<li key={e.id}>
|
||||||
@@ -626,7 +656,10 @@ export function ScannerPage() {
|
|||||||
{bulkStatus ? (
|
{bulkStatus ? (
|
||||||
<p className="mt-2 text-sm text-black/50 dark:text-white/50">{bulkStatus}</p>
|
<p className="mt-2 text-sm text-black/50 dark:text-white/50">{bulkStatus}</p>
|
||||||
) : null}
|
) : null}
|
||||||
<NewServicesList newServices={bulkNewServices} />
|
<NewServicesList
|
||||||
|
newServices={bulkNewServices}
|
||||||
|
onResolve={(id) => setBulkNewServices((prev) => prev.filter((s) => s.id !== id))}
|
||||||
|
/>
|
||||||
<NameChangesReview
|
<NameChangesReview
|
||||||
nameChanges={bulkNameChanges}
|
nameChanges={bulkNameChanges}
|
||||||
onResolve={(key) => setBulkNameChanges((prev) => prev.filter((c) => nameChangeKey(c) !== key))}
|
onResolve={(key) => setBulkNameChanges((prev) => prev.filter((c) => nameChangeKey(c) !== key))}
|
||||||
|
|||||||
@@ -374,6 +374,14 @@ function SearchEngineHint() {
|
|||||||
Danach: „lp <suchbegriff>" in die Adresszeile – bei eindeutigem Treffer geht's direkt
|
Danach: „lp <suchbegriff>" in die Adresszeile – bei eindeutigem Treffer geht's direkt
|
||||||
zum Dienst, sonst zur LaunchPad-Suche mit vorbefülltem Begriff.
|
zum Dienst, sonst zur LaunchPad-Suche mit vorbefülltem Begriff.
|
||||||
</p>
|
</p>
|
||||||
|
<div className="rounded-lg bg-amber-500/10 p-2 text-xs text-amber-700 dark:text-amber-400">
|
||||||
|
<strong>Android:</strong> Firefox für Android unterstützt Schlagwort-Suchen bislang
|
||||||
|
grundsätzlich nicht (auch keine synchronisierten Desktop-Kürzel) – das ist eine
|
||||||
|
Firefox-Einschränkung, kein LaunchPad-Problem. Auf Android funktioniert es in{" "}
|
||||||
|
<strong>Chrome</strong> genauso wie oben für Chrome/Edge beschrieben. Alternative: die
|
||||||
|
LaunchPad-Seite über „Zum Startbildschirm hinzufügen" als App-Symbol ablegen – dann reicht
|
||||||
|
ein Fingertipp bis zum Suchfeld.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user